Using HttpResponseMessage to view errors - c#

I can use the following code which works fine to log in using my Web API. However, whensomething goes wrong and an error is returned, I don't know how to get at the contect of the HttpResponseMessage. If I just use the ReadAsStringAsync() method, I get the error in the string, but what type is it? If I know the type I can get the object.
HttpResponseMessage response = await client.PostAsJsonAsync("api/Login", loginObject);
if (response.IsSuccessStatusCode)
{
var _logonResponse = await response.Content.ReadAsAsync<TokenResponseModel>();
}
else
{
// an error has occured, but what is the type to read?
var test = await response.Content.ReadAsStringAsync();
}
On the server it is returning;
BadRequest(ModelState).
Thanks for any help.
EDIT: I have since resolved the issue like this;
var value = await response.Content.ReadAsStringAsync();
var obj = new { message = "", ModelState = new Dictionary<string, string[]>() };
var x = JsonConvert.DeserializeAnonymousType(value, obj);

The result returned back is an JSON object with a "Message" and a "ModelState" properties.
The "ModelState" state value is an object, whose properties are arrays of strings. The property list of "ModelState" varies from time to time depending on which property is invalid.
Hence, to get a strong-type returned response, why don't you manipulate the ModelState yourself on the server side, and then pass the object to the BadRequest() method

Here is just grabbing raw json in text of error message...
if (!response.IsSuccessStatusCode)
{
dynamic responseForInvalidStatusCode = response.Content.ReadAsAsync<dynamic>();
Newtonsoft.Json.Linq.JContainer msg = responseForInvalidStatusCode.Result;
result = msg.ToString();
}

Try IOStreamReader. This is vb.net, but that's not too hard to convert:
IOStreamReader = New IO.StreamReader(Response.GetResponseStream)
RespStr = IOStreamReader.ReadToEnd
Or
Dim HttpReq As Net.HttpWebRequest = Nothing
Dim HttpStatus As Net.HttpStatusCode = Nothing
HttpResp = CType(HttpReq.GetResponse(), Net.HttpWebResponse)
'verify the response
HttpStatus = HttpResp.StatusCode

try following :
try
{
HttpResponseMessage response = await client.PostAsJsonAsync("api/Login", loginObject);
response.EnsureSuccessStatusCode();
var _logonResponse = await response.Content.ReadAsAsync<TokenResponseModel>();
return _logonResponse;
}
catch (Exception ex)
{
throw ex;
}

Related

blazor httpClient.PostJsonAsync status code exception

When using the HTTP in Blazor client side it only works if the response is a success response, but if it is not found or bad response it gives exception and doesn't complete the code.
I want to parse the object I send in the response even if the request is not successful I mean 400 or 404, I send an object with error list so I need to get it.
It gives me error in the console that the request is not successful.
If I make the request to be (OK) then it works, but I need to send 400 status with the object "RequestResult" how I could manage this?
var result = await _httpClient.PostJsonAsync<RequestResult>("api/account/auth", authModel);
if (result.Successful)
{
await _localStorage.SetItemAsync("Token", authModel.SecurityToken);
AuthData.AuthToken= result.Token;
((ApiAuthenticationStateProvider)_authenticationStateProvider).MarkUserAsAuthenticated(result.Token);
_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("bearer", result.Token);
return result;
}
return result;
and this is the controller code when i change BadRequest to Ok it work
public IActionResult Post([FromBody]AuthModel model)
{
var res = _authManager.SignInUser(model);
if (!res.Successful)
{
return BadRequest(new RequestResult { Successful = false, Errors = new List<string>() { res?.errors } });
}
return Ok(new RequestResult { Successful = true ,Token=res.access_token});
}
PostJsonAsync works this way. It will throw an exception if the content cannot be parsed. Here is a suggested workaround:
Required using statement: using System.Text.Json;
var response = await _httpClient.PostAsync("api/account/auth", authModel);
if (response.IsSuccessStatusCode)
{
//parse result as following
using (var sr = await response.Content.ReadAsStreamAsync())
{
var parsedResult = JsonSerializer.DeserializeAsync<RecipeDetailDto>(sr);
}
}
else
{
//If the bad request content/body is a json object
//parse error content
using (var sr = await response.Content.ReadAsStreamAsync())
{
//If the bad request content is a json
//var parsedErrorResult = JsonSerializer.DeserializeAsync<yourErroObjset>(sr);
}
//OR if the content is string
var errorResult = await response.Content.ReadAsString();
}
Did not testet it, but it should give you the context how to approach this.

