Ajax POST works with [FromForm] but not with [FromBody]

2.2k Views Asked by At

In my View I pass BookViewModel and I have a form which I am trying to create a Book with. I can perform this if I use the [FromForm] attribute but I am trying to understand how that differs from [FromBody]. Here are my models:

public class Book
{
    public int Id { get; set; }
    public string Title { get; set; }
    public int GenreId { get; set; }    //foreign key from Genre

    public virtual Genre Genre { get; set; }
}

public class BookViewModel
{
    public ICollection<Book> Books { get; set; }
    public Book Book { get; set; }
}

Here is my View and Controller:

[HttpGet]
public IActionResult Index()
{
    BookViewModel books = new BookViewModel
    {
        Books = _context.Books.Include(x => x.Genre).ToList()
    };  // _context.Books is my database context and table

    return View();
}

[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Index([FromForm] BookViewModel b)  //works without
{                                                       //[FromForm] too
    //code to perform tasks
    //etc
    //return Json(true);      The parameters work fine but I would like [FromBody]
}

View:

<form id="formid" method="post">
    <div asp-validation-summary="ModelOnly"></div>

    <label>Title</label>
    <input asp-for="Book.Title" class="form-control" />
    <span asp-validation-for="Book.Title"></span>

    <label>Genre</label>
    <input asp-for="Book.GenreId" class="form-control" />
    <span asp-validation-for="Book.GenreId"></span>

    <button type="button" onclick="SaveData()">Submit</button>
</form>

<script>
    function SaveData(){
        event.preventDefault();
        var formData = $("#formid").serialize();

        $.ajax({
            url: "/",
            type: "POST",
            data: formData,
            beforeSend: function(request){
                  request.setRequestHeader(
                  "RequestVerificationToken", 
                       $("[name=__RequestVerificationToken']").val());
            }
         });
     }
</script>

All of this code works fine but I would like to use [FromBody] instead.

I have added contentType: "application/json" to my ajax, and added [FromBody] to my action method, but when I submit my form, BookViewModel is null in the action method. I would like to know how I could submit my form with [FromBody] with contentType: "application/json" instead of application/x-www-url-formencoded?

[FromBody] will perform default model binding which is what I want here. I am assuming that the reason [FromBody] has not worked is because of the way I am serializing the form but I am not sure.

1

There are 1 best solutions below

0
On BEST ANSWER

The difference between [FromForm] and [FromBody] as below:

[FromForm] : Gets values from posted form fields.
[FromBody] : Gets values from the request body.

To use the [FromBody] attribute, you could get the entered value via JQuery, then create JavaScript object, and send it to controller method, code as below:

<script>
    function SaveData() {
        event.preventDefault();
        //var formData = $("#formid").serialize();

        var bookvm = {};
        var book = {};
        book.Title = "hello"; //use jquery to get the entered value 
        book.GenreId = "1001";
        var genre = {};
        genre.Id = 1001;
        genre.Name = "Type A";
        book.Genre = genre;
        bookvm.Book = book;

        $.ajax({
            url: "/Home/BookIndex",  //change the url to yours
            type: "POST",
            data: JSON.stringify(bookvm),
            contentType: "application/json; charset=utf-8",
            dataType: "json",
            beforeSend: function (request) {
                request.setRequestHeader(
                    "RequestVerificationToken",
                    $("[name='__RequestVerificationToken']").val());
            }
        });
    }
</script>

The result as below:

enter image description here