Publishing a JSON Model on ASP.Net MVC3 with Anti-Counterfeit Token

So, I hit my head against the wall with this, and I cannot find good sources for this. I may be forgetting how the model binding file works in MVC3, but here's what I'm trying to do: I have an editor associated with Knockout to handle model editing. There are not many in 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 trigger:

 [HttpPost] [ValidateAntiForgeryToken] public ActionResult UpdateTemplate(SetupTemplate template) 

From another question here, I took this pretty useful snippet to get the anti-fake token:

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

What happens to me when I try 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 }}); 

As a result, in the data section of the Chrome Developer Tools form

 Id:1 Name:Greeting Template: [Template Text] __RequestVerificationToken: [The really long anti-forgery token] 

The anti-corrosion token is confirmed, but my model is zero. Most of the examples I've seen just use only one parameter, not a model.

I am sure that I am missing something obvious, any understanding of 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 the request payload:

 {"Id":1,"Name":"Greeting","Template":"...","__RequestVerificationToken":"..."}: 

And the server does not pick up the anti-fake token. This has been tested both with and without contentType parameters up to $.ajax() .

+7
source share
5 answers

@Mark gets credit for taking me on the right path for this and pointing me to some links that now allow me to handle the anti-fake token quite transparently. However, the solution to the problem has changed:

 public ActionResult UpdateTemplate(SetupTemplate template) 

in

 public ActionResult UpdateTemplate(SetupTemplate model) 

And now it fills in the values ​​correctly. I would really like to know why this is fixed, but at the moment it works.

+1
source

The mapping did not work with the parameter as a template, because it encounters one of the properties that have the same name (case). If you use anything other than a template, it will work well for this controller parameter.

There is so a link explaining the details, I can not find it now easily.

 public class SetupTemplate { public int Id { get; set; } public string Name { get; set; } public string Template { get; set; } } 
+2
source

Here is my solution. Define the jQuery function as follows:

 (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 validation token in the headers. You also need a filter attribute to check the antiforgery token. Here he 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"]); } } } 

This is very simple in your controller, just mark it with a 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"); } }); 

This works for me. Enjoy it!

+2
source

Can you try using JSON.stringify ?

 $.ajax({ type: "post", url: endpoint, data: JSON.stringify(payload), success: function(data) { //Handle success } }); 
+1
source

Actually for me it worked with a complex object:

 var application = { Criteria: { ReferenceNumber: $("input[name='Criteria.ReferenceNumber'").val() }, AppliedVia: "Office" }; // get the next step $.ajax({ url: form.attr("action"), dataType: "html", type: "POST", data: { __RequestVerificationToken: $("input[name=__RequestVerificationToken]").val(), application: application }, } 

However, it should be noted that the left application in data should be the actual parameter name in your method / action. This works with MVC 5 (pre.NET Core)

0
source

All Articles