How to store object data from an asynchronous task? - c#

I have a controller method that gets a data from an API.
The LeaveBalanceAsync is called by an AJAX when my page loads.
public async Task<IActionResult> LeaveBalanceAsync(int empnum)
{
return await APICallAsync(empnum);
}
private async Task<IActionResult> APICallAsync(int emp)
{
//Fetch the JSON string from URL.
LeaveModel leave = new LeaveModel();
string apiUrl = $"http://myapi/{emp}";
HttpClient client = new HttpClient();
HttpResponseMessage response = await client.GetAsync(apiUrl);
if (response.IsSuccessStatusCode)
{
APIResponse<LeaveModel> apiResponse = JsonConvert.DeserializeObject<APIResponse<LeaveModel>>(await response.Content.ReadAsStringAsync());
leave = apiResponse.Data;
userLeave = leave;
}
//Return the Deserialized JSON object.
return Json(leave);
}
I would like to store the data that I got from that method to an object like this (as you can see above I tried to pass it into the userLeave model/object but after the LeaveBalanceAsync is done executing,the data that was captured and stored into userLeave is also gone)
public LeaveModel userLeave = new LeaveModel();
Or is there any possible ways I can do this using another method to store it into that object?

Related

Send binarydata to Azure webAPI POST endpoint

Currently my webAPI has the following POST endpoint:
public async Task<ActionResult<string>> AddUserImage([FromRoute] string userId, [FromHeader] bool doNotOverwrite, [FromBody] byte[] content, CancellationToken ct)
My goal is to send an image file to the endpoint. However, I cannot find a correct way to send an octect-stream or ByteArrayContent or some other type over the internet. All attempts end in an HTTP 415.
This is my best attempt to send the image over the internet:
public async Task<bool> AddOrReplaceImage(string id, string endpoint, byte[] imgBinary)
{
if (imgBinary is null) throw new ArgumentNullException(nameof(imgBinary));
var request = new HttpRequestMessage(HttpMethod.Post, endpoint);
request.Headers.Add("doNotOverwrite", "false");
request.Content = JsonContent.Create(imgBinary);
// I also tried: request.Content = new ByteArrayContent(imgBinary);
request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream"); // Does not seem to change a thing
var apiResult = await new HttpClient().SendAsync(request); // Returns 415
return apiResult.IsSuccessStatusCode;
}
I doubt both the parameters of the endpoint and the way I send the HTTP request. How can I simply receive and send an image over the internet?
Frist Solution :- Which worked in my case.
You can try [FromForm] and IFormFile Like this :-
If controller is annotated with [ApiController] then[FromXxx] is required. For normal view controllers it can be left.
public class PhotoDetails
{
public string id {get;set;}
public string endpoint {get;set;}
public IFormFile photo {get;set;}
}
public async Task<ActionResult<string>> AddUserImage([FromForm] PhotoDetails photoDetails, CancellationToken ct)
I tried this in .net core and it worked but i needed array of files so i used [FromForm] and IFormFile[] and sending from angular.
Second Solution :-
I tried replicate question scenario with question code.
and then changed the implementation and it worked. Please find the below
code
PhotoDetails photopara = new PhotoDetails();
photopara.id = id;
photopara.endpoint = endpoint;
photopara.photo = imgdata;
string json = JsonConvert.SerializeObject(photopara);
var stringContent = new StringContent(json, Encoding.UTF8, "application/json");
using (var client = new HttpClient())
{
var response = await client.PostAsync("http://localhost:57460/WeatherForecast", stringContent);
if (!response.IsSuccessStatusCode)
{
return null;
}
return (await response.Content.ReadAsStreamAsync()).ToString();
}
public class PhotoDetails
{
public string id {get;set;}
public string endpoint {get;set;}
public byte[] photo {get;set;}
}
In this solution, I changed IformFile to byte[] in photodetail class because httpresponsemessage creating problem.
Get Image or byte array in Post Method
Please try this without json serialization
using (var client = new HttpClient())
using (var formData = new MultipartFormDataContent())
{
formData.Add(idContent, "id", "param1");
formData.Add(endpointContent, "endpoint", "file1");
formData.Add(bytesContent, "photo", "file2");
var response = await client.PostAsync("http://localhost:57460/WeatherForecast", formData);
if (!response.IsSuccessStatusCode)
{
return null;
}
return (await response.Content.ReadAsStreamAsync()).ToString();
}
public async Task<ActionResult<int>> AddUserImage([FromForm] PhotoDetails photo, CancellationToken ct)
{
// logic
}
Still Not working then You can try the below link also
Send Byte Array using httpclient

