Dispose HTTP response in .NET WebAPI - c#

I have a .NET WebAPI application and this is one of my api:
public IHttpActionResult Get()
{
...building myResult here...
var content = ElasticSearch.Json.ToJson(myResult);
var response = Request.CreateResponse(HttpStatusCode.OK);
response.Content = new StringContent(content, Encoding.UTF8, "application/json");
return ResponseMessage(response);
}
I get a CA2000 error from code analisys:
Error CA2000 In method 'GroupsController.Get(string, string, string,
bool, string)', call System.IDisposable.Dispose on object 'response'
before all references to it are out of
scope
So I modified the code like this:
var content = ElasticSearch.Json.ToJson(myResult);
using (var response = Request.CreateResponse(HttpStatusCode.OK))
{
response.Content = new StringContent(content, Encoding.UTF8, "application/json");
return ResponseMessage(response);
}
So far so good. No memory leaks and the code analyzer is happy again.
Unfortunately now one of my test is complaining that it cannot access anymore a disposed object. Here the test testing that api (just the final part):
// Assert
var httpResponseMessage = await result.ExecuteAsync(CancellationToken.None);
var resultJson = await httpResponseMessage.Content.ReadAsStringAsync();
Assert.AreEqual(expectedJson, resultJson);
The Assert() is complaining that it cannot access an already disposed object, that is the actual api result:
System.ObjectDisposedException : Cannot access a disposed object.
Object name: 'System.Net.Http.StringContent'. at
System.Net.Http.HttpContent.CheckDisposed() at
System.Net.Http.HttpContent.ReadAsStringAsync()
How can I fix that? Disposing the object seems reasonable but at the same time the test should be able to access it

You can use ApiController.OK
return Ok(myResult);
You should not use using (var response = Request.CreateResponse(HttpStatusCode.OK)) because ResponseMessageResult will hold reference to disposed HttpResponseMessage. That's the reason you get this error in assert.
To check, change your code to snippet below and add breakpoint on result. Check result.Response.disposed
using (var response = Request.CreateResponse(HttpStatusCode.OK))
{
response.Content = new StringContent(content, Encoding.UTF8, "application/json");
result = ResponseMessage(response);
}
// result.Response.disposed is true hence error in assert.
return result;

Related

API HTTP client returning data

I am working on this helper method that will call an API using the body section. I am passing in the url and data in the model. Then I SerializeObject the model, but I am not sure what to return I get the error message about the response.Content is not found.
public static async System.Threading.Tasks.Task<HttpResponse> HttpClientHandlerAsync(string url, object model)
{
var fullUrl = apiUrl + url;
var json = JsonConvert.SerializeObject(model);
var data = new StringContent(json, Encoding.UTF8, "application/json");
Client.DefaultRequestHeaders.Add("Accept", "*/*");
Client.DefaultRequestHeaders.Authorization
= new AuthenticationHeaderValue("Bearer", "token");
var response = await Client.PostAsync(fullUrl, data);
return response;
}
Add await in front of your
await Client.PostAsync(fullUrl, data);
Because you're trying to get content of Task
I am not sure what to return I get the error message about the response.Content is not found.
Set a breakpoint and hover over the response to see the status code. You could have a 500 server error, authentication error etc.
Furthermore
using (var client = new HttpClient())
Do not do this. It doesn't work the way you think it does, it will starve your connection pool and eventually throw an exception. You need to define the HttpClient somewhere and continue to reuse the same instance.
Further reading if you care https://aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong/

C# System.Net.Http.StringContent() gives exception during the run time

We have the following piece of code where we use constructor of System.Net.Http.StringContent(). This following code is executed by multiple threads in my application.
public static HttpContent GetContent(this object model)
{
var body = JsonConvert.SerializeObject(model, Formatting.Indented, new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore });
var content = new StringContent(body, Encoding.UTF8, "application/json");
content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
return content;
}
We are getting Null reference exception in the execution of above code. "body"
param is just a string variable which we confirmed it to be not null. The following is the stack trace:
System.NullReferenceException : Object reference not set to an
instance of an object. at System.Text.Encoding.get_WebName() at
System.Net.Http.StringContent..ctor(String content, Encoding encoding,
String mediaType) at
AW.Api.Client.Extensions.HttpExtensions.GetContent(Object model,
String contentType)
StringContent() is trying to access the WebName property of Encoding.cs and on seeing the library code of Encoding.cs,
https://referencesource.microsoft.com/#mscorlib/system/text/encoding.cs,00987bab2ca262fa
We can notice that while accessing the property "WebName", a private field "dataItem" is being referenced after a null-check. Can anyone please help in understanding what might go wrong here? Is there a possibility of race condition where "dataItem" is being referenced by one thread while other thread setting it to null.
It seems like this code line not the true exception line.
Try to check the LINE REFERENCED THE VARIABLE "content"
Here is the code example ,I assume you use System.Net.Http.HttpClient
using (HttpClient client = new HttpClient())
{
var content = new StringContent(body, Encoding.UTF8, "application/json");
client.BaseAddress = new Uri(url);
client.DefaultRequestHeaders.Clear();
// below is the variable content
var response = await client.PostAsync(url, content).ConfigureAwait(true);
if (!response.IsSuccessStatusCode) throw new FileNotFoundException();
var responseContent = response.Content;
return await responseContent.ReadAsStringAsync().ConfigureAwait(true);
}

Call async method HttpClient without await throw error

When i call async method using httpclient without await cause i don't want to wait response
then throw exception "object reference not set to an instance of an object"
\\ Call PostAsync
var ignore = webRequest.PostAsync(json, token);
\\ PostAsync method
using (var httpClient = new HttpClient())
{
if (!string.IsNullOrEmpty(auth))
httpClient.DefaultRequestHeaders.TryAddWithoutValidation("Authorization", auth);
var content = new StringContent(json, Encoding.UTF8, "application/json");
// this line throw exception
var response = await httpClient.PostAsync(this.URL, content);
if (response.IsSuccessStatusCode)
{
result = await response.Content.ReadAsStringAsync();
}
}
and then i get error on my visual studio image below.
image
if i don't want to wait response, how i do it?
I think the problem is the basic concept of async await, check this link for a clearer idea:
https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/async/

Post using HttpClient & Read HttpResponseMessage status

I am posting to an API using HttpClient and getting back the HttpResponseMessage.
I am reading the status code from the reply but I it's always 200
Posting:
var json = JsonConvert.SerializeObject(loginDto);
var stringContent = new StringContent(json, Encoding.UTF8, "application/json");
var client = new HttpClient();
var response = await client.PostAsync("http://localhost:57770/api/Account/Login", stringContent);
I am replying from API the HttpResponseMessage:
return new HttpResponseMessage(HttpStatusCode.Unauthorized);
But when I read the response, it's always 200
How can I achieve this?
Asp.Net Core no longer recognizes HttpResponseMessage as part of the pipeline. This means it will be treated like any other returned model and serialized as content. Hence the 200 OK status.
The API controller action should return IActionResult derived result.
[HttpPost]
public IActionResult SomeAction(...) {
//...
return StatusCode((int)HttpStatusCode.Unauthorized); //401
//...
}
Or just use
return Unauthorized();
which is derived from StatusCodeResult and is used a short hand to replace the code shown above.
Reference ControllerBase.Unauthorized.

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