A non-empty request body is required - c#

I've found a few other questions here on SO that address this issue; however, all those questions seem to have had their issues in the type of request being sent wether content-type was the issue, or model binding. In my situation, the error seems to get thrown on the Ok at the end of the action method.
So far I have have tried the following:
Adding/removing FromBody attribute
Ensuring my testing framework Swagger is sending as application/json
I tried changing to to a Patch just to see what would happen but the issue still persists
I also tried changing the return from Ok to NoContent and the issue still persists
As I said above, I did step through this code and verified that I hit the mapping code and all properties are properly mapped. The user itself does get updated in the database. The error is only thrown after execution exits the action method (after Ok) is called. So this throws me off even more since the error insists my request body is empty.
Are there any other reasons this error would be thrown besides an actual empty request body or wrong content-type payload?
/// <summary>
/// Updates the provided account and setting information
/// </summary>
/// <param name="vm"></param>
[HttpPost]
[ProducesResponseType(200)]
[ProducesResponseType(400, Type=typeof(string))]
public async Task<IActionResult> UpdateAccountInfo([FromBody]UpdateAccountInfoViewModel vm)
{
var appModel = _mapper.Map<ChartLog.Application.Models.UpdateAccountInfoModel>(vm);
appModel.UserId = User.GetUserId().Value;
await _accountApp.UpdateAccountAndSettings(appModel);
return Ok();
}

The problem ended up being with a bug in a custom middleware I created. I was replaying the request twice.
public async Task InvokeAsync(HttpContext context, RequestDelegate next)
{
string stringBody = string.Empty;
try
{
await next(context).ConfigureAwait(false);
}
catch (Exception ex)
{
if (context.Request.Body.CanSeek)
{
stringBody = await FormatRequest(context.Request).ConfigureAwait(false);
}
await HandleExceptionAsync(context, ex, stringBody).ConfigureAwait(false);
}
//await next(context).ConfigureAwait(false);
}
The commented out code was the culprit. If no exception occurred then it would exit the try and essentially replay the same RequestDelegate. My assumption is the request stream has already been read and is already disposed or read to the end.

I have got the same issue when I try to learn HttpPath. And I found the root cause is: I didn't pass the "Content-Length" value in Postman.

I have got the issue on sending data to Swagger Api. When Swagger is sending as application/json it is needed to support the data just in raw json format in body.
As the Content-Type is JSON, sending data as parameters in URL will return the response:
A non-empty request body is required
And if the data is placed in body with Form format this is leaded to this response :
"'-' is invalid within a number, immediately after a sign character ('+' or '-'). Expected a digit ('0'-'9'). Path: $ | LineNumber: 0 | BytePositionInLine: 1."
Put the data in raw json to resolve the issue.
{
"username": "abcd",
"password": "123456"
}

A bit late to the party here, but I just ran into this error and traced it to another cause that has not been mentioned yet. In my case, the deserialization of the body was failing due to case-sensitivity and/or naming conventions, and I got that rather unhelpful error message. I found that it worked fine if I ensured that all the property names in the JSON were lowercase, but not otherwise. Your mileage may vary, but for me the fix was to add this to my startup:
services.AddControllers()
.AddJsonOptions(options =>
{
options.JsonSerializerOptions.PropertyNameCaseInsensitive = true;
options.JsonSerializerOptions.PropertyNamingPolicy = null;
});

My problem was how I was sending the parameters. So my final code looks like;
My controller:
[Route("api/[controller]")]
[Produces(MediaTypeNames.Application.Json)]
[ApiController]
public class MyApiController : BaseApiController
{
public void Post(TestObject value)
{
}
}
My http call:
var _post = function (data) {
var requestJSON = JSON.stringify(data);
var _defer = $q.defer();
$http({
method: 'POST',
url: "https://" + window.location.host + "/api/MyApi/",
data: requestJSON,
headers: { 'Content-Type': 'application/json; charset=utf-8' }
})
.then(function (result) {
_defer.resolve(result.data);
},
function () { _defer.reject(); });
return _defer.promise;
};
And how I build my parameter:
$scope.save = function () {
var parameters = new Object();
parameters.Id = 1;
parameters.Name = 'Juan Perez';
app.POST(parameters).then(function () {
console.log('ok');
}, function (data) {
console.log('error');
});
}

Related

"Http failure response for http://localhost:49540/api/create/: 404 Not Found" Angular 11 POST request

