So, I've been banging my head against the wall with this, and I can't find any good sources for this. Maybe I'm forgetting how the model binding stuff works in MVC3, but here's what I'm trying to do: I have some an editor bound up with Knockout to handle editing of a model. There's not much to the model:
public class SetupTemplate
{
public int Id { get; set; }
public string Name { get; set; }
public string Template { get; set; }
}
The signature of the action i'm trying to call is:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult UpdateTemplate(SetupTemplate template)
From another question on here, I picked up this rather helpful snippet to get the anti-forgery token:
window.addAntiForgeryToken = function(data) {
data.__RequestVerificationToken = $('#__AjaxAntiForgeryForm input[name=__RequestVerificationToken]').val();
return data;
};
Which all comes together with me trying to post an update via ajax:
payload = window.addAntiForgeryToken(ko.mapping.toJS(self.data));
$.ajax({
type: "post",
url: endpoint,
data: payload,
success: function(data) {
//Handle success
}});
Which results in this in the form data section of the Chrome developer tools
Id:1
Name:Greeting
Template: [Template Text]
__RequestVerificationToken: [The really long anti-forgery token]
The antiforgery token is picked up, but my model is null. Most examples I've seen of this just use a single parameter passed along, and not a model.
I'm sure I'm missing something obvious, any insights on what it could be?
EDIT: In response to @Mark, changing the call to this:
$.ajax({
type: "post",
dataType: "json",
contentType: 'application/json',
url: endpoint,
data: JSON.stringify(payload),
success: function(data) {
//Do some stuff
}});
Results in a request payload of this:
{"Id":1,"Name":"Greeting","Template":"...","__RequestVerificationToken":"..."}:
And the server not picking up the anti-forgery token. This was tried both with and without the contentType
parameters to $.ajax()
.
The mapping did not work with the parameter as template because it clashes with one of the property that has the same name (bearing case). If you use anything other than template it will work well for that controller parameter.
There is a so link explaining the details, I'm not able to find that now easily.
public class SetupTemplate
{
public int Id { get; set; }
public string Name { get; set; }
public string Template { get; set; }
}
Here is my solution. Define a jQuery function like this:
(function ($) {
$.getAntiForgeryToken = function () {
return $('input[name="__RequestVerificationToken"]').val();
};
// (!) use ValidateJsonAntiForgeryToken attribute in your controller
$.ajaxJsonAntiforgery = function (settings) {
var headers = {};
headers['__RequestVerificationToken'] = $.getAntiForgeryToken();
settings.dataType = 'json';
settings.contentType = 'application/json; charset=utf-8';
settings.type = 'POST';
settings.cache = false;
settings.headers = headers;
return $.ajax(settings);
};
})(jQuery);
It just puts your verification token to headers. You also need filter attribute to check your antiforgery token. Here it is:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Helpers;
using System.Web.Mvc;
namespace MyProject.Web.Infrastructure.Filters
{
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class,
AllowMultiple = false, Inherited = true)]
public sealed class ValidateJsonAntiForgeryTokenAttribute
: FilterAttribute, IAuthorizationFilter
{
public void OnAuthorization(AuthorizationContext filterContext)
{
if (filterContext == null)
{
throw new ArgumentNullException("filterContext");
}
var httpContext = filterContext.HttpContext;
var cookie = httpContext.Request.Cookies[AntiForgeryConfig.CookieName];
AntiForgery.Validate(cookie != null ? cookie.Value : null,
httpContext.Request.Headers["__RequestVerificationToken"]);
}
}
}
In your controller it's really easy, just mark it with new attribute (ValidateJsonAntiForgeryToken):
[Authorize, HttpPost, ValidateJsonAntiForgeryToken]
public ActionResult Index(MyViewModel viewModel)
And on the client side:
$.ajaxJsonAntiforgery({
data: dataToSave,
success: function() { alert("success"); },
error: function () { alert("error"); }
});
It works for me. Enjoy!
Can you try using JSON.stringify
?
$.ajax({
type: "post",
url: endpoint,
data: JSON.stringify(payload),
success: function(data) {
//Handle success
}
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With