Are the following two methods, getData1Async() and getData2Async() are essentially the same? If so why don't I need EnsureSuccessStatusCode() in getData2Async() method?
class Program
{
static void Main(string[] args)
{
try
{
string uri = "https://www.blahblah.com/getdata";
Task<string> x = getData1Async(uri);
System.Diagnostics.Debug.WriteLine(x.Result);
Task<string> y = getData2Async(uri);
System.Diagnostics.Debug.WriteLine(y.Result);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
static async Task<string> getData1Async(string uri)
{
HttpClient client = new HttpClient();
HttpResponseMessage response = await client.GetAsync(uri);
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync();
}
static async Task<string> getData2Async(string uri)
{
var httpClient = new HttpClient();
return await httpClient.GetStringAsync(uri);
}
}
getData1Async - here you are getting the object of type HttpResponseMessage and if you don't ensure that response has completed successfully and call response.Content.Read..., the answer will be indeterministic.
getData2Async - directly calls httpClient itself to get the string which internally makes sure that it only returns when data has been received.
Related
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);
}
This question already has answers here:
async/await - when to return a Task vs void?
(6 answers)
Closed 11 months ago.
I am trying to use this in a method call to get a string from a source
HttpClient client = new HttpClient();
HttpResponseMessage response = await client.GetAsync(url);
string responseBody = await response.Content.ReadAsStringAsync();
Console.Write(responseBody);
When used outside of a method and class, it works fine with my url, and returns the proper string I am looking for. However, when I put it in my class and method, it no longer does anything. No errors, just returns nothing.
For reference, the method I am using looks like this
public async void get(String test) {
HttpClient client = new HttpClient();
HttpResponseMessage response = await client.GetAsync(test);
string responseBody = await response.Content.ReadAsStringAsync();
Console.Write(responseBody);
}
I am clueless why this stops working once I put it into a method.
Make your Method returning a Task and it works
public async Task get(String test)
{
HttpClient client = new HttpClient();
HttpResponseMessage response = await client.GetAsync(test);
string responseBody = await response.Content.ReadAsStringAsync();
Debug.Write(responseBody);
}
private async void btnTest_Click(object sender, EventArgs e)
{
try
{
Task t = get("https://stackoverflow.com/questions/71787949/c-sharp-httpclient-get-does-not-work-when-put-in-async-method");
IEnumerable<Task> tasks = new[] { t };
await Task.WhenAll(tasks);
}
catch (Exception oException)
{
Debug.WriteLine(oException.ToString());
}
}
I'm working on Xamarin.Forms App connected with Web Api 2 Api and all requests and responses work with HttClient. This is my code:
class for all my requests and definiot of HttpClient
public class DataStore : IDataStore<object>
{
HttpClient client;
public DataStore()
{
client = new HttpClient()
{
BaseAddress = new Uri($"{App.Uri}")
};
}
Example of one of my requests :
public async Task<User> GetProfileSetup()
{
try
{
if (CrossConnectivity.Current.IsConnected)
{
string token = DependencyService.Get<ISharedFunctions>().GetAccessToken();
client.DefaultRequestHeaders.Clear();
client.DefaultRequestHeaders.Add("Accept", "application/json");
client.DefaultRequestHeaders.Add("Authorization", "Bearer " + token);
var response = await client.GetAsync(#"api/User/GetProfilSetup");
if (response.IsSuccessStatusCode)
{
string jsonMessage;
using (Stream responseStream = await response.Content.ReadAsStreamAsync())
{
jsonMessage = new StreamReader(responseStream).ReadToEnd();
}
User user = JsonConvert.DeserializeObject<User>(jsonMessage);
return user;
}
else
{
var m = response.Content.ToString();
return null;
}
}
else
{
return null;
}
}
catch (Exception ex)
{
Debug.WriteLine(ex);
string error = ex.Message;
return null;
}
}
My idea is to check every response(Response Status Code) in one place. I need this for throw Alert Errors , for refresh token etc. Is there a possible way to this ? I want to have control on every request/response.
if anyone have problem with this , just need to implement custom handler , who will inherit form DelegatingHandler. My code example:
public class StatusCodeHandler : DelegatingHandler
{
public StatusCodeHandler(HttpMessageHandler innerHandler) : base(innerHandler) { }
public GetStatusCode GetStatusCode = new GetStatusCode();
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
HttpResponseMessage response = null;
response = await base.SendAsync(request, cancellationToken);
if (response.IsSuccessStatusCode)
{
return response;
}
else
{
var status_code = (int)response.StatusCode;
GetStatusCode.GetResponseCode(status_code);
}
return response;
}
}
This is not related to xamarin, its a question of abstraction in OOP. You can and should abstract HttpClient and its methods to remove all the boilerplate.
Example - GetAsync<T>(url) will check for connectivity, forms request adds necessary headers, waits for response, checks response status, reads response and finally returns the deserialised response. That way, if you want to add caching layer it's easier. Basic OOP.
Abstracting your code:
public async Task<T> GetAsync(string url)
{
try
{
if (!CrossConnectivity.Current.IsConnected)
{
// throw custom exception?
new NoNetworkException();
}
var token = DependencyService.Get<ISharedFunctions>().GetAccessToken();
client.DefaultRequestHeaders.Clear();
client.DefaultRequestHeaders.Add("Accept", "application/json");
client.DefaultRequestHeaders.Add("Authorization", "Bearer " + token);
var response = await client.GetAsync(url);
if (!response.IsSuccessStatusCode)
{
// read response and throw for logging?
new InvaidResponseException();// custom exceptions makes it easier for catching
}
using (Stream responseStream = await response.Content.ReadAsStreamAsync())
{
// there should be an async overload to read too
var jsonMessage = new StreamReader(responseStream).ReadToEnd();
return JsonConvert.DeserializeObject<T>(jsonMessage);
}
}
catch(NoNetworkException ex)
{
// handle
}
catch(InvaidResponseException ex)
{
// handle
}
}
I have an interface that is defined as follows:
internal interface IHttpService
{
Task SendGetRequest(string param);
}
And the following concrete class (obviously there is compilation errors):
public class HttpService : IHttpService
{
private readonly HttpClient client;
private const string httpLink = "https://somesite.org/search?q=";
private const string httpSuffix = "&format=json&ads=1";
public HttpService()
{
client = new HttpClient();
client.DefaultRequestHeaders.Add("user-agent", "myapp");
}
public async Task SendGetRequest(string param)
{
var response = await client.GetAsync(httpLink + param + httpSuffix);
return response.Content.ReadAsStringAsync();
}
}
So I obviously get a compilation error when returning the ReadAsStringAsync function, but I want my viewmodel to get the response from this function. My viewmodel is as follows:
public SearchViewModel()
{
httpService = (App.Current as App).Container.GetService<IHttpService>();
SearchCommand = new RelayCommand(() =>
{
// Will need to do some proper validation here at some point
var response = await httpService.SendGetRequest(httpStringToSend);
});
}
I'm sure i'm missing something but i'm not entirely sure what...
ReadAsStringAsync is asynchronous and needs to be awaited.
You also need to use the generic Task<T> as your return type rather than Task, because your asynchronous operation is returning a value i.e. string.
public async Task<string> SendGetRequest(string param)
{
var response = await client.GetAsync(httpLink + param + httpSuffix);
return await response.Content.ReadAsStringAsync();
}
HttpResponseMessage is also IDisposable so you should add a using block:
public async Task<string> SendGetRequest(string param)
{
using (var response = await client.GetAsync(httpLink + param + httpSuffix))
{
return await response.Content.ReadAsStringAsync();
}
}
I am trying to call an async method from a synchronous method and it keeps bombing on the call to GetUsTraceApiHealth() but with no errors. What is the problem?
Calling Method:
public ActionResult TestSSN()
{
try
{
var apiResponse = GetUsTraceApiHealth().GetAwaiter().GetResult();
string responseBody = apiResponse.Content.ReadAsStringAsync().Result;
return Json(responseBody, JsonRequestBehavior.AllowGet);
}
catch (Exception e)
{
throw new Exception(e.Message);
}
}
Method Being Called:
public async Task<HttpResponseMessage> GetUsTraceApiHealth()
{
using (HttpClient httpClient = new HttpClient())
{
try
{
string uri = $"https://trace.{ConfigHelper.SterlingDomain}health?deep";
HttpResponseMessage apiResponse = await httpClient.GetAsync(uri);
return apiResponse;
}
catch (Exception e)
{
throw new Exception(e.Message);
}
}
}
Follow the async mantra of "async all the way down". Basically, you should almost never call .Result on a task. In the majority of cases, your calling method should also be async. Then you can simply await the result of the operation:
public async Task<ActionResult> TestSSN()
{
//...
var apiResponse = await GetUsTraceApiHealth();
string responseBody = await apiResponse.Content.ReadAsStringAsync();
//...
}
It should be up to the application host at the top level (in this case ASP.NET and the web server) to handle the synchronization context. You shouldn't try to mask an asynchronous operation as a synchronous one.
Simplified version of your code:
public async Task<ActionResult> TestSSN()
{
var apiResponse = await GetUsTraceApiHealthAsync();
return Json(apiResponse, JsonRequestBehavior.AllowGet);
}
public async Task<string> GetUsTraceApiHealthAsync()
{
using (HttpClient httpClient = new HttpClient())
{
string uri = $"https://trace.{ConfigHelper.SterlingDomain}health?deep";
return apiResponse = await httpClient.GetStringAsync(uri);
}
}
There's no reason to return the HttpResponseMessage to read its content as string, just use GetStringAsync.
Also, never catch an exception just to rethrow it. If you need to do that, use:
catch(Exception ex)
{
//log or whatever
throw;
}
You shouldn't mix the async and sync operations together. Proper way to perform it is decorating your methods as async and simply using await;
public async Task<ActionResult> TestSSN()
{
try
{
var apiResponse = await GetUsTraceApiHealth().GetAwaiter().GetResult();
string responseBody = await apiResponse.Content.ReadAsStringAsync().Result;
return Json(responseBody, JsonRequestBehavior.AllowGet);
}
catch (Exception e)
{
throw new Exception(e.Message);
}
}
If you don't able to apply async in the all paths, you could use ConfigureAwait to prevent deadlock.
public async Task<HttpResponseMessage> GetUsTraceApiHealth()
{
using (HttpClient httpClient = new HttpClient())
{
try
{
string uri = $"https://trace.{ConfigHelper.SterlingDomain}health?deep";
HttpResponseMessage apiResponse = await httpClient.GetAsync(uri).ConfigureAwait(false);
return apiResponse;
}
catch (Exception e)
{
throw new Exception(e.Message);
}
}
}