I am trying to troubleshoot the response object I get from the web service call.
When I try response.StatusCode in ItemService.cs. Says
does not contain definition for 'Statuscode'. Are you missing directive or assembly reference.
I would appreciate if anyone could advise me on how to catch the exact response code and error message.
ItemService.cs
public async Task<List<Item>> GetItems()
{
var response = await _httpClient.GetFromJsonAsync<List<Item>>("api/item");
if(response.StatusCode)// error
{}
}
You need to use HttpClient.GetAsync method to return the value of Task<HttpResponseMessage>.
In the HttpResponseMessage class, it contains StatusCode property which is what you need.
Updated:
To check whether the response returns a success code, you should use the IsSuccessStatusCode property. Otherwise, you should compare the status code below:
if (response.StatusCode != HttpStatusCode.OK)
Next, extract the content from the HttpResponseMessage with HttpContentJsonExtensions.ReadFromJsonAsync method.
public async Task<List<Item>> GetItems()
{
var response = await _httpClient.GetAsync("api/Item");
if (!response.IsSuccessStatusCode)
{
Console.WriteLine(response.StatusCode);
return new List<Item>();
}
return await response.Content.ReadFromJsonAsync<List<Item>>();
}
Reference
Make HTTP requests using IHttpClientFactory in ASP.NET Core (CreateClient section with demo)
Related
I am making a communication between two microservices. I am trying to obtain the content from the model "UserBookPreference". I already debugged and the content that is being sent in the PostAsync() request is correct (let's say, data from microservice A). The problem arises when I try to receive the contents from the Post method in microservice B. The model type I have as parameter is the same I am sending from the postAsync. Since I am using .NET 5, the JsonContent.Create() method is a good approach according to my reasearch. I have tried other methodologies, such as using the StringContent() method but still I get the null object in microservice B. This is the code I am using.
In microservice A:
public async Task<string> AddFavoriteBook(UserBookPreference bookModel)
{
JsonContent content = JsonContent.Create(bookModel);
var response = await httpClient.PostAsync(URLHelper._baseUserPreferencesMicroserviceURL + "/Book", content);
var responseString = await response.Content.ReadAsStringAsync();
return responseString;
}
In microservice B:
[HttpPost("Favorites/Book")]
public IActionResult AddUserFavoriteBook(UserBookPreference bookModel)
{
try
{
_prefService.AddUserBookFavorite(bookModel);
return Ok(bookModel);
}
catch (Exception ex)
{
return BadRequest(new { message = ex.Message });
}
}
Thank you for your time.
You need to either add [FromBody] attribute before UserBookPreference in your endpoint or add a [ApiController] attribute to your controller to bind UserBookPreference to incoming body. docks
The Stackoverflow API, if the request is not successful, how to read the response please?
using (HttpClient Client = new HttpClient(handler))
{
string response = string.Empty;
response = Client.GetStringAsync(apiUri.Uri).Result;
return JsonConvert.DeserializeObject<Questions<Question>>(response);
}
When an error occurs:
{"error_id":502,"error_message":"too many requests from this IP, more requests available in 82216 seconds","error_name":"throttle_violation"}
An error InnerException = {"Response status code does not indicate success: 400 (Bad Request)."}
The following paragraph is from Call a Web API From a .NET Client (C#)
HttpClient does not throw an exception when the HTTP response contains an error code. Instead, the IsSuccessStatusCode property is false if the status is an error code. If you prefer to treat HTTP error codes as exceptions, call HttpResponseMessage.EnsureSuccessStatusCode on the response object. EnsureSuccessStatusCode throws an exception if the status code falls outside the range 200–299. Note that HttpClient can throw exceptions for other reasons — for example, if the request times out.
In your case the code could be something like the following.
var response = await client.GetAsync(apiUri.Uri);
if (!response.IsSuccessStatusCode)
{
var text = await response.Content.ReadAsStringAsync();
// Log error
return null; // throw etc.
}
else
{
var text = await response.Content.ReadAsStringAsync();
var o = JsonConvert.DeserializeObject<Questions<Question>>(o);
return o;
}
(You can move var text = ... outside of if if you wish.)
Status code can be examined instead of calling IsSuccessStatusCode, if needed.
if (response.StatusCode != HttpStatusCode.OK) // 200
{
throw new Exception(); / return null et.c
}
I am making a POST request to a route which is returning JSON data.
[HttpPost("api/v1/testGetAll")]
public object Test([FromBody]object filteringOptions)
{
return myService.GetLogs(filteringOptions).ToArray();
}
Route works fine, filtering works fine, and when I test the route in Postman I get the right response. However this is only a back-end, and I would like to invoke this route from my custom API gateway.
The issue I'm facing is getting that exact response back. Instead I am getting success status, headers, version, request message etc.
public object TestGetAll(string ApiRoute, T json)
{
Task<HttpResponseMessage> response;
var url = ApiHome + ApiRoute;
using (var client = new HttpClient())
{
client.BaseAddress = new Uri(url);
client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
try
{
response = client.PostAsync(url, new StringContent(json.ToString(), Encoding.UTF8, "application/json"));
return response.Result;
}
catch (Exception e)
{
...
}
}
}
How can I get exact content back?
You need to read the content from response.
var contentString = response.Result.Content.ReadAsStringAsync().Result;
If you wish, you can then deserialize the string response into the object you want returning.
public async Task<TResult> TestGetAll<TResult>(string apiRoute, string json)
{
// For simplicity I've left out the using, but assume it in your code.
var response = await client.PostAsJsonAsync(url, json);
var resultString = await response.Content.ReadAsStringAsync();
var result = JsonConvert.DeserializeObject<TResult>(resultString);
return result;
}
You have to return the response as an HttpResponseMessage.
Try changing your return statement to
[HttpPost("api/v1/testGetAll")]
public IHttpActionResult Test([FromBody]object filteringOptions)
{
return Ok(myService.GetLogs(filteringOptions).ToArray());
}
Please note: This will return the response with status code 200. In case you want to handle the response based on different response code. You can create the HttpResponseMessage like this-
Request.CreateResponse<T>(HttpStatusCode.OK, someObject); //success, code- 200
Request.CreateResponse<T>(HttpStatusCode.NotFound, someObject); //error, code- 404
T is your object type.
And so on...
What's the proper way to handle errors when using Pushstreamcontent?
I use Pushstreamcontent to stream data directly from database to a client.
On the client I use HttpCompletionOption.ResponseHeadersRead when recieving the result.
In the case the data is not available, I want to return a HttpStatusCode 404 (Not Found) for example.
Currently I only detect that there is no data, during the execution of the lambda (CopyBinaryValueToResponseStream).
At that point in time I cannot change the state of the HttpResponeMessage anymore.
So what is a proper way to handle such cases? I wanted to avoid an additional check in the database upfront, but right now that seems to be the only way to get it done?
[Route("{id}")]
public HttpResponseMessage GetImage(int id)
{
HttpResponseMessage resp = new HttpResponseMessage();
// do I need to check here first if the data is available?
// and return 404 if the data is not available
// resp.StatusCode = HttpStatusCode.NotFound
// or can I handle it later from within the lambda?
resp.Content = new PushStreamContent(async (responseStream, content, context) =>
{
// what if an error happens in this function? who do I get that error to the client?
await CopyBinaryValueToResponseStream(responseStream, id);
});
return resp;
}
You cannot fix it within the PushStreamContent action. By the time that action is executing, you have already started sending the response, and thus have already sent a 200. This is a drawback of PushStreamContent.
If you have some way to detect that the resource does not exist before streaming (for example, if some file does not exist), you can detect that first and return a 404, i.e. not using PushStreamContent at all in that case.
[Route("{id}")]
public HttpResponseMessage GetImage(int id)
{
HttpResponseMessage resp = new HttpResponseMessage();
if (File.Exists(#"c:\files\myfile.file"))
{
resp.StatusCode = HttpStatusCode.NotFound;
return resp;
}
// file exists - try to stream it
resp.Content = new PushStreamContent(async (responseStream, content, context) =>
{
// can't do anything here, already sent a 200.
await CopyBinaryValueToResponseStream(responseStream, id);
});
return resp;
}
Is there anything wrong with my code here? I keep getting this error:
System.InvalidOperationException: The request message was already sent. Cannot send the same request message multiple times.
My HttpRequestMessage is inside a Func so I figured I get a brand new request each time I pass in func().
public async Task<HttpResponseMessage> GetAsync(HttpRequestMessage request)
{
return await RequestAsync(() => request);
}
public async Task<HttpResponseMessage> RequestAsync(Func<HttpRequestMessage> func)
{
var response = await ProcessRequestAsync(func);
if (response.StatusCode == HttpStatusCode.Unauthorized)
{
WaitForSomeTime();
response = await ProcessRequestAsync(func);
}
return response;
}
private async Task<HttpResponseMessage> ProcessRequestAsync(Func<HttpRequestMessage> func)
{
var client = new HttpClient();
var response = await client.SendAsync(func()).ConfigureAwait(false);
return response;
}
You are calling the same func parameter twice:
var response = await ProcessRequestAsync(func);
//...
response = await ProcessRequestAsync(func);
In this case func returns the same request every single time. It doesn't generate a new one every time you call it. If you truly need a different request each time then func needs to return a new message each call:
var response = await GetAsync(() => new HttpRequestMessage()); // Create a real request.
public async Task<HttpResponseMessage> GetAsync(Func<HttpRequestMessage> requestGenerator)
{
return await RequestAsync(() => requestGenerator());
}
I had the same issue, but no repetition in my code. Turned out I had added a watch on an asynchronous process. That watch called the process while I stepped through the code, so that when I got to the line I was trying to debug it crashed with this error message.
Removing all watches solved the problem.
Leaving this here for other people who might have the same problem.