Serialize async response from a database client

I have an endpoint which is used to create an item. The controller calls the service which creates the item, makes some changes on the db and db returns data based on the procedure. The db returns a json like response, but is not always the same, so I have to adjust on the backend so that I can formalize the response type.
The problem is that create item service is asynchronous and I need to be able to await the response so I can make a new response based on that. How can I await the response and that I get from db client and then return data based on that.
This is my Action and I want to be able to serialize async response from service
[HttpPost]
public IActionResult CreateItem([FromBody] InputModel item)
{
var jsonString = _itemService.CreateItem(item);
ResponseModel? response = JsonSerializer.Deserialize<ResponseModel>(jsonString);
return new ObjectResult(response.Response) { StatusCode = response.StatusCode };
}
The default response model
public class ResponseModel
{
public string Response { get; set; }
public int StatusCode { get; set; }
}
Create Item service, which makes the post request to the client and it has to be async.
Depending on the status code that is coming from the client, I want to be able to set my action status code as well.
public async Task<string> CreateItem(InputModel item)
{
if (item.VersionType != 1)
{
return new { Response = "Incorrect data", StatusCode = 400 }.ToString()!;
}
var json = JsonSerializer.Serialize(item);
var data = new StringContent(json, Encoding.UTF8, "application/json");
var response = await client.PostAsync(url, data);
var content = await response.Content.ReadAsStringAsync();
return content;
}
You can make your action method async and then await the method call _itemService.CreateItem for it :
[HttpPost]
public async Task<IActionResult> CreateItem([FromBody] InputModel item)
{
var jsonString = await _itemService.CreateItem(item);
ResponseModel? response = JsonSerializer.Deserialize<ResponseModel>(jsonString);
return new ObjectResult(response.Response) { StatusCode = response.StatusCode };
}
Now your action method would asyncrounously wait for the result from CreateItem and when it returs result, it will continue executing further and send the deserialized response back to client.

C# get a http response from another class method

Previously i handled all my http requests in a single class but i would like to move the http login functionality to a different class but now i cant access the http client response.IsSuccessStatusCode
this is my original code whic works
var http = new HttpClient();
var url = String.Format(shared.AppDetails.domainurl+"/v2auth/default/login");
var response2 = await http.PostAsync(url, credentials);
if (response.IsSuccessStatusCode)
{
//do after login stuff
}
Now i would like to move the login logic to a different class that is in a different folder(auth->dbhelpers)
class LoginHttp
{
public static async Task<object> loginAsync(String username, String password)
{
var values = new Dictionary<string, string>
{
{ "username",username },
{ "password", password }
};
var credentials = new FormUrlEncodedContent(values);
var http = new HttpClient();
var url = String.Format(shared.AppDetails.domainurl + "/v2auth/default/login");
var response = await http.PostAsync(url, credentials);
return response;
}
}
So am now trying to access the returned response via
var responsefromhttplogin = auth.dbhelpers.AuthHttp.loginAsync(login_username.Text, login_password.Password);
if (responsefromhttplogin .IsSuccessStatusCode) //this fails
{
//do after login stuff
}
How can i get the retrned response be of type HttpClient again?
Am getting an error of
Task<Objct> does not contain defination for IsSuccessStatusCode
Make your loginAsync method return Task instead. Currently you are returning an object, then you will have access to the IsSuccessStatusCode
class LoginHttp{
public static async Task<HttpResponseMessage> loginAsync(String username, String password)
{
var values = new Dictionary<string, string>
{
{ "username",username },
{ "password", password }
};
var credentials = new FormUrlEncodedContent(values);
var http = new HttpClient();
var url = String.Format(shared.AppDetails.domainurl + "/v2auth/default/login");
var response = await http.PostAsync(url, credentials);
return response;
}
}
You also need to use await in your calling method, otherwise you get a Task back
var responsefromhttplogin = await auth.dbhelpers.AuthHttp.loginAsync(login_username.Text, login_password.Password);
if (responsefromhttplogin.IsSuccessStatusCode)
{
//do after login stuff
}
Try to change your code to await async method like this:
var responsefromhttplogin = await auth.dbhelpers.AuthHttp.loginAsync(login_username.Text, login_password.Password);
if (responsefromhttplogin.IsSuccessStatusCode)
{
//do after login stuff
}
Now after you add await this responsefromhttplogin.IsSuccessStatusCode should be accessible.
And also change your method to return Task<HttpResponseMessage> instead of Task<object>
You are returning Task<object> from loginAsync(), to access the result of the task you can use Task.Result.
I would consider to narrow down the type that you are returning from object to HttpResponseMessage.
Judging from your code, you are returning a Task<object>
You should either cast the return response; as return (HttpResponseMessage) response;
Or change the return type to be Task<HttpResponseMessage>
Relevant documentation

