Search code examples
c#asp.net-corepostfrombodyattribute

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


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.


Solution

  • 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