F# cannot post to web api?

I have the following code to post to a web api.
[<CLIMutable>]
type MyModel = { FileName:string; Period:DateTime; DownloadedTime:DateTimeOffset; Url:string; }
let PostDownload (filepath, date, url) =
async {
try
use client = new HttpClient()
let content = { FileName = filepath; Period = date; DownloadedTime = DateTimeOffset.Now; Url = url }
let! response = Async.AwaitTask(client.PostAsJsonAsync("http://localhost:5000/api/DownloadedFiles", content))
with
| ex -> logger.Error(ex, "Exception: " + ex.Message)
} |> Async.Start
The service has the following code and the debugger shows all the fields of downloadedFile are the default values (nulls or the minimum values for value types).
[HttpPost]
public void Post([FromBody]DownloadedFile downloadedFile)
{
try
{
_context.DownloadedFile.Add(downloadedFile);
_context.SaveChanges();
}
catch (Exception ex) { ...... }
}
The fiddler shows the F# code (or PostAsJsonAsync cannot handle F# mutable record type?) added # at the end of field name?
{"FileName#":"test","Period#":"2100-01-01T00:00:00","DownloadedTime#":"2016-08-18T15:50:37.5004391-04:00","Url#":"test"}
I don't know from where you get HttpClient.PostAsJsonAsync, because it's not on the version of HttpClient I'm currently looking at. Nevertheless, I typically use this extension, which works for me:
type HttpClient with
member this.PostAsJsonAsync (requestUri : string, value : obj) =
let json = string value
let content = new StringContent (json)
content.Headers.ContentType <-
Headers.MediaTypeHeaderValue "application/json"
this.PostAsync (requestUri, content)
I just ran into this issue and lost some hours solving it.
It seems the serializer that is used in HttpClient.PostAsJsonAsync does not handle fsharp types well. The # symbol issue is also not easily googleable.
Using the following code however seems to work:
task {
let content = JObject.FromObject({ Id = "foo" }).ToString()
let! response = client.PostAsync("url", new StringContent(content, Encoding.UTF8, "application/json"))
return "Hello world"
}

ASP.NET MVC handle request error

In my .Net MVC app I need to handle server side validation. If there was something wrong with request I get this:
{
"validationMessage": message
}
with StatusCode = 200.
Otherwise of course I get response proper for the call. My issue is that I have troubles checking for validation messages and then deserializing response (I always get null there though fiddler shows me that response comes back).
public static async Task<Response<T>> Response<T>(HttpResponseMessage response)
{
var res = new Response<T>();
if (response.IsSuccessStatusCode)
{
//check for validation messages
var serverErrorInfo = await response.Content.ReadAsAsync<ServerError>();
if (serverErrorInfo.ValidationMessage != null)
{
res.ErrorInfo = new ErrorInfo(serverErrorInfo.ValidationMessage);
}
else
{
var result = await response.Content.ReadAsAsync<T>();
res.IsSuccess = true;
res.Result = result;
}
return res;
}
What am I doing wrong? Does the response get disposed after first attempt to read it as a ServerError? Since I use generics I cannot first check if there is response and than read the validationMessage.
ServerError Code:
[JsonObject]
public class ServerError
{
public string validationMessage{ get; set; }
}
Probably a deserialization issue. Have you tried ValidationMessage with a capital V in the json response? Also, is serverErrorInfo null entirely, or is just the property ValidationMessage null? Can you check what the value of response.Content is, right before deserialization to ServerError?
In the end the solution was just using some ugly code:
var serverErrorInfo = JsonConvert.DeserializeObject<ServerError>(await response.Content.ReadAsStringAsync());
I am very unsure why ReadAsAsync fails there.

Getting response body on failed request with HttpRequestException

I am trying to log failed requests from my HttpRequestException.
My server returns error code and additional JSON payload at the response body. I need to access that JSON. How do I read the response body in case of an errored request? I know that the actual response is not null. It's an API and I confirm that it returns JSON payload with 4xx status codes, giving detailed insight about the error.
How do I access it? Here is my code:
using (var httpClient = new HttpClient())
{
try
{
string resultString = await httpClient.GetStringAsync(endpoint);
var result = JsonConvert.DeserializeObject<...>(resultString);
return result;
}
catch (HttpRequestException ex)
{
throw ex;
}
}
I am trying to get data in throw ex line, but I couldn't find a way.
Exactly as #Frédéric suggested, if you use GetAsync method you'll get the proper HttpResponseMessage object which give more information about the response. To get details when error occurs you can desearlize the errors to an Exception or your custom exception object from Response content like below:
public static Exception CreateExceptionFromResponseErrors(HttpResponseMessage response)
{
var httpErrorObject = response.Content.ReadAsStringAsync().Result;
// Create an anonymous object to use as the template for deserialization:
var anonymousErrorObject =
new { message = "", ModelState = new Dictionary<string, string[]>() };
// Deserialize:
var deserializedErrorObject =
JsonConvert.DeserializeAnonymousType(httpErrorObject, anonymousErrorObject);
// Now wrap into an exception which best fullfills the needs of your application:
var ex = new Exception();
// Sometimes, there may be Model Errors:
if (deserializedErrorObject.ModelState != null)
{
var errors =
deserializedErrorObject.ModelState
.Select(kvp => string.Join(". ", kvp.Value));
for (int i = 0; i < errors.Count(); i++)
{
// Wrap the errors up into the base Exception.Data Dictionary:
ex.Data.Add(i, errors.ElementAt(i));
}
}
// Othertimes, there may not be Model Errors:
else
{
var error =
JsonConvert.DeserializeObject<Dictionary<string, string>>(httpErrorObject);
foreach (var kvp in error)
{
// Wrap the errors up into the base Exception.Data Dictionary:
ex.Data.Add(kvp.Key, kvp.Value);
}
}
return ex;
}
Usage:
using (var client = new HttpClient())
{
var response =
await client.GetAsync("http://localhost:51137/api/Account/Register");
if (!response.IsSuccessStatusCode)
{
// Unwrap the response and throw as an Api Exception:
var ex = CreateExceptionFromResponseErrors(response);
throw ex;
}
}
Here's the source article detailing more about handling the HttpResponseMessage and it's content.
Use GetAsync instead of GetStringAsync. GetAsync will not throw an exception and will allow you to access response content, status code and any other header you may require.
See this page for more.
Essentially what #RyanGunn posted but implemented in your code.
You should be able to ReadAsStringAsync from the resultString.Content
I am working on an SDK that employs similar code except we use a switch statement to check for various HttpStatusCodes that we intend to return before the DeserializeObject line.
using (var httpClient = new HttpClient())
{
try
{
string resultString = await httpClient.GetStringAsync(endpoint);
var result = JsonConvert.DeserializeObject<...>(resultString.Content.ReadAsStringAsync().Result);
return result;
}
catch (HttpRequestException ex)
{
throw ex;
}
}

How to deal with HTML error codes and timeout from HttpClient()

I'm using HttpClient to connect to a server (see simplified code below). I cant figure out how I would respond to HTML error codes (e.g. 403) and timeouts so I can report what the result is.
When I encounter a 403 error code an error pop-up occurs in Visual Studio. But I can figure out how I convert this into try in the code. i.e. is the name of the exception present in the error pop-up?
using System.Net.Http;
HttpClient client = new HttpClient();
var response = client.PostAsync(dutMacUrl, null).Result;
var result = response.Content.ReadAsStringAsync().Result;
you can use async/await feature to simplify your code and avoid using Result.
for example
public async Task<string> Foo(string uri)
{
var client = new HttpClient();
try
{
var response = await client.PostAsync(uri, null);
}
catch (Exception ex)
{
//here you handle exceptions
}
// use this if (response.StatusCode != HttpStatusCode.OK) { do what you want }
// or this if (response.IsSuccessStatusCode) { do what you want }
var result = await response.Content.ReadAsStringAsync();
return result;
}
If you are using webAPI another option is to use IHttpActionResult
public object IHttpActionResult mymethod()
{
// instantiate your class or object
IEnumerable<yourClass> myobject = new IEnumerable<yourClass>();
// assuming you want to return a collection
try
{
// do stuff
// handle dto or map result back to object
return Ok(myobject)
}
catch(Exception e)
{
// return a bad request if the action fails
return BadRequest(e.Message)
}
}
This would allow you to make a call to your api endpoint and either return a successful response with the updated object or return a bad request if the endpoint fails.

Categories

Resources