0

I have a JQuery function that works ok but if I enable [AntiForgerToken] on the Action Method the JQuery function can't access the Action Method, on the view I have other snippet where I enabled AntiForgeryToken:

@using (Html.BeginForm("InsertStudent","Students",FormMethod.Post, new { @id="myform"}))
{
    @Html.AntiForgeryToken()

It doesn't matter if the @Html.AntiForgeryToken() inside the view is enabled or not, the JQuery function works good, the one with the problem is at the Action Method...

Why is happening that? What I'm missing?? I've read is very important for security to have [AntiForgeryToken] enabled on the Post Action Methods so I think that the application should work with it enabled in both places the Action Method and the View.

JQuery function:

function InsertShowStudents() {
    var counter = 0;
    $.ajax({        
        type:"post",
        url: "/Students/InsertStudent/",
        data: { Name: $("#Name").val(), LastName: $("#LastName").val(), Age: $("#Age").val() }
    }).done(function (result) {
        if (counter==0) {
        GetStudents();
        CounterStudents();
            counter = 1;
        }
        else {
            $("#tableJQuery").append("<tr>"+"<td>"+result.Name+"</td>"+"<td>"+result.LastName+"</td>"+"<td>"+result.Age+"</td>"+"</tr>")
        }
        //clear the form
       $("#myform")[0].reset();
    }).error(function () {
        $("#divGetStudents").html("An error occurred")
    })
}

Action method:

 [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult InsertStudent(Student student)
        {
            if (ModelState.IsValid)
            {
                db.Students.Add(student);
                db.SaveChanges();
                //ModelState.Clear();
                return RedirectToAction("InsertStudent");
            }
            return View(student);
        }

columns of the table:

@foreach (var item in Model) {
    <tr>
        <td>
            @Html.DisplayFor(modelItem => item.Name)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.LastName)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.Age)
        </td>
      @* <td style="display:none" class="tdStudentID">@Html.DisplayFor(modelItem => item.StudentID)</td>    *@  
        <td>
            <img src="~/images/deleteIcon.png" width="20" height="20" class="imgJQuery" data-id="@item.StudentID" />
        </td>
       <td>
           @Html.ActionLink("Details","Details", new { id=item.StudentID})
       </td>
    </tr>
}
AlexGH
  • 2,735
  • 5
  • 43
  • 76
  • 1
    Your not passing the value of the token in the ajax data. Easiest if you just use `data: $('#myform').serialize(),` which will serialize all form controls including the token. But having `RedirectToAction()` in your POST method is pointless - ajax calls never redirect. –  Jul 11 '16 at 04:37
  • @StephenMuecke it works now :), are you going to post the answer so I can give you the point or do you prefer if I do that? – AlexGH Jul 11 '16 at 04:44
  • Give me 20 min and I'll add this and another alternative as well –  Jul 11 '16 at 04:47
  • 1
    Similar ways: http://stackoverflow.com/questions/14473597/include-antiforgerytoken-in-ajax-post-asp-net-mvc. The verification token can be obtained using `var token = $('#myform input[name=__RequestVerificationToken]').val();` and pass it into serialized data part. – Tetsuya Yamamoto Jul 11 '16 at 04:49
  • @StephenMuecke ok, I will change the Action Method and delete the `RedirectToAction()` then, didn't think that ajax never redirect – AlexGH Jul 11 '16 at 04:50
  • @TetsuyaYamamoto ok that's other option, good to know – AlexGH Jul 11 '16 at 04:51
  • I'll put the details of what you method should be in the answer - what your doing is awful performance as I noted in one of your previous questions :) –  Jul 11 '16 at 04:53
  • @StephenMuecke great!!! :) – AlexGH Jul 11 '16 at 04:55
  • @StephenMuecke I did everything like you told me except to add a row to the table with the data contained in the fields see the comments below the answer, I've added the table code to the question so it's easier to understand for you what I mean, I'm not sure if I can add that row using ajax taking in count the elements of the table thxs :) – AlexGH Jul 11 '16 at 18:07

1 Answers1

1

You not passing the value of the token in your ajax call so an exception is thrown. You can get the value of the token using

var token = $('[name=__RequestVerificationToken]').val();

and modify your ajax call to include it using

data: { __RequestVerificationToken: token, Name: $("#Name").val(), LastName: $("#LastName").val(), Age: $("#Age").val() }

however, it is easier to just serialize your form which will include the token

$.ajax({        
    type:"post",
    url: '@Url.Action("InsertStudent", "Students")', // don't hardcode your url's
    data: $('#myform').serialize(),
}).done(function (result) {

Side note: Ajax calls never redirect (the whole point of ajax is to stay on the same page) so having return RedirectToAction("InsertStudent"); in your InsertStudent() will not work. In addition, your returning html, so the $("#tableJQuery").append() code in the .done() callback will fail.

It appears you have a form to add a new Student so your method simply need to return a JsonResult indicating success or otherwise, and if successful, then you can add a new row to your table based on the values in the form, for example

}).done(function (result) {
    if (result) {
        var row = $('<tr></tr>');
        row.append($('<td></td>').text($("#Name").val()));
        ... // add other cells
        $("#tableJQuery").append(row);
        //clear the form
        $("#myform")[0].reset();
    } else {
        // Oops something went wrong
    }
})
  • very nice, I'm using an external js file so I declared a hidden input text in my View with the information of the Url.Action and I referenced it from ajax in the external js file, that works good. I modified too the Action Method and I'm returning Json on success or not. – AlexGH Jul 11 '16 at 17:52
  • The only thing is that I added in my table a new column with an img element so when click on that img the element will be deleted(implemented with ajax), I added too other column called Details with details of the student, that calls the Details Action Method, so I'm not sure if I can add using ajax a row with the data, that img with the functionality and the Details hyperlink, do you suggest me to call the GetStudents() function directly? – AlexGH Jul 11 '16 at 18:01
  • 1
    Rather than a hidden input, you should use `data-*` attributes. For example, in a button that triggers the script, you could use add `data-url="@Url.Action(....)"` and then retrieve it using `var url = $(this).data('url');`. But in your case you already have it in the `
    ` tag your generating with `Html.BeginForm()` so you can just use `url: $('#myform').attr('action');`
    –  Jul 11 '16 at 23:16
  • 1
    You can easily create the html with a new row including the image and link. The `InsertStudent` method just needs to return json including the new `ID` of the `Student` you inserted (e.g. `return Json(new { id = student.StudentID });`) and then in the ajax - `row.append($('').append($('').attr('scr', '....').data('id', response.id)));`) - there is no reason to degrade your app by making multiple calls to the server and regenerating the whole table all over again –  Jul 11 '16 at 23:25
  • 1
    Another alternative is to return a partial view of the new `Student` which is just a single `` element of that `Student` and then append that to the existing table. –  Jul 11 '16 at 23:27