I am currently developing a client app using Angular 11, that consumes .Net 5 API. The API get request is working as expected but when I send a POST request it fails with the error saying
POST http://localhost:49540/api/create/ 404 (Not Found)
Http failure response for http://localhost:49540/api/create/: 404 Not Found
I have tried removing the trailing slash at the end but it didn't help.
Here my API action method
[Route("api/[controller]")]
[ApiController]
public class BooksController : ControllerBase
{
[HttpPost]
[Route("create")]
public async Task<IActionResult> AddBook(CreateBook model)
{
var book = mapper.Map<Book>(model);
// Replace value with userManager.GetUserId(User);
try
{
book.CreatedByUserId = Fakes.Faker.FakeUserId();
book.DateCreated = DateTime.Now;
book.BookStatus = BookStatus.Available;
book.BookId = new Guid();
return Ok(await bookService.AddAsync(book));
}
catch (Exception ex)
{
return StatusCode(StatusCodes.Status500InternalServerError, new ResponseMessage
{
Succeeded = false,
Errors = new List<string>() { ex.Message }
});
}
}
}
Client Code
addBook(book: CreateBook): Observable<CreateBook> {
return this.httpClient.post<CreateBook>(baseURL + 'create', book, {
headers: new HttpHeaders({
'Content-Type': 'application/json'
})
}).pipe(catchError(this.handleError));
}
handleError(errorResponse: HttpErrorResponse): Observable<never> {
if (errorResponse instanceof ErrorEvent) {
console.error("Client side error: ", errorResponse);
} else {
console.error("Server side Error: " + errorResponse.message);
}
return throwError("Message");
}
I have configured CORS to allow any header, method and origin.
What is the problem? Is it with my code or there is something Missed? I am confused because get request works correctly, also the post request is working in Swagger.
Your current url fails because you're missing a fragment in the path. To understand why, let's look at your controller's [Route] attribute.
[Route("api/[controller]")]
This means the path segment of the url must start with /api/Books, because your controller is named "Books" (this works by taking the class name and removing the word "Controller" from the end if it exists).
This is then appended with the action's [Route] attribute.
[Route("create")]
So the full expected url path is /api/Books/create. "Books" can also be lowercase if you want, the code will understand it either way.
For further reading, see Routing in ASP.NET Core - Microsoft Docs.
Just change your action route attribute
[HttpPost]
[Route("~/api/create")]
[Route("~/api/books/create")]
public async Task<IActionResult> AddBook(CreateBook model)

JSON posted in body of http.request comes up null on ASP.NET server side

