WebAPI: using async methods in business logic - c#

The aim is to make controller that uses async method in my custom service.
Controller:
[Route("api/data/summary")]
[HttpGet]
public async Task<IHttpActionResult> Get()
{
var result = await DataService.GetDataObjects();
return Ok(result);
}
Service:
public static async Task<IEnumerable<DataObject>> GetDataObjects()
{
var apiKey = "some-api-key";
var path = "path-to-external-service";
using (var client = new HttpClient())
{
var dataToProcess = // some data object
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", apiKey);
client.BaseAddress = new Uri(path);
HttpResponseMessage response = await client.PostAsJsonAsync("", dataToProcess);
var content = await response.Content.ReadAsStringAsync();
var result = MakeEntities(content); // some logic
return result;
}
}
But I came across with the problem that controller's action returns empty result before service actually finished processing data.
Could you please advice how to implement it correctly?

Your code is OK and controller doesn't seem to return a value before GetDataObjects returns value.
Except for the situations below:
MakeEntities uses some asynchronous operation and you don't await it inside MakeEntities. So MakeEntities return task.
Exception rises while your code is running. Make sure GetDataObjects and MakeEntities code works fine.

The aim is to make controller that uses async method in my custom service.
Controller:
[HttpGet]
[Route("api/data/summary")]
public async Task<IHttpActionResult> Get()
{
var result = await DataService.GetDataObjects().ConfigureAwait(false);
return Ok(result);
}
Service:
public static async Task<ResponseEntity> GetDataObjects()
{
ResponseEntity response = new ResponseEntity();
var apiKey = "some-api-key";
var path = "path-to-external-service";
using (var client = new HttpClient())
{
var dataToProcess = // some data object
client.BaseAddress = new Uri(path);
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", apiKey);
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response = await client.PostAsJsonAsync("", dataToProcess).ConfigureAwait(false);
string responseString = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
var result = JsonConvert.DeserializeObject<ResponseEntity>(responseString);
return response;
}
}

Related

Calling API with HttpClient catch BadRequest

I'd like to call a Web API in .NET 6 with httpclient. The code works fine when I return OK(result), I get the right result. The problem is when I return a BadRequest, I'd like to have access to the properties EN, FR, NL. In the HttpRequestException, I just receive the message "500 internal exception error", not the properties EN, FR, NL.
How can I do to get these values ?
[HttpPost(nameof(Testing), Name = "Testing")]
public async Task<ActionResult<MyResponseDto>> Testing(ParameterDto parameter)
{
var res = new MyResponseDto
{
//Few properties her
};
//return Ok(res);
return BadRequest(new { FR = "My Error FR", NL = "My Error NL", EN = "My Error EN" });
}
I call web api from console application doe testing purpose with this code :
in program.cs
var result = await Api.Testing();
public static class Api
{
public static async Task<string> Testing()
{
string response = await HttpRequests.Post(
"/api/Testing",
new { /* few parameter here */ });
var result = JsonConvert.DeserializeObject<MyResponseDto>(response);
}
}
public static class MyHttpRequests
{
private static readonly HttpClient client = new HttpClient();
private const string url = "myURL";
public static async Task<string> Post(string entryPoint, Object dto)
{
string url = $"{url}{entryPoint}";
string dto = JsonConvert.SerializeObject(dto);
HttpContent httpContent = new StringContent(dto, Encoding.UTF8, "application/json");
try
{
using(HttpResponseMessage response = await client.PostAsync(url, httpContent))
{
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
response.EnsureSuccessStatusCode();
string responseBody = await response.Content.ReadAsStringAsync();
return responseBody;
}
}
catch(HttpRequestException e)
{
Console.WriteLine($"Message :{e.Message} ");
}
return await Task.FromResult(string.Empty);
}
}
This line is throwing the exception:
response.EnsureSuccessStatusCode();
Without it, you would continue to return the response body which will contain a json representation of your error variables.
So instead of throwing an exception like this, you can manually check the response status code and parse the json. EG:
public static async Task<string> Post(string entryPoint, Object dto)
{
string url = $"{url}{entryPoint}";
string dto = JsonConvert.SerializeObject(dto);
HttpContent httpContent = new StringContent(dto, Encoding.UTF8, "application/json");
using(HttpResponseMessage response = await client.PostAsync(url, httpContent))
{
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
string responseBody = await response.Content.ReadAsStringAsync();
if (response.IsSuccessStatusCode)
return responseBody;
Console.WriteLine($"Error: {responseBody}");
}
return await Task.FromResult(string.Empty);
}

httpclient sendasync and return object will trigger two times

I have below code which use httpclient and use sendasync, when I test, it always run twice on API.
Controller :
[HttpGet("GetAllStores")]
public async Task<bookstores> GetAllStores()
{
Console.writeline("Trigger Stores")
return await dbContext.set<bookstores>().toListAsync()
}
httpclient
public async Task<IEnumerable<bookstores>> FetchAllStores()
{
var requestContent = new HttpRequestMessage
{
Method = HttpMethod.Get
};
var response = await httpClient.SendAsync("http://127.0.0.1:5000/GetAllStores", HttpCompletionOption.ResponseHeadersRead);
response.EnsureSuccessStatusCode();
return JsonSerializer.Deserialize<bookstores>(await response.Content.ReadAsStringAsync(), defaultJsonSerializerOptions);
}
Test
public async Task GetSettingProfile_AsExpected()
{
var sut = new ApiClient(
new HttpClient() { BaseAddress=new Uri("http://localhost:40331") });
await sut.FetchAllStores();
}
The output in console is show Trigger Stores two times
Can I know how to make it which call API in one time ?

