Unexpected end of Stream Exception on redirection - c#

I am testing an ASP.NET MVC application. There is a contoller with a method that looks like:
[HttpPost]
public async Task<ActionResult> Login(LoginForm loginForm)
{
// Do some work
return RedirectToAction(nameof(Admins));
}
The signature of Admins method looks like:
public async Task<ActionResult> Admins(ExtendedPagingForm form)
and I try to invoke it like this:
var url = $"{TestConfig.Instance.ServerUrl}/{actionMethod}";
var sendForm = GetFormContent(new MultipartFormDataContent(), sendData); //login and password
HttpResponseMessage response = await httpClient.PostAsync(url, sendForm);
The response contains Internal server error with info:
IOException: Unexpected end of Stream, the content may have already
been read by another component.
On MSDN it is said that RedirectToAction returns an HTTP 302 response to the browser, which causes the browser to make a GET request to the specified action. So it must not work neither in browser, nor in Postman, but it works fine there. Can I force my httpClient to send POST request with data or some mock request body?

I've solved the problem.
I changed MultipartFormDataContent to FormUrlEncodedContent and it works

In my case I was already using FormUrlEncodedContent but it was still happening. Mine was a Xamarin project.
Restarting Visual Studio and Emulator helped me.

Related

Simple POST with HttpClient

I have two ASP.NET Core 2.1 apps and I'm trying to make a simple POST call from one app to the other using HttpClient.
For some reason, when I use [FromBody] to get the simple text I'm trying receive, I get a BadRequest error.
Here's my code on the sender. First, this is what's in my ConfigureServices method. I'm using the new HttpClientFactory feature in ASP.NET Core 2.1. I also created a client class named myApiCallerClient that handles my API calls:
services.AddHttpClient("myNamedClient", client =>
{
client.BaseAddress = new Uri("http://localhost:50625/api/");
client.DefaultRequestHeaders.Clear();
client.DefaultRequestHeaders.Add("Accept", "application/json");
});
services.AddScoped(x => {
var httpClientFactory = x.GetRequiredService<System.Net.Http.IHttpClientFactory>();
var myApiCallerClient = httpClientFactory.CreateClient("myNamedClient");
return new Clients.ApiCaller.ApiCallerClient(myApiCallerClient);
});
Here's the code in myApiCallerClient:
public async Task SayHello()
{
var response = await _httpClient.PostAsync("test", new StringContent("Saying hello!", System.Text.Encoding.UTF8, "text/plain"));
}
And here's my code on the receiving end which is the POST() API method in TestController:
[HttpPost]
public async Task Post([FromBody]string input)
{
// Some logic
}
My call doesn't hit this method if I use [FromBody] and I get BadRequest error on the sender. If I use [FromHeader] my request hits this API method but I'm getting a null value.
What am I doing wrong here?
ASP.NET Core doesn't support a Content-Type of text/plain out of the box, so the server is rejecting your request as it's not something that it can parse when using the [FromBody] attribute.
In the comments, you said:
Ultimately I won't be sending text. My main concern here is that I'm not hitting my API method. [...] Once I make sure everything is working, I'll be sending JSON data which will get deserialized on the receiving end.
In order to test whether the problem is due to the text/plain Content-Type, you can update your PostAsync line to something like this:
var response = await _httpClient.PostAsync(
"test",
new StringContent("\"Saying hello!\"", System.Text.Encoding.UTF8, "application/json"));
As application/json is a supported Content-Type by default, the code above uses that and wraps the value you were sending with "s in order to make it a valid JSON value.

Why does Firefox have a problem with this 204 (No Content) response?

I'm calling a WebAPI controller from jQuery AJAX to request an item be deleted via a REST API.
The WebAPI controller returns a 204 (No Content) response, which then invokes a second request (GET). For this example I expect both requests to receive a 204 (No Content) response.
When viewed in Chrome both responses are fully recognised.
However, Firefox can't seem to cope with a 204 if it contains no content.
To obtain both the Chrome (above) and Firefox (below) screenshots the same requests were sent to a WebAPI controller from both browsers. The screenshot below shows that Firefox only recognises the first 204 response.
The response to these two requests is created differently:
The first response uses...
return Request.CreateResponse(HttpStatusCode.NoContent);
...which returns an instance of HttpResponseMessage.
The second response uses...
return new NoContentResult();
...which we've defined as an implementation of IHttpActionResult...
public class NoContentResult : IHttpActionResult
{
private readonly HttpRequestMessage _request;
public NoContentResult(HttpRequestMessage request)
{
_request = request;
}
public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
var response = _request.CreateResponse(HttpStatusCode.NoContent, new StringContent("{}"));
return Task.FromResult(response);
}
}
The new StringContent("{}") on the 4th-from-last line is an attempt to provide some content to help Firefox. I've also tried new StringContent("null") with the same result.
Also, if that second response is inspected in Firefox, the following error message is found:
SyntaxError: JSON.parse: unexpected end of data at line 1 column 1 of
the JSON data
It seems that Firefox is trying to parse this problematic 204, but it apparently makes no attempt to parse the first 204 response.
Furthermore, the request type (DELETE vs. GET) doesn't seem to matter: if I return 204 as an IHttpActionResult from the first call then Firefox still doesn't like it.
Firefox also displays the first response as containing 168 bytes and the second as containing 0 bytes even though it will happily display the headers (below). Firefox is clearly upset at something.
Can anyone explain why Firefox has a problem with a 204 response when delivered as an implementation of IHttpActionResult but not when it's delivered as an HttpResponseMessage?
[The reason that the first request returns an HttpResponseMessage while the second returns an IHttpActionResult is that the first method has been in place for a long time whereas the second method is only now being added. And as IHttpActionResult is easier to test, we'd like to use that.]

How to handle PostJsonAsync() call

Currently I have 3rd party WebApi which has the following external call using Flurl builder.
await _client.Url.ToString().PostJsonAsync(data);
And I'm trying to handle the response with such endpoint:
[HttpPost]
public void HandleResponse(HttpResponseMessage response)
{
}
The response message is with status OK but has Content and Headers = null
How can I handle this properly?
This API endpoint doesn't make any sense to me:
[HttpPost]
public void HandleResponse(HttpResponseMessage response)
{
//...
}
The endpoint would be handling a request and returning a response, not the other way around. Something more like this:
[HttpPost]
public HttpResponseMessage HandleResponse(HttpRequestMessage request)
{
//...
}
When something contacts an API (or server of any kind, really), it's sending a request to that API. That API receives the request and returns a response. The semantics of these two words pretty much describe the process, it's very important to keep them straight.
Consider it like this... If someone asks you a question, what you receive is a question. Not an answer. What you send back to that person is the answer.

Web API call works but gives exception The view 'xxx' or its master was not found or no view engine supports

Strange one here, code calls the method, and the method grabacat is executed on the server (I debug it and step through right to the end). The code returns to the client, but the response it received was 500 Internal Server Error with the above message. So it's saying it couldn't find the web API method that it just called successfully.
using (var response = await client.PostAsXmlAsync("cats/grabacat", mycatprefs))
{
if (response.IsSuccessStatusCode) // 500 cats/grabacat not found
Controller code:
[Route("~/api/cats/grabacat")]
[HttpPost]
public async Task GrabACat()
{
}
After debugging, if I change it to public async Task<SomeObject> GrabACat() then it works OK. This lets me return an object back to the client. However I don't want to return anything back; I want it to be equivelent to calling a void method. It will examine the status code to determine if it was successful.
I have got it working by changing GrabACat to Task and returning new object(); but I am not sure why this is required. It seems crude to return an empty object just to get it to work.
Any ideas?
The WebAPI method has a Route attribute like this:
[Route("~/api/cats/grabacat")]
Which means the URL is wrong in the POST request - you are missing the /api prefix:
using (var response = await client.PostAsXmlAsync("api/cats/grabacat", mycatprefs))
//snip

HttpClient GET request fails, POST Succeeds

In a Xamarin application (backed by ASP.NET WebApi), I'm having trouble getting [all of] my GET requests to succeed -- they return 404. In fact, when watching network traffic in Fiddler, I don't even see the request happen.
Here is [basically] what I'm doing:
public async Task<bool> ValidateSponsor(string attendeeId, string sponsorId)
{
string address = String.Format("{0}/Sponsors/?attendeeId={1}&sponsorId={2}", BASE_URI, attendeeId, sponsorId);
var response = await client.GetAsync(address);
var content = response.content;
if (!response.IsSuccessStatusCode)
throw new HttpRequestException("Check your network connection and try again.");
string result = await content.ReadAsStringAsync();
return Convert.ToBoolean(result);
}
If I copy out the address variable and paste it into a browser, it succeeds. POST requests (to different methods, of course) succeed. I've also tried using the PCL version RestSharp but get the same results -- POST succeeds and GET fails.
Edit:
This looks like it also may only be a problem when deployed to Azure, it works fine locally.

Categories