PostAsJsonAsync posts null - asp.net-core

I am trying to post an object using PostAsJsonAsync, but it is always null at the receiving API. The calling code is here:
public async Task UploadDocument(FileDto model)
{
var response = await _httpClient.PostAsJsonAsync("file/UploadDocument", model);
response.EnsureSuccessStatusCode();
}
The signature of the receiving code is here:
[HttpPost]
[Route("UploadDocument")]
public async Task<IHttpActionResult> UploadDocument(FileDto document)
FileDto is identical in both projects and only contains one string property "FileName"
The problem is that the document is always null.
I can use PostAsync which works fine:
public async Task UploadDocument(FileDto model)
{
string inputJson = Newtonsoft.Json.JsonConvert.SerializeObject(model);
HttpContent inputContent = new StringContent(inputJson, Encoding.UTF8, "application/json");
var response = await _httpClient.PostAsync("file/UploadDocument", inputContent);
response.EnsureSuccessStatusCode();
}
Looking at Fiddler, with the first (not working) example, the request looks like this:
POST http://localhost:59322/api/file/UploadDocument HTTP/1.1
Accept: application/json
Transfer-Encoding: chunked
Content-Type: application/json; charset=utf-8
Host: localhost:59322
28
{"FileName":"File-0000004157.jpg"}
0
The second (working) example looks like this in Fiddler:
POST http://localhost:59322/api/file/UploadDocument HTTP/1.1
Accept: application/json
Transfer-Encoding: chunked
Content-Type: application/json; charset=utf-8
Host: localhost:59322
{"FileName":"File-0000004157.jpg"}
The first example appears to have added extra text - see the "28" and "0".
Where is this text coming from. I can just go with PostAsync, but it seems a shame to add the extra code when PostAsJsonAsync does it for you.
Anybody have any ideas?

You need to return IActionResult instead of IHttpActionResult in asp.net core
[HttpPost]
[Route("UploadDocument")]
public async Task<IActionResult> UploadDocument(FileDto document)
{
return Ok(document);
}
https://learn.microsoft.com/en-us/aspnet/core/migration/webapi?view=aspnetcore-3.0
ASP.NET Core Web Api Error for IhttpActionResult

There seems to be an issue with PostAsJsonAsync in .net core : It returns a content-length of 0 causing the receiving ends to ignore the declared empty body.
https://github.com/aspnet/AspNetWebStack/issues/252
HttpClient PostAsJsonAsync behaving different in .NET Core and Classic .NET
The workaround is to use... PostAsync

Related

Bad Request (400) for POST request to ASP.NET Core 6, minimal API, sent from Postman