How to call POST method from API using HttpClient?

I am trying to use the HttpClient class to call a POST method from my API which is supposed to add Server information to a DB.
The way I'm trying to call it is below. But when I step through it to debug it steps through my if statement for response.IsSuccessStatusCode.
public static async Task<Server> PostServer(Server server)
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("http://localhost:50489/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
StringContent content = new StringContent(JsonConvert.SerializeObject(server));
// HTTP POST
HttpResponseMessage response = await client.PostAsync("api/Server/", content);
if (response.IsSuccessStatusCode)
{
string data = await response.Content.ReadAsStringAsync();
server = JsonConvert.DeserializeObject<Server>(data);
}
}
return server;
}
Also here is the POST method in my API below it was automatically generated in VS.
// POST: api/Servers
[ResponseType(typeof(Server))]
public async Task<IHttpActionResult> PostServer(Server server)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
db.Servers.Add(server);
await db.SaveChangesAsync();
return CreatedAtRoute("DefaultApi", new { id = server.Server_ID }, server);
}

Async Tasks fetching json from api's not runnin async

I'm new to async Task in c#. What I want to accomplish is following:
- Api that calls 3 other api's async
- Return 3 datatsets as one
[System.Web.Http.AcceptVerbs("GET", "POST")]
[System.Web.Mvc.HttpGet]
public async Task<string> ServiceModelsForTournamentBase(int id)
{
var matchInfoJson = await GetJsonFromApi("api/asyncdata/searchmatchfortournament/" + id, _siteUrl);
var scoringPlayersJson = await GetJsonFromApi("api/asyncdata/scoringplayersfortournament/" + id, _siteUrl);
var teamsJson = await GetJsonFromApi("api/asyncdata/tournamentteams/" + id, _siteUrl);
// return json containing all three
}
private async Task<string> GetJsonFromApi(string serviceUrl, Uri siteUrl)
{
using (var client = new HttpClient())
{
client.BaseAddress = siteUrl;
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var response = await client.GetAsync(serviceUrl);
return response.IsSuccessStatusCode
? await response.Content.ReadAsStringAsync()
: string.Empty;
}
}
[System.Web.Http.AcceptVerbs("GET", "POST")]
[System.Web.Mvc.HttpGet]
public async Task<string> ServiceModelsForTournamentBase(int id)
{
var jsons = await Task.WhenAll(
GetJsonFromApi("api/asyncdata/searchmatchfortournament/" + id, _siteUrl),
GetJsonFromApi("api/asyncdata/scoringplayersfortournament/" + id, _siteUrl),
GetJsonFromApi("api/asyncdata/tournamentteams/" + id, _siteUrl)
);
var matchInfoJson = jsons[0];
var scoringPlayersJson = jsons[1];
var teamsJson = jsons[2];
// return json containing all three
}

Error with await operator

There is problem with my code. How can I solve this problem? This problem in await operator.
public MyModel()
{
HttpClient client = new HttpClient();
HttpResponseMessage response = await client.GetAsync("https://api.vkontakte.ru/method/video.get?uid=219171498&access_token=d61b93dfded2a37dfcfa63779efdb149653292636cac442e53dae9ba6a049a75637143e318cc79e826149");
string googleSearchText = await response.Content.ReadAsStringAsync();
JObject googleSearch = JObject.Parse(googleSearchText);
IList<JToken> results = googleSearch["response"].Children().Skip(1).ToList();
IList<MainPage1> searchResults = new List<MainPage1>();
foreach (JToken result in results)
{
MainPage1 searchResult = JsonConvert.DeserializeObject<MainPage1>(result.ToString());
searchResults.Add(searchResult);
}
You're trying to use await within a constructor. You can't do that - constructors are always synchronous.
You can only use await within a method or anonymous function with the async modifier; you can't apply that modifier to constructors.
One approach to fixing this would be to create a static async method to create an instance - that would do all the relevant awaiting, and then pass the results to a simple synchronous constructor. Your callers would then need to handle this appropriately, of course.
public static async Task<MyModel> CreateInstance()
{
string googleSearchText;
using (HttpClient client = new HttpClient())
{
using (HttpResponseMessage response = await client.GetAsync(...))
{
googleSearchText = await response.Content.ReadAsStringAsync();
}
}
// Synchronous constructor to do the rest...
return new MyModel(googleSearchText);
}
You can't use await in the constructor of a class.
An async method returns a Task object which can be executed async. A constructor does not have a return type and thus can't return a Task object, and thus can't be awaited.
A simple fix for this problem is create a Init function:
public MyModel()
{
}
public async Task Init()
{
HttpClient client = new HttpClient();
HttpResponseMessage response = await client.GetAsync("https://api.vkontakte.ru/method/video.get?uid=219171498&access_token=d61b93dfded2a37dfcfa63779efdb149653292636cac442e53dae9ba6a049a75637143e318cc79e826149");
string googleSearchText = await response.Content.ReadAsStringAsync();
JObject googleSearch = JObject.Parse(googleSearchText);
IList<JToken> results = googleSearch["response"].Children().Skip(1).ToList();
IList<MainPage1> searchResults = new List<MainPage1>();
foreach (JToken result in results)
{
MainPage1 searchResult = JsonConvert.DeserializeObject<MainPage1>(result.ToString());
searchResults.Add(searchResult);
}
}
Then when you create your model:
var model = new MyModel();
await model.Init();

Categories