I am having an issue with posting JSON to my web server I have a ASP.NET web application running on a server that utilizes attribute routing to route requests. Here's the problem area of that project:
[HttpPost]
[Route("users/new")]
public string PostDataModel([FromBody] dataModel jsonObj)
{
try
{
WebApiApplication.db.AddToDatabase(jsonObj);
return "successfully inserted into database";
}
catch (Exception ex)
{
Tools.Logger.log("[POST DATA][ERROR] : " + ex.Message);
return ex.Message;
}
}
As of now, using Postman to manually send a POST request with the JSON inserted into the request body, and the Content-Type set to application/json, the function successfully parses the JSON into the correct object specified ([FromBody] DataModel). Also I do have accessors for all the properties in DataModel.
The issue comes in with my unit tests. I created a Node.js project to do some unit testing against the API. I am using the http module to do this. Heres where the requests are made:
req = http.request(options, (response) => {
var body = '';
response.on('data', (d) => {
body += d;
});
response.on('end', () => {
if (options.method == 'POST') {
options.body = JSON.stringify(options.body);
});
)};
the "options" parameter is defined as:
var options = {
method: 'POST',
host: customSite,
path: '/users/new',
headers: { 'Content-Type' : 'application/json' },
body: {
'UserId': "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
'UserName': "Testing Suite",
'Name': "Tester",
'Email': "tester#test.com",
'Password': "fake,",
'BirthDate': "1/1/0001 12:00:00 AM",
'NumberOfItems': 0,
'Links': [],
'TotalLikes': 100
},
}
Now when I run the unit test the API throws an exception which is caught which comes out as : "Object reference not set to an instance of an object."
The JSON I'm passing in the unit test is literally copied and pasted from the POST request i used in Postman, which worked. I cannot figure out what the issue is that is causing the API to fail when hit from my Node.js unit tests and to succeed when hit from Postman.

FileUpload in angularjs

I'm using angular-file-upload as found in Here. It was working for what I needed, but now the need has changed, and I'd like to be able to send additional data about (object) along with the file. There isn't much documentation about it. With what I could see, when I used options directive as attribute and provided the data as object, it's not listed anywhere in FileUploader. Also, my controller gives me an error when I upload. I added a Model class in Post as argument, which is what causing the controller to break.
[HttpPost]
public Task<HttpResponseMessage> PostFile(QAFileAttribute QAFile)
{
this.QAFile = QAFile;
string newLocation = GetCurrentUploadDirectory();
HttpRequestMessage request = this.Request;
if(!request.Content.IsMimeMultipartContent())
{
throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
}
string root = UploadLocation; //newLocation;
var provider = new MultipartFormDataStreamProvider(root);
var task = request.Content.ReadAsMultipartAsync(provider)
.ContinueWith<HttpResponseMessage>(o =>
{
if (o.IsFaulted || o.IsCanceled)
{
Request.CreateErrorResponse(HttpStatusCode.InternalServerError, o.Exception);
}
foreach (MultipartFileData file in provider.FileData)
{
if(String.IsNullOrEmpty(file.Headers.ContentDisposition.FileName))
{
return Request.CreateResponse(HttpStatusCode.NotAcceptable, "Filename is not acceptable.");
}
UploadFile(file, QAFile);
}
return Request.CreateResponse(HttpStatusCode.OK);
});
return task;
}// end Post
So, how can I send the multi-part file along with additional data?
This link will sure help you , I have implemented this.
https://uncorkedstudios.com/blog/multipartformdata-file-upload-with-angularjs
app.service('fileUpload', ['$http', function ($http) {
this.uploadFileToUrl = function(file, additional, uploadUrl){
var fd = new FormData();
fd.append('file', file);
$.each(additional, function(obj) {
fd.append(obj.key,obj.value);
});
$http.post(uploadUrl, fd, {
transformRequest: angular.identity,
headers: {'Content-Type': undefined}
})
.success(function(){
})
.error(function(){
});
}
}]);
I figured it out myself. This link helped a lot. Turned out, I didn't have to do a whole lot to provide additional data. I just needed to provide formData as request body to be processed inside the action; the controller action doesn't take any parameters. The problem with my code was that (I want to say due to improper documentation of angular-file-upload) I had misunderstood what formData is. Turned out, it's array of objects. It needed to be like this:
var uploader = new FileUploader({
url: 'api/upload',
formData: [{...}]
});
This sent the data to the controller action as request body. From there, I just had to access it as provider.FormData["somename"];

JQuery.ajax does not wait for C# Server's response and calls error function, testing on localhost

On the user-side, I have a javascript code, that POSTs to the server via JQuery.ajax. On the server-side, I receive the request and handle it, but the user-side script does not wait for the servers response and calls error: getFail (see below) function.
EDIT: problem was due to having the server and the user-side on different domains, in my case different localhosts. See detailed answer by #Jacob below.
How did I check if the user-side waits for the server or not?
I've just run the server in debug mode, and set a breakpoint where it receives the POST-ed values. And the user-side already called the error function.
Could anybody tell me, why it is not working "properly" or what and where is the source of error?
What I suspect to be one of the sources of the "improper" behavior is the dataType parameter of the JQuery.ajax function. It is supposed to specify the expected type of the return value from the server. I am not sure what to set this, so I did not specify hoping the default will find out.
[For the dataType parameter] If none is specified, jQuery will try to infer it based on the MIME type of the response. Possible types are: xml, html, script, json, jsonp, text, multiple. (Source: http://api.jquery.com/jQuery.ajax/, JQuery docs).
Here are the codes:
User-side javascript code:
function logIn() {
try {
alert('try in logIn');
jQuery.ajax({
type: "POST",
url: "http://localhost:8080/Token",
cache: false,
data: {
"grant_type": "password",
"username": document.getElementById("username").value,
"password": document.getElementById("password").value
},
contentType: "application/x-www-form-urlencoded", // data type sent to server
// dataType: "json", // data type expected from server <- THIS may be a source of the problem
success: getSuccess,
error: getFail
});
} catch (e) {
alert(e);
}
function getSuccess(data, textStatus, jqXHR) {
alert(data.Response);
};
function getFail(jqXHR, textStatus, errorThrown) {
//jqXHR.status is 0
//textStatus is "error"
//errorThrown is empty
alert(jqXHR.status); // <-- THIS is what gets called, instantly after the post.
};
};
Server-side C# code:
public class ApplicationOAuthServerProvider
: OAuthAuthorizationServerProvider
{
public override async Task ValidateClientAuthentication(
OAuthValidateClientAuthenticationContext context)
{
await Task.FromResult(context.Validated()); // break-point was here
}
// This is called by await Task.FromResult(context.Validated())
public override async Task GrantResourceOwnerCredentials(
OAuthGrantResourceOwnerCredentialsContext context)
{
// Here manager will have correct values from the POST-ed data
var manager = context.OwinContext.GetUserManager<ApplicationUserManager>();
var user = await manager.FindAsync(context.UserName, context.Password);
if (user == null)
{
context.SetError(
"invalid_grant", "The user name or password is incorrect.");
context.Rejected();
return;
}
foreach (var userClaim in user.Claims)
{
identity.AddClaim(new Claim(userClaim.ClaimType, userClaim.ClaimValue));
}
context.Validated(identity);
}
}
Seeing what the actual error is that you're getting back will confirm/disprove this, but I've seen CORS issues to blame for this. Depending on how the call is being made and what the server supports, you may have a preflight request that is handled by the server as though it was the full request, and then the client can reject the response because it wasn't cross-origin authorized.
I'm not sure what your server-side framework is, but this may help if indeed this is a cross-origin issue:
CORS with WebAPI for XmlHttpRequest

Post parameter is always null

Since upgrading to RC for WebAPI I'm having some real odd issue when calling POST on my WebAPI.
I've even gone back to the basic version generated on new project. So:
public void Post(string value)
{
}
and calling from Fiddler:
Header:
User-Agent: Fiddler
Host: localhost:60725
Content-Type: application/json
Content-Length: 29
Body:
{
"value": "test"
}
When I debug, the string "value" is never being assigned to. It's just always NULL.
Anyone having this issue?
(I first saw the issue with a more complex type)
The problem is not only bound to ASP.NET MVC 4, the same problem occurs for a fresh ASP.NET MVC 3 project after RC installation
I have been scratching my head over this today.
My solution is to change the [FromBody] to a HttpRequestMessage, essentially moving up the HTTP stack.
In my case I am sending data across the wire which is zipped json which is then base64'd. All this from an android app.
The original signature of my web endpoint looked like this (using [FromBody]) :
My fix for this issue was to revert to using a HttpRequestMessage for the signature of my endpoint.
You can then get access to the post data using this line of code:
This works and allows you access to the raw untouched post data. You don't have to mess around with fiddler putting an = sign at the beginning of your string or changing the content-type.
As an aside, I first tried to following one of the answers above which was to change the content type to: "Content-Type: application/x-www-form-urlencoded". For raw data this is bad advice because it strips out + characters.
So a base64 string that starts like this: "MQ0AAB+LCAAAAAA" ends up like this "MQ0AAB LCAAAAAA"! Not what you want.
Another benefit of using HttpRequestMessage is that you get access to all the http headers from within your endpoint.
Since you have only one parameter, you could try decorating it with the [FromBody] attribute, or change the method to accept a DTO with value as a property, as I suggested here: MVC4 RC WebApi parameter binding
UPDATE: The official ASP.NET site was updated today with an excellent explanation: https://learn.microsoft.com/en-us/aspnet/web-api/overview/advanced/sending-html-form-data-part-1
In a nutshell, when sending a single simple type in the body, send just the value prefixed with an equal sign (=), e.g. body:
=test
I've just had this occur using Fiddler. The problem was that I hadn't specified Content-Type.
Try including a header for Content-Type in your POST request.
Content-Type: application/x-www-form-urlencoded
Alternatively, as per comments below, you may need to include a JSON header
Content-Type: application/json
I've ran into this problem as well, and this is how I solved my problem
webapi code:
public void Post([FromBody] dynamic data)
{
string value = data.value;
/* do stuff */
}
client code:
$.post( "webapi/address", { value: "some value" } );
I was using Postman and I was doing the same mistake.. passing the value as json object instead of string
{
"value": "test"
}
Clearly the above one is wrong when the api parameter is of type string.
So, just pass the string in double quotes in the api body:
"test"
Try creating a class to serve as the data model, then send a JSON object with properties matching the properties of your data model class. (Note: I have tested this and it works with the newest MVC 4 RC 2012 that I just downloaded today).
public HttpResponseMessage Post(ValueModel model)
{
return Request.CreateResponse<string>(HttpStatusCode.OK, "Value Recieved: " + model.Value);
}
public class ValueModel
{
public string Value { get; set; }
}
The below JSON object is sent in HTTP-POST body, content-type is application/json
{ "value": "In MVC4 Beta you could map to simple types like string, but testing with RC 2012 I have only been able to map to DataModels and only JSON (application/json) and url-encoded (application/x-www-form-urlencoded body formats have worked. XML is not working for some reason" }
I believe the reason why you have to create a data model class is because simple values are assumed to be from the url parameters, and a single complex value is assumed to be from the body. They do have the [FromBody] and [FromUrl] attributes, but using [FromBody] string value still did not work for me. Seems like they are still working out a lot of bugs so I'm sure this will change in the future.
Edit:
Got XML to work in the body. The default XML serializer was changed to DataContractSerializer instead of XmlSerializer. Putting the following line in my Global.asax file fixed this issue (reference)
GlobalConfiguration.Configuration.Formatters.XmlFormatter.UseXmlSerializer = true;
After some tries, I think the default behavior is correct and there is nothing to hack.
The only trick is: if your post method argument is string like below, you should send a plain string with double quotes in the body (when using ajax or postman), e.g.,
//send "{\"a\":1}" in body to me, note the outer double quotes
[HttpPost("api1")]
public String PostMethod1([FromBody]string value)
{
return "received " + value; // "received {\"a\":1}"
}
Otherwise if you send a json string in the post body without outer double quotes and escaped inner quotes, then it should be able to be parsed to the model class (the argument type), e.g., {"a":1, "b":2}
public class MyPoco{
public int a;
public int b;
}
//send {"a":1, "b":2} in body to me
[HttpPost("api2")]
public String PostMethod2([FromBody]MyPoco value)
{
return "received " + value.ToString(); //"received your_namespace+MyPoco"
}
I was looking for a solution to this problem for some minutes now, so I'll share my solution.
If you post a model your model needs to have an empty/default constructor, otherwise the model can't be created, obviously.
Be careful while refactoring. ;)
For those who are having the same issue with Swagger or Postman like I did, if you are passing a simple attribute as string in a post, even with the "ContentType" specified, you still going to get a null value.
Passing just:
MyValue
Will get in the controller as null.
But if you pass:
"MyValue"
The value will get right.
The quotes made the difference here. Of course, this is only for Swagger and Postman. For instance, in a Frontend app using Angular this should be resolved by the framework automaticly.
This worked for me:
Create a C# DTO class, with a property for every attribute you want to pass from jQuery/Ajax
public class EntityData
{
public string Attr1 { get; set; }
public string Attr2 { get; set; }
}
Define the web api method:
[HttpPost()]
public JObject AddNewEntity([FromBody] EntityData entityData)
{
Call the web api as such:
var entityData = {
"attr1": "value1",
"attr2": "value2"
};
$.ajax({
type: "POST",
url: "/api/YOURCONTROLLER/addnewentity",
async: true,
cache: false,
data: JSON.stringify(entityData),
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function (response) {
...
}
});
I had the same issue and found that when changing the Content Type to "application/json" did not fix the problem. However "application/json; charset=utf-8" worked.
I had a similar issue where the request object for my Web API method was always null. I noticed that since the controller action name was prefixed with "Get", Web API treated this as a HTTP GET rather than a POST. After renaming the controller action, it now works as intended.
With Angular, I was able to pass data in this format:
data: '=' + JSON.stringify({ u: $scope.usrname1, p: $scope.pwd1 }),
headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8' }
And in Web API Controler:
[HttpPost]
public Hashtable Post([FromBody]string jsonString)
{
IDictionary<string, string> data = JsonConvert.DeserializeObject<IDictionary<string, string>>(jsonString);
string username = data["u"];
string pwd = data["p"];
......
Alternatively, I could also post JSON data like this:
data: { PaintingId: 1, Title: "Animal show", Price: 10.50 }
And, in the controller, accept a class type like this:
[HttpPost]
public string POST(Models.PostModel pm)
{
....
}
Either way works, if you have an established public class in the API then post JSON, otherwise post '=' + JSON.stringify({..: ..., .. : ... })
In my case the problem was that the parameter was a string and not an object, i changed the parameter to be JObject of Newsoft.Json and it works.
If you are sure about your sent JSON then you must trace your API carefully:
Install Microsoft.AspNet.WebApi.Tracing package
Add config.EnableSystemDiagnosticsTracing(); in the WebApiConfig class inside Register method.
Now look at the Debug output and you will probably find an invalid ModelState log entry.
If ModelState is invalid you may find the real cause in its Errors:
No one can even guess such an exception:
Could not load file or assembly 'Newtonsoft.Json, Version=9.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed' or one of its dependencies. The located assembly's manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040)
Met with a similar problem in ASP.NET Core and another possible cause is ASP.NET binding (silent) failure due to various reasons such as sending null to be bound to a not null property:
{
"prop1":1139357,
"prop2":1139356,
"items":[
{"key":"15","someprop":34,"notnullprop":null},
{"key":"16","someprop":34,"notnullprop":null},
{"key":"22","someprop":34,"notnullprop":null}]
}
On such case there is no exception being thrown and the whole model will be null, even if this happens deep inside the object hierarchy.
Adding line
ValueProviderFactories.Factories.Add(new JsonValueProviderFactory());
to the end of function protected void Application_Start() in Global.asax.cs fixed similar problem for me in ASP.NET MVC3.
If you are using a DataContractSerializer for your Xml Formatter or JSON Formatter, you need to get rid of it.
I had this in my WebApiConfig file:
public static void Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
var jsonFormatter = config.Formatters.OfType<JsonMediaTypeFormatter>().First();
jsonFormatter.UseDataContractJsonSerializer = true;
}
Simply I comment out jsonFormatter.UseDataContractJsonSerializer = true;and my input parameter isn't null anymore. Thanks to 'Despertar' for giving me a clue.
For complex types, Web API tries to read the value from the message body, using a media-type formatter.
Please check if you got any [Serializable] attribute decorating your model class.
Remove the attribute to see if it works. This worked for me.
Double check your data types. The dotnet model binder will not convert a float to an integer (and I'm assuming other related concepts). This will cause the entire model to be rejected.
If you have json like this:
{
"shoeSize": 10.5
}
but your c# model looks like this:
class Shoe{
public int shoeSize;
}
the model binder will reject the model and you will get null.
I had the same issue of getting null as parameter, but it was related to large objects. It turned out the problem was related to IIS max length. It can be configured in web.config.
<system.web>
<httpRuntime targetFramework="4.7" maxRequestLength="1073741824" />
</system.web>
I wonder why Web API suppressed the error and sends null objects to my APIs. I found the error using Microsoft.AspNet.WebApi.Tracing.
JSON.stringify(...) solved my issues
I know this is not an answer to this question, but I came across it when searching for a solution to my problem.
In my case, the complex type was not being bound but I was not doing a POST, I was doing a GET with querystring parameters. The solution was to add [FromUri] to the arg:
public class MyController : ApiController
{
public IEnumerable<MyModel> Get([FromUri] MyComplexType input)
{
// input is not null as long as [FromUri] is present in the method arg
}
}
I had the same problem in Fiddler. I already had Content-Type: application/json; charset=utf-8 or Content-Type: application/json in the request header.
My request body was also a plain string, and in Fiddler I had written: {'controller':'ctrl'}. This made the string parameter in my POST method be null.
Fix: remember to use quotation marks, thereby indicating a string. That is, I fixed it by writing "{'controller':'ctrl'}". (Note: when writing JSON, either be sure to use apostrophes or escape the quotation marks like this: "{\"controller\":\"ctrl\"}").
The most simple way I found to deal with simple JSON object that I pass into MVC 6 is getting the the type of the post parameter like NewtonSoft jObject:
public ActionResult Test2([FromBody] jObject str)
{
return Json(new { message = "Test1 Returned: "+ str }); ;
}
The best solution for me is going full HTTP as below:
[Route("api/open")]
[HttpPost]
public async Task<string> open(HttpRequestMessage request)
{
var json = await request.Content.ReadAsStringAsync();
JavaScriptSerializer jss = new JavaScriptSerializer();
WS_OpenSession param = jss.Deserialize<WS_OpenSession>(json);
return param.sessionid;
}
and then deserializing the string to the object you expect in the post body.
For me, WS_OpenSession is a class that contained sessionid, user and key.
You can from there use the param object and access its properties.
Very very effective.
I did say sourced from this url:
http://bizcoder.com/posting-raw-json-to-web-api
I'm a little late to the party, but anyone who stumbles across a NULL value passed when using a controller simply add "=" to the front of your POST request.
The controller also passed a NULL value when I used the application/json Content-Type. Note the "application/x-www-form-urlencoded" Content-Type below. The return type from the API however is "application/json".
public static string HttpPostRequest(string url, Dictionary<string, string> postParameters)
{
string postData = "=";
foreach (string key in postParameters.Keys)
{
postData += HttpUtility.UrlEncode(key) + "="
+ HttpUtility.UrlEncode(postParameters[key]) + ",";
}
HttpWebRequest myHttpWebRequest = (HttpWebRequest)HttpWebRequest.Create(url);
myHttpWebRequest.Method = "POST";
byte[] data = System.Text.Encoding.ASCII.GetBytes(postData);
myHttpWebRequest.ContentType = "application/x-www-form-urlencoded";
myHttpWebRequest.ContentLength = data.Length;
Stream requestStream = myHttpWebRequest.GetRequestStream();
requestStream.Write(data, 0, data.Length);
requestStream.Close();
HttpWebResponse myHttpWebResponse = (HttpWebResponse)myHttpWebRequest.GetResponse();
Stream responseStream = myHttpWebResponse.GetResponseStream();
StreamReader myStreamReader = new StreamReader(responseStream, System.Text.Encoding.Default);
string pageContent = myStreamReader.ReadToEnd();
myStreamReader.Close();
responseStream.Close();
myHttpWebResponse.Close();
return pageContent;
}
it doesn't matter what type of value you wish to post, just enclose it within the quotation marks, to get it as string. Not for complex types.
javascript:
var myData = null, url = 'api/' + 'Named/' + 'NamedMethod';
myData = 7;
$http.post(url, "'" + myData + "'")
.then(function (response) { console.log(response.data); });
myData = "some sentence";
$http.post(url, "'" + myData + "'")
.then(function (response) { console.log(response.data); });
myData = { name: 'person name', age: 21 };
$http.post(url, "'" + JSON.stringify(myData) + "'")
.then(function (response) { console.log(response.data); });
$http.post(url, "'" + angular.toJson(myData) + "'")
.then(function (response) { console.log(response.data); });
c#:
public class NamedController : ApiController
{
[HttpPost]
public int NamedMethod([FromBody] string value)
{
return value == null ? 1 : 0;
}
}
If you put [FromBody] annotation and you have a Dto object as a parameter to your method and still not able to get the data through, start looking into the properties and fields of your DTO.
I had this same problem, where my DTO was coming null. I found the reason was that one of the properties was pointing into an object that cannot be serialised :( which causes the media-formatter to fail to parse the data. Thus the object was always null.
Hope it helps others too
I am pretty late to this but was having similar issues and after a day of going through a lot of the answers here and getting background I have found the easiest/lightweight solution to pass back one or more parameters to a Web API 2 Action is as follows:
This assumes that you know how to setup a Web API controller/action with correct routing, if not refer to: https://learn.microsoft.com/en-us/aspnet/web-api/overview/getting-started-with-aspnet-web-api/tutorial-your-first-web-api.
First the Controller Action, this solution also requires the Newtonsoft.Json library.
[HttpPost]
public string PostProcessData([FromBody]string parameters) {
if (!String.IsNullOrEmpty(parameters)) {
JObject json = JObject.Parse(parameters);
// Code logic below
// Can access params via json["paramName"].ToString();
}
return "";
}
Client Side using jQuery
var dataToSend = JSON.stringify({ param1: "value1", param2: "value2"...});
$.post('/Web_API_URI', { '': dataToSend }).done(function (data) {
console.debug(data); // returned data from Web API
});
The key issue I found was making sure you only send a single overall parameter back to the Web API and make sure it has no name just the value { '': dataToSend }otherwise your value will be null on the server side.
With this you can send one or many parameters to the Web API in a JSON structure and you don't need to declare any extra objects server side to handle complex data. The JObject also allows you to dynamically iterate over all parameters passed in allowing easier scalability should your parameters change over time. Hopefully that helps someone out that was struggling like me.

Categories