why I get 400 bad request when testing ASP.NET CORE minimal API app with Postman?
Default VS 2022 ASP.NET core minimal API project, with following method handler:
app.MapPost("/test", (AnalizeSentenceRequest r) =>
{
return Results.Ok($"Echo: {r.Sentence}");
});
DTO:
public class AnalizeSentenceRequest
{
public string? Sentence { get; set; }
}
Windows 10 or Ubuntu curls works fine:
> curl -X POST http://localhost:5050/test -H "Content-Type: application/json" -d "{\"sentence\":\"testing\"}"
response:
"Echo: testing"
But Postman gets 400:
Postman headers:
Edit: Postman response:
It complains that it cannot bind to DTO AnalizeSentenceRequest r
I tried to add [FromBody] attribute to the handler:
([FromBody] AnalizeSentenceRequest r) => ...
but it makes no difference.
It works with curl though... So it looks like Postman is messing up with the body, but what it might be?
Full response:
Microsoft.AspNetCore.Http.BadHttpRequestException: Required parameter "AnalizeSentenceRequest r" was not provided from body.
at Microsoft.AspNetCore.Http.RequestDelegateFactory.Log.RequiredParameterNotProvided(HttpContext httpContext, String parameterTypeName, String parameterName, String source, Boolean shouldThrow)
at lambda_method1(Closure , Object , HttpContext , Object )
at Microsoft.AspNetCore.Http.RequestDelegateFactory.<>c__DisplayClass46_3.<<HandleRequestBodyAndCompileRequestDelegate>b__2>d.MoveNext()
--- End of stack trace from previous location ---
at Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
at Swashbuckle.AspNetCore.SwaggerUI.SwaggerUIMiddleware.Invoke(HttpContext httpContext)
at Swashbuckle.AspNetCore.Swagger.SwaggerMiddleware.Invoke(HttpContext httpContext, ISwaggerProvider swaggerProvider)
at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)
HEADERS
=======
Accept: */*
Host: localhost:5050
Content-Type: application/json
Putting a check in the Content-Length checkbox in Postman. Reason being is that the Content-Length header is mandatory for messages with entity bodies, unless the message is transported using chunked encoding. Content-Length is needed to detect premature message truncation when servers crash and to properly segment messages that share a persistent connection.

Correctly perform the DELETE with RestSharp

Till now I've used RestSharp to perform POST/GET passing a JSON payload as parameter/body.
Now I've to perform a delete (you can see the example form documentation just here)
DELETE https://api.xxx.it/shipment
HTTP/1.1 Accept-Encoding: gzip,deflate
Content-Type: application/x-www-form-urlencoded
X-API-KEY: APIKEY123456789
Content-Length: 10 Host: api.xxx.it
Connection: Keep-Alive
User-Agent: Apache-HttpClient/4.1.1 (java 1.5)
id=1234567
and my code below.
public Task PerformShipmentDeleteAsync(ShipmentDeleteRequest objectRequest)
{
var client = new RestClient(settingsService.Endpoint);
var request = new RestRequest("shipment", DataFormat.Json);
request.AddHeader(Constants.XApiKey, settingsService.ApiXKey);
request.AddParameter( "text/plain",$"id={objectRequest.Id}", ParameterType.RequestBody);
var res = client.Delete(request);
return Task.CompletedTask;
}
and ShipmentDeleteRequest.cs
public class ShipmentDeleteRequest
{
[JsonProperty("id")]
public int Id { get;set; }
}
The only way I've found is to format the string in this way, but It's a hack.
How do I correctly pass the body as the example without passing a string but just the C# object?

How to concat rows text from a list into a success text msg in .Net Core wep api

concat rows text from a list into a success text msg in .Net Core wep api
Has nothing to do with WebAPI itself. It is more of a framework issue. Use string.Join(delimiter, ienumerable).
However, here is an example:
[Route("api/[controller]")]
public class DemoController : Controller
{
[HttpGet]
public IActionResult Get()
{
var listOfMessages = new[] {"these", "should", "be", "combined"};
var result = string.Join(' ', listOfMessages);
return Ok(result);
}
}
The result looks like this:
HTTP/1.1 200 OK
Content-Type: text/plain; charset=utf-8
Date: Tue, 22 May 2018 10:59:38 GMT
Content-Length: 24
these should be combined

Adding multiple allow headers is only adding the last header in http response

I am attempting to add multiple Allow Headers to the HttpResponse in aspnetcore v1.1.2. When I run the code below, the headers are added to the IHeaderDictionary on the HttpResponse, however only the last header (in this case "POST") in the collection is actually added to the http response when serialized. Has anyone else experienced this, or am I doing something wrong??
Here is the code I am using.
public override async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next)
{
context.HttpContext.Response.Headers.Add("Allow", new StringValues(new [] {"GET", "POST"}));
await next();
}
I am doing this within a ResultFilterAttribute.
Many Thanks...
This should fix the issue:
context.HttpContext.Response.Headers.Add("Allow", "GET, POST");
Your code is fine, the only place that you may need to change is your response parsing logic. Double check your response - it contains 2 Allow headers, not one header with 2 values:
Allow: GET
Allow: POST
Simple example:
Lets say you have next controller action:
[Route("api/[controller]")]
public class ValuesController : Controller
{
// GET api/values
[HttpGet]
[AddHeader]
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
}
Get request using curl:
curl -X GET http://localhost:5000/api/values -i
Response:
HTTP/1.1 200 OK
Date: Fri, 23 Jun 2017 22:23:24 GMT
Content-Type: application/json; charset=utf-8
Server: Kestrel
Transfer-Encoding: chunked
Allow: GET
Allow: POST
["value1","value2"]

How to post int array using $http to a ASP.Net Web Api Controller method

I am trying to post an array of integers to delete from a service using $http to ASP.Net Web API controller method. I have tried both
[HttpDelete]
public HttpResponseMessage DeleteMany(int[] ids) { }
[HttpDelete]
public HttpResponseMessage DeleteMany(DeleteManyDto dto) { }
public class DeleteManyDto
{
public int[] Ids {get;set;}
}
I keep getting the params as null. I have tried a couple of variations in the Angular service. Here is an example
this.deleteMany = function(ids) {
// ids is an int array. e.g. [1,4,6]
return $http.delete('api/Foo/DeleteMany', { toDelete: ids }).then(function(result) {
return result.status;
});
};
Anyone know what I could be missing?
UPDATE: Spelling mistake. Included debug from HTTP request.
Request URL:http://localhost:54827/api/Foo/DeleteMany
Request Method:DELETE
Status Code:500 Internal Server Error
Request Headersview source
Accept:application/json, text/plain, */*
Accept-Encoding:gzip,deflate,sdch
Accept-Language:en-US,en;q=0.8
Cache-Control:no-cache
Connection:keep-alive
Cookie:__RequestVerificationToken=blabla
Host:localhost:54827
Origin:http://localhost:54827
Pragma:no-cache
Referer:http://localhost:54827/
User-Agent:Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.101 Safari/537.36
Response Headersview source
The code above is not producing any request payload. When I do an update I see the request payload. Example
Request Payloadview parsed
{"id":4,"name":"zzzz", "bar": 2}
For what it is worth I am using Angular 1.2.rc3
Try changing your controller method signature to this:
[HttpDelete]
public HttpResponseMessage DeleteMany(int[] toDelete) { }
or if you using your ViewModel than change delete call to this:
return $http.delete('api/Foo/DeleteMany', { Ids: ids }).then(function(result) {
return result.status;
});
Edit:
But I think that real problem is in $http.delete call. You can't send body with DELETE verb in HTTP, and because MVC binds data from message body or url your array is not binded.
Try implement DeleteMany with POST request.
http://docs.angularjs.org/api/ng.$http#methods_delete
You see that in documentation delete method doesn't have data param in its definition.
Or you can try this approach, where you add your data to message header:
What is a clean way to send a body with DELETE request?
You have a spelling error in your API:
public HttpResponseMessage DeleteManay(int[] ids) { }
should be:
public HttpResponseMessage DeleteMany(int[] ids) { }
Update:
Appears it was a spelling mistake in the question.