简体   繁体   English

使用防伪令牌将JSON模型发布到ASP.Net MVC3

[英]Posting a JSON model to ASP.Net MVC3 with Anti-forgery token

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. 也许我忘记了模型绑定东西在MVC3中是如何工作的,但这是我想要做的:我有一些与Knockout绑定在一起的编辑器来处理模型的编辑。 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: 这一切与我一起尝试通过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 这导致了Chrome开发者工具的表单数据部分

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. 提取了防伪令牌,但是我的模型为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: 编辑:响应@Mark,更改对此的调用:

$.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() . 尝试使用$.ajax()contentType参数是否有此方法。

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: 定义一个jQuery函数,如下所示:

(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): 在您的控制器中,这非常简单,只需用新属性(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 ? 您可以尝试使用JSON.stringify吗?

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

@Mark gets credit for leading me down the right path for this, and pointing me at some links that now let me handle the anti forgery token rather transparently. @Mark因引导我走上正确的道路而获得称赞,并指出了一些链接,这些链接现在使我可以相当透明地处理防伪令牌。 However, what solved the issue was changing: 但是,解决问题的方法正在改变:

public ActionResult UpdateTemplate(SetupTemplate template)

to: 至:

public ActionResult UpdateTemplate(SetupTemplate model)

And now it's properly filling in the values. 现在,它可以正确地填充值。 I'd really like to know why that fixed it, but for now, it works. 我真的很想知道为什么可以解决这个问题,但是现在,它可以工作了。

Actually the following worked for me 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
    },
}

One thing of note however is to make sure that the left application in data should be the actual parameter name in your method/action. 但是,需要注意的一件事是要确保data剩余的application应该是您的方法/操作中的实际参数名称。 This works as of MVC 5 (pre .NET Core) 从MVC 5(.NET Core之前的版本)开始运行

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM