blazor httpClient.PostJsonAsync status code exception - c#

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.

Related

Error converting JSON response from backend to type with JsonConvert.DeserializeObject

So I am having this weird problem with deserializing a response from my BackEnd, The request works fine and the BackEnd succesfully responds with a result.
This is the error I get:
'Error converting value "{"Succes":true,"AuthKey":"$2a$13..."}" to type 'FrontEnd.LoginUserResponse'. Path '', line 1, position 96.'
The code I am using to make the HTTP call and deserialize the string:
public async Task<bool> loginUser(LoginUserData login)
{
HttpContent httpContent = new StringContent(JsonConvert.SerializeObject(login), Encoding.UTF8);
httpContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
HttpResponseMessage responseMessage = await httpClient.PostAsync("http://ip/webBackEnd/api/user/login", httpContent);
string response2 = responseMessage.Content.ReadAsStringAsync().Result;
LoginUserResponse response = JsonConvert.DeserializeObject<LoginUserResponse>(response2);
if (response.Succes)
{
return true;
}
else { return false; }
}
I tried making a response2 to check the value and I have noticed it does something weird with 3 backslashes. This might be the reason why this is occuring.
This is response2 that visual studio shows when I click the magnifying glass:
"{\"Succes\":true,\"AuthKey\":\"$2a$11$tQCw4zGGd2J2fXAxAN68Ruu3xheTuMKq4EHbeLtc9DAa2rgzJe8bS\"}"
When I hover on visual studio:
https://imgur.com/a/jUyLz6d
This is the Class that it is converting to
public class LoginUserResponse
{
[JsonProperty("succes")]
public bool succes { get; set; }
[JsonProperty("authkey")]
public string authkey { get; set; }
}
The Backend code:
[HttpPost]
[Route("login")]
public string Login([FromBody]LogInData logInData)
{
IReadUser.LogInRequest request = new IReadUser.LogInRequest(logInData);
IReadUser.LogInResponse backResponse = readUser.logIn(request);
LogInResponse response = new LogInResponse();
response.succes = backResponse.Succes;
response.authkey = backResponse.AuthKey;
return JsonConvert.SerializeObject(response);
}
EDIT // SOLUTION
Ok so, the front-end was fine, it was my backend code sending a double serialised string. I used
return JsonConvert.SerializeObject(response);
When I also could have used
return response;
So if you every get an error like this, it's probably the backend doubling up on the serialization.
Thanks for all the help!
Couple of things:
1. you should await responseMessage.Content.ReadAsStringAsync():
public async Task<bool> loginUser(LoginUserData login)
{
var httpContent = new StringContent(JsonConvert.SerializeObject(login), Encoding.UTF8);
httpContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
var responseMessage = await httpClient.PostAsync("http://ip/webBackEnd/api/user/login", httpContent);
var response2 = await responseMessage.Content.ReadAsStringAsync();
var response = JsonConvert.DeserializeObject<LoginUserResponse>(response2);
return response.Succes
}
And 2. Based on your image it looks like the response from your backing service is being serialized twice.
1:
"{\"Succes\":true,\"AuthKey\":\"$2a$11$tQCw4zGGd2J2fXAxAN68Ruu3xheTuMKq4EHbeLtc9DAa2rgzJe8bS\"}"
2:
"\"{\\\"Succes\\\":true,\\\"AuthKey\\\":\\\"$2a$11$tQCw4zGGd2J2fXAxAN68Ruu3xheTuMKq4EHbeLtc9DAa2rgzJe8bS\\\"}\""
now to deserialize you have to do it twice
var s = JsonConvert.DeserializeObject<string>(response2);
var response = JsonConvert.DeserializeObject<LoginUserResponse>(s);
Probably best to fix the service if that's actually what is happening

Why is my HTTP POST Body content received/processed as NULL?

I want to be able to receive the Body content of a POST request inside the POST function definition in the REST API.
I have a client code that converts a C# object into JSON and then wraps it in a HTTP StringContent. This payload is then sent via a HTTP Post request to the URL. However the Post method in API always returns NULL when I try returning the received string.
Client:
public async void Register_Clicked(object sender, EventArgs e) // When user enters the Register button
{
Sjson = JsonConvert.SerializeObject(signup);
var httpContent = new StringContent(Sjson);
using (HttpClient client = new HttpClient())
{
client.BaseAddress = new Uri("https://apiname.azurewebsites.net");
var response = await client.PostAsync("api/values", httpContent);
var responseContent = await response.Content.ReadAsStringAsync();
StatusLabel.Text = responseContent; //To display response in client
}
}
API POST Definition:
[SwaggerOperation("Create")]
[SwaggerResponse(HttpStatusCode.Created)]
public string Post([FromBody]string signup)
{
return signup;
}
I want the clients input back as a response to be displayed in the client (StatusLabel.Text). However all I receive is NULL. Kindly guide me in the right direction.
I have prepared a simple example on how you can retrieve your Result from your Task:
async Task<string> GetResponseString(SigupModel signup)
{
Sjson = JsonConvert.SerializeObject(signup);
var httpContent = new StringContent(Sjson);
using (HttpClient client = new HttpClient())
{
client.BaseAddress = new Uri("https://apiname.azurewebsites.net");
var response = await client.PostAsync("api/values", httpContent);
var responseContent = await response.Content.ReadAsStringAsync();
}
return responseContent;
}
And you can call this as:
Task<string> result = GetResponseString(signup);
var finalResult = result.Result;
StatusLabel.Text = finalResult; //To display response in client
If you are not using async keyword, then you can call get your Result as:
var responseContent = response.Content.ReadAsStringAsync().Result;
EDIT
Basically you would have to POST your signup model to your API Controller. Use SigupModel signup as a parameter if you would like. Then whatever processing you are doing in the API, the final result in a string. Now this string can either be a null, empty or have a value. When you get this value in your client, you would have do the following:
var responseContent = await response.Content.ReadAsStringAsync();
var message = JsonConvert.DeserializeObject<string>(responseContent);
Here you will get your message a string and then you can set it as: StatusLabel.Text = message ;

HttpClient GetAsync not working as expected

When testing my web API with Postman my API get executes fine!
When it comes to running the code with HttpClient in my client application the code executes without error but without the expected result on the server.
What could be happening?
From my client application:
private string GetResponseFromURI(Uri u)
{
var response = "";
HttpResponseMessage result;
using (var client = new HttpClient())
{
Task task = Task.Run(async () =>
{
result = await client.GetAsync(u);
if (result.IsSuccessStatusCode)
{
response = await result.Content.ReadAsStringAsync();
}
});
task.Wait();
}
return response;
}
Here is the API controller:
[Route("api/[controller]")]
public class CartsController : Controller
{
private readonly ICartRepository _cartRepo;
public CartsController(ICartRepository cartRepo)
{
_cartRepo = cartRepo;
}
[HttpGet]
public string GetTodays()
{
return _cartRepo.GetTodaysCarts();
}
[HttpGet]
[Route("Add")]
public string GetIncrement()
{
var cart = new CountedCarts();
_cartRepo.Add(cart);
return _cartRepo.GetTodaysCarts();
}
[HttpGet]
[Route("Remove")]
public string GetDecrement()
{
_cartRepo.RemoveLast();
return _cartRepo.GetTodaysCarts();
}
}
Note these API calls work as expected when called from Postman.
You shouldn't use await with client.GetAsync, It's managed by .Net platform, because you can only send one request at the time.
just use it like this
var response = client.GetAsync("URL").Result; // Blocking call!
if (response.IsSuccessStatusCode)
{
// Parse the response body. Blocking!
var dataObjects = response.Content.ReadAsAsync<object>().Result;
}
else
{
var result = $"{(int)response.StatusCode} ({response.ReasonPhrase})";
// logger.WriteEntry(result, EventLogEntryType.Error, 40);
}
You are doing fire-and-forget approach. In your case, you need to wait for the result.
For example,
static async Task<string> GetResponseFromURI(Uri u)
{
var response = "";
using (var client = new HttpClient())
{
HttpResponseMessage result = await client.GetAsync(u);
if (result.IsSuccessStatusCode)
{
response = await result.Content.ReadAsStringAsync();
}
}
return response;
}
static void Main(string[] args)
{
var t = Task.Run(() => GetResponseFromURI(new Uri("http://www.google.com")));
t.Wait();
Console.WriteLine(t.Result);
Console.ReadLine();
}
Simple sample used to get page data.
public string GetPage(string url)
{
HttpResponseMessage response = client.GetAsync(url).Result;
if (response.IsSuccessStatusCode)
{
string page = response.Content.ReadAsStringAsync().Result;
return "Successfully load page";
}
else
{
return "Invalid Page url requested";
}
}
I've had a problem with chace control when using httpclient.
HttpBaseProtocalFilter^ filter = ref new HttpBaseProtocolFilter();
filter->CacheControl->ReadBehavior = Windows::Web::Http::Filters::HttpCacheReadBehavior::MostRecent;
HttpClient^ httpClient = ref new HttpClient(filter);
I'm not really sure what the expected results are or what results your getting at all so this is really just a guessing game right now.
When I POST something using HttpClient I found adding headers by hand seemed to work more often than using default headers.
auto httpClient = ref new HttpClient();
Windows::Web::Http::Headers::HttpMediaTypeHeaderValue^ type = ref new Windows::Web::http::Headers::HttpMediaTypeHeaderValue("application/json");
content->Headers->ContentType = type;
If I don't do these 2 things I found, for me anyways, that half the time my web requests were either not actually being sent or the headers were all messed up and the other half of the time it worked perfectly.
I just read a comment where you said it would only fire once, that makes me think it is the cachecontrol. I think what happens is something (Windows?) sees 2 requests being sent that are the exact same, so to speed things up it just assumes the same answer and never actually sends the request a 2nd time

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.

Using HttpResponseMessage to view errors

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;
}

Categories

Resources