Values are null when POSTing to Razor Handler via JavaScript fetch()

Viewed 456

I'm trying to POST email content to a Razor Handler, but the request values don't seem to be properly binding. I've been scouring all I can (including many questions on SO) for answers without luck.

The code sends the data properly as best I can tell from the Network tab. My breakpoint in the handler is hit, but the request has null values. (I'm running .NET Core 3.0, for what it's worth.)

I've tried:

  • adding Content-Type to the fetch headers
  • adding the [FromBody] attribute to the request parameter
  • sending the body as an object instead of serializing it
  • changing List<int> to int[]
  • changing my request class properties to be lowercase, camelcase, etc, just incase there was a different ContractResolver in my default SerializerSettings

My code and some screenshots are below. Any suggestions/answers appreciated.

JS

//sample data
const To = "stack@overflow.com";
const Body = "This is the email body.";
const Subject = "Hello world!";
const PortraitId = 17;
const FileIds = [1,2,3];
const body = { PortraitId, To, Subject, Body, FileIds };

const handler = "SendEmail";
const url = new URL(window.location.href.split('?')[0]);
const parameters = { handler };
url.search = new URLSearchParams(parameters).toString();

await fetch(url, {
  method: "POST",
  body: JSON.stringify(body),
  headers: {
    "RequestVerificationToken": $('input[name="__RequestVerificationToken"]').val()
  }
});

C#

public class SendEmailRequest
{
  public int PortraitId { get; set; }
  public string To { get; set; }
  public string Subject { get; set; }
  public string Body { get; set; }
  public List<int> FileIds { get; set; }
}
public async Task<IActionResult> OnPostSendEmail(SendEmailRequest request) {
  //do stuff
}

enter image description here

enter image description here

enter image description here

3 Answers

Ok, so I replicated that, added both [FromBody] and "Content-Type": "application/json"

([FromBody] SendEmailRequest request)

and

headers: {
   "RequestVerificationToken": $('input[name="__RequestVerificationToken"]').val(),
   "Content-Type": "application/json"
}

And this started working properly. So if with these fixes it won't work, please to share all your Middlewares (or Authentication providers).

enter image description here

So, model is being sent as null and the values from ajax end are being passed fine. I see, to initialize your object model you need to specify from where should the object mapper map the values. In your case, you need to specify [FromBody] explicitly along with your object in the parameters so it can be sure that the values are going to come from the body of the ajax call. Like:

public async Task<IActionResult> OnPostSendEmail([FromBody] SendEmailRequest request)
{
    //do stuff
}

This will tell the object mapper to map the object of type SendEmailRequest from the json body that is being sent in the POST call and it will go straight to the body and pick the relevant fields and initialize the SendEmailRequest request object with the provided values.

Since your browser is already taking your request body as JSON but I would like you to explicitly tell the ajax call that the data you are sending is of type json.

await fetch(url, {
  method: "POST",
  body: JSON.stringify(body),
  headers: {
    "Content-Type": "application/json; charset = utf-8;", // By Adding this header.
    "RequestVerificationToken": $('input[name="__RequestVerificationToken"]').val()
  }
});

As I see, you already have tried the changes suggested above, so I would like to take you back to traditional ajax call. Let me know if this works for you?

$.ajax({
    method: 'POST',
    contentType: 'application/json; charset=utf-8;',
    dataType: 'json',
    data: JSON.stringify(body),
    success: function(response){
        console.log(response);
    },
    fail: function(err){
        console.log(err);
    }
});

Your code looks about right. Try to create the object in javascript as follows:

{
  portraitId: 1,
  to: "string",
  subject: "string"
  body: "string"
  fileIds: [1,2]
}

Notice that keys are camelCase instead of PascalCase. Also be aware that this kind of "null objects" errors are almost always related to how you send the request to the backend.

I.E: If you send this object to your back end server:

{
  portraitId: "string", //notice that I change the data type expected from number to string
  to: "string",
  subject: "string"
  body: "string"
  fileIds: [1,2]
}

the whole model binding breaks, and you get null values in your properties. Its like the controller says, "hey I accept only THIS model, and no other else. I wont compromise". Wich is good because we want to be sure that our data maintain some kind of consistency

Related