22

I've been looking at examples of how to do this on SO and as far as I can tell I've tried all the examples I can find with no success so far. I've tried altering some of the implementations to my scenario but this has thus far failed as well.

I have this on my page in _layout.cshtml so I always have a token available:

<form id="__AjaxAntiForgeryForm" action="#" method="post"> @Html.AntiForgeryToken()</form>

I also have this method in my JavaScript utils file:

AddAntiForgeryToken = function (data) {
    data.__RequestVerificationToken = $('#__AjaxAntiForgeryForm input[name=__RequestVerificationToken]').val();
    return data;
};

This is all working as expected and I get an anti forgery token. My actual posting code is:

myPage.saveData = function() {
    var saveUrl = '/exercises/PostData';

    var myData = JSON.stringify(myPage.contextsArrays);

    $.ajax({
        type: 'POST',
        async: false,
        url: saveUrl,
        data: AddAntiForgeryToken({ myResults: myData }),
        success: function () {
            alert('saved');
        },
        dataType: 'json',
        contentType: "application/json; charset=utf-8"
    });
};

My action method looks like this:

    [HttpPost, ValidateAntiForgeryToken, JsonExceptionFilter]
    public JsonResult PostData(List<ResultsDc> myResults)
    {
        return Json(_apiClient.SubmitResults(myResults));
    }

I've been testing this with the various implementations I've been trying and the response is always:

{"errorMessage":"The required anti-forgery form field \"__RequestVerificationToken\" is not present."}

I'm not posting a form it's just an array of data but checking the data that actually gets posted the Json doesn't look right (it's all encoded) but the __RequestVerificationToken parameter name is there and the token value is also present.

I'm pretty confused by all this at the moment and cannot find the correct way to send the token so that my MVC action is invoked. If I remove the ValidateAntiForgeryToken attribute and have JSON.stringify(myPage.contextsArrays); as the data the json looks correct (unencoded) and it maps fine.

How do I get this token posted properly without a form?

CodingIntrigue
  • 75,930
  • 30
  • 170
  • 176
Jammer
  • 9,969
  • 11
  • 68
  • 115
  • 1
    I'm confused. If you've not seen this and your answer below is someone elses (I have seen that code on other answers on SO) why are you trying to answer something you don't understand?? – Jammer Jul 25 '13 at 13:07
  • There is no need to separate attributes ... – Jammer Jul 25 '13 at 13:11

3 Answers3

35

Cardboard developer strikes again.

All I had to do was remove:

contentType: "application/json; charset=utf-8"

And by doing that (which changes the kind of post request being made), to get the Json content of the actual data property to bind correctly to your T model type DO NOT JSON.stringify() the data.

Jammer
  • 9,969
  • 11
  • 68
  • 115
  • Life saver... thanks! this comment was all I needed after 2 days searching.. Much obliged. – Beau D'Amore Apr 30 '15 at 14:06
  • Superb! - followed a lot of solutions but to no avail, this however turned out to the culprit, many thanks! – Dalbir Singh Feb 20 '17 at 15:07
  • Thank you! Waste hours on this!!! Read article after article and just didnt get anywhere. Tried so many different things but all pretty much providing the same end result which was to include the verification token when posting and could not get this to work until I found your suggestion by fluke!! Still can't believe it! Thank you for sharing!! – Thierry Mar 16 '17 at 02:02
  • why it can't use `contentType: "application/json; charset=utf-8"`? In which situation, we can use `contentType: "application/json; charset=utf-8"` and we should stringify the data? – Willy Jun 23 '17 at 10:39
  • 1
    It appears to need contentType: "application/x-www-form-urlencoded" to find the 'form-field' token – Lance Oct 02 '17 at 03:36
17

(NOTE: I know this isn't all that much different than what is already here)

I realize this has already been answered, but I've done this quite a bit and will add my answer to the pile. I have no Forms in my site, so I had to come up with ways to handle this. A lot of research here on stackoverflow and some blogs finally gave me the info I needed.

First, here's my code at the top of my "master" page (MVC Razor):

@using (Html.BeginForm(null, null, FormMethod.Post, new { id = "__AjaxAntiForgeryForm" }))
{
    @Html.AntiForgeryToken()
}

Then, in all ajax calls (jQuery), I start like this:

var token = $("#__AjaxAntiForgeryForm input").val();
var dataObject = {
  __RequestVerificationToken: token,
  myData: "whatever"
};

$.ajax({
  url: '@Url.Action("action", "controller")',
  type: 'POST',
  dataType: 'json',
  data: dataObject,
  error: function (jqXHR, textStatus, errorThrown) {
    console.log("ERROR!");
  },
  success: function (data) {
    //whatever
  }
});

Finally, in the Controller side of things, this works fine:

public class controllerController : Controller
{
    [HttpPost]
    [ValidateAntiForgeryToken]
    public JsonResult action (myModel myData)
    {
        return Json(new { Success = "true" }, JsonRequestBehavior.DenyGet);
    }
}

Hopefully this would help others in the same boat.

ESDictor
  • 745
  • 2
  • 9
  • 22
1

The validation is hardcoded to look at form data.

https://aspnetwebstack.codeplex.com/SourceControl/latest#src/System.Web.WebPages/Helpers/AntiXsrf/AntiForgeryTokenStore.cs

    public AntiForgeryToken GetFormToken(HttpContextBase httpContext)
    {
        string value = httpContext.Request.Form[_config.FormFieldName];

You can simulate a form submission by following a certain format. For that, see How to mimic an HTML form submission in a POST request?

If you want, you can also make your own validator. How to POST JSON data to an Asp.Net MVC 2 app with ValidateAntiForgeryToken explains how to do this. It is for MVC 2 but you can pick up some ideas from it.

Community
  • 1
  • 1
user247702
  • 23,641
  • 15
  • 110
  • 157
  • Sure, the custom validator could be seen as overkill. I just explain the cause of the issue and two possible solutions. The answer you posted is is pretty much the end result of the info in my first link. – user247702 Jul 25 '13 at 12:53