How to pass model object to Web API Post method

I want to save data to database in MVC application. I have below class which calls the webapi1 service post method. Here Sample is a model object. I have a view which uses the Sample object. I want to inert the Sample Object data to db using the service call.
public SampleTestCreate(Sample)
{
string uri = baseUri + "Test";
using (HttpClient httpClient = new HttpClient())
{
Task<HttpResponseMessage> response = httpClient.PostAsJsonAsync(uri, new StringContent(JsonConvert.SerializeObject(Test)));
// Task<HttpResponseMessage> response = httpClient.PostAsJsonAsync(uri, Test);
var data = response.Result.Content.ReadAsStringAsync().Result;
return JsonConvert.DeserializeObjectAsync<Test>(response.Result.Content.ReadAsStringAsync().Result).Result;
}
}
// POST api/Sample
public HttpResponseMessage PostSample(Sample Sample)
{
if (ModelState.IsValid)
{
db.Samples.Add(Sample);
db.SaveChanges();
HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.Created, Sample);
response.Headers.Location = new Uri(Url.Link("DefaultApi", new { id = Sample.SampleID }));
return response;
}
else
{
return Request.CreateResponse(HttpStatusCode.BadRequest);
}
}
Here my service post method is getting called , but the "Sample" objects all the properties values are coming null. The model object binding is not happening.

HttpResponseMessage from HttpClient to User Defined Object C#

I have seen similar questions asked but none that seem to help me with my issue so please bear with me.
I have a WebAPI controller method that is defined as such:
[HttpPost]
[Route("")]
public HttpResponseMessage CreateMyObject(MyObjectRequest myObject)
{
MyObject o;
try
{
o = _serviceFactory.GetInstance().CreateMyObject(myObject);
}
catch (Exception ex)
{
ex.WriteToLog();
throw ApiHelper.CreateResponseException(HttpStatusCode.InternalServerError, ex);
}
var response = Request.CreateResponse(HttpStatusCode.Created, o);
var uri = Url.Link("GetMyObjectById", new { myObjectId = o.MyObjectId.ToString() });
response.Headers.Location = new Uri(uri);
return response;
}
Say, MyObject contains two properties,
public MyObject
{
public Guid MyObjectId;
public string MyObjectName
}
A client was written to call these controller methods in a WPF application. Here is the client method that is being used:
public HttpResponseMessage CreateQuote(MyObjectRequest myObject)
{
var hashtable = new Hashtable
{
{"myObject", myObject}
};
var task = GetResponse("", hashtable);
var response = task.Result;
return response;
}
protected async Task<HttpResponseMessage> GetResponse(string path, Hashtable parameters)
{
var response = await GetAsync(BuildRequestUri(path, parameters)).ConfigureAwait(false);
return response.IsSuccessStatusCode ? response : new HttpResponseMessage();
}
protected async Task<HttpResponseMessage> GetResponse(string path)
{
return await GetResponse(path, null);
}
The controller and supporting client code was not written by me and was already in the system. I am just consuming this in the WPF application. Now, I am trying to call the controller method via the client in the application and get the MyObject from the response so that I can access the MyObjectId that has been created and set. I have tried some of the other responses to similar questions but have not even seen some of the methods that are called on the response in order to get the information. Here is the first part of the call to the client that I have in the application:
var httpResponse = ApplicationService.CreateMyObject(myObjectRequest);
The application service simply injects the client into the constructor and allows me to call the CreateMyObject method. Is there any insight that can be given to me on how I should be getting the MyObject object out of the response?
I'm still a little new to web api as well, but I'm currently working with it on a project. Give the following code a try:
MyObject myObject;
if (response.IsSuccessStatusCode)
{
// Parse the response body. Blocking!
myObject = response.Content.ReadAsAsync<MyObject>().Result;
}
So you could theoretically change your method like this (may not be exactly what you want):
public MyObject CreateQuote(MyObjectRequest myObject)
{
var hashtable = new Hashtable
{
{"myObject", myObject}
};
var task = GetResponse("", hashtable);
var response = task.Result;
MyObject newObject;
if (response.IsSuccessStatusCode)
{
// Parse the response body. Blocking!
newObject= response.Content.ReadAsAsync<MyObject>().Result;
}
return newObject; // instead of response
}

Categories

Resources