【问题标题】:MVC 5 Multiple Anti Forgery tokens AJAXMVC 5 多个防伪令牌 AJAX
【发布时间】:2016-10-25 18:36:34
【问题描述】:

我的问题是我有一个部分视图,其中包含一个充满下拉列表的表单。我正在使用 AJAX 动态更新下拉内容。在我实施防伪令牌之前一切正常。在 ajax 帖子中序列化我的表单后,我将在我的_LoginPartial.cshtml 表单中获取防伪令牌,并在我从_config.cshtml 发布数据的表单中获取防伪令牌。这会导致验证属性使帖子无法正常工作并阻止 ajax 工作。

我尝试了以下方法:

  1. 删除所有标记并仅在 _Layout.cshtml 页面中放置一个标记并在 ajax 调用中引用该标记。这适用于 Ajax,但在提交表单时,由于令牌不在表单中,所以我收到未找到令牌的错误。

  2. 然后我也尝试使用 AJAX 发布表单,并在 _Layout.cshtml 视图中引用单个令牌。这会将数据返回到 ajax 而不是我希望的整个视图。

我的问题是:

  1. 如何仅将当前表单令牌放入 ajax post 函数中?
  2. 或者,有没有正确的方法来做我想做的事情?

我很确定有一种简单的方法可以解决这个问题,但我真的只是一个初学者,我正在努力寻找自己的脚。任何帮助,将不胜感激。

_LoginPartial.cshtml

@using Microsoft.AspNet.Identity
@using (Html.BeginForm("LogOff", "Account", FormMethod.Post, new { id = "logoutForm", @class = "navbar-right" }))
{

    if (Request.IsAuthenticated)
    {
        <ul class="nav navbar-nav navbar-right">
            <li>
                @Html.ActionLink("Hello " + User.Identity.GetUserName() + "!", "Index", "Manage", routeValues: null, htmlAttributes: new { title = "Manage" })
            </li>
            @if (User.IsInRole("admin"))
            { 
                <li>@Html.ActionLink("Register", "Register", "Account", routeValues: null, htmlAttributes: new { id = "registerLink" })</li>
            }
            <li><a href="javascript:document.getElementById('logoutForm').submit()">Log off</a></li>
        </ul>
    }
    else
    {
        <ul class="nav navbar-nav navbar-right">
            @if (User.IsInRole("admin"))
            {
                <li>@Html.ActionLink("Register", "Register", "Account", routeValues: null, htmlAttributes: new { id = "registerLink" })</li>
            }
            <li>@Html.ActionLink("Log in", "Login", "Account", routeValues: null, htmlAttributes: new { id = "loginLink" })</li>
        </ul>
    }

_Layout.cshtml

 <!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>@ViewBag.Title - Doogies Website</title>
    @Styles.Render("~/Content/css")
    @Styles.Render("~/Content/themes/base/css")
    @Scripts.Render("~/bundles/modernizr")
    @Scripts.Render("~/bundles/jquery")
    @Scripts.Render("~/bundles/jqueryui")
    @Scripts.Render("~/bundles/jqueryval")
</head>
<body>

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

    <div class="navbar navbar-inverse navbar-fixed-top">
        <div class="container">
            <div class="navbar-header">
                <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                </button>
                @Html.ActionLink("Doogies Site", "Index", "Home", new { area = "" }, new { @class = "navbar-brand" })
            </div>
            <div class="navbar-collapse collapse">
                <ul class="nav navbar-nav">
                    <li>@Html.ActionLink("Home", "Index", "Home")</li>
                    <li>@Html.ActionLink("About", "About", "Home")</li>
                    <li>@Html.ActionLink("Contact", "Contact", "Home")</li>
                    <li>@Html.ActionLink("RTCG", "RTCG_Configurator", "Rtcg")</li>
                </ul>
                @Html.Partial("_LoginPartial")
            </div>
        </div>
    </div>

    <div class="container body-content" >
        @RenderBody()
        <hr />
        <footer>
            <p>&copy; @DateTime.Now.Year - Chris Duguid</p>
        </footer>
    </div>


    @Scripts.Render("~/bundles/bootstrap")
    @RenderSection("scripts", required: false)
</body>
</html>

_config.cshtml(表单所在的部分视图)

是的,我知道该表单没有提交信息。我正在尝试使用 ajax 提交。

@model HomeWeb.Models.RtcgConfigurationModel

@using (Html.BeginForm( new { enctype = "multipart/form-data" , id = "configform"}))
{
    <div class="form-horizontal">
        <h3>Configure RTCG</h3>
        <hr />
        @Html.ValidationSummary(true)


        <div class="form-group">
            @Html.LabelFor(model => model.RtcgCabinetType, "Cabinet Type", new { @class = "control-label col-md-5" })
            <div class="col-md-7">
                @Html.DropDownListFor(model => Model.RtcgCabinetType, Model.Cabinets, new { onchange = "changed();" })
                @Html.ValidationMessageFor(model => model.RtcgCabinetType)
            </div>
        </div>
        <div class="form-group">
            @Html.LabelFor(model => model.RtcgAdaptorType, "Adaptor Type", new { @class = "control-label col-md-5" })
            <div class="col-md-7">
                @Html.DropDownListFor(model => Model.RtcgAdaptorType, Model.Adaptors, new { onchange = "changed();" })
                @Html.ValidationMessageFor(model => model.RtcgAdaptorType)
            </div>
        </div>
        <div class="form-group">
            @Html.LabelFor(model => model.RtcgQtyAdaptors, "Adaptor Qty.", new { @class = "control-label col-md-5" })
            <div class="col-md-7">
                @Html.DropDownListFor(model => Model.RtcgQtyAdaptors, Model.AdaptorQtys, new { onchange = "changed();" })
                @Html.ValidationMessageFor(model => model.RtcgQtyAdaptors)
            </div>
        </div>

        <div class="form-group">
            @Html.LabelFor(model => model.RtcgTerminationMethod, "Termination", new { @class = "control-label col-md-5" })
            <div class="col-md-7">
                @Html.DropDownListFor(model => Model.RtcgTerminationMethod, Model.TerminationMethods)
                @Html.ValidationMessageFor(model => model.RtcgTerminationMethod)
            </div>
        </div>
        <div class="form-group">
            @Html.LabelFor(model => model.RtcgFaceplateStyle, "Faceplate Style", new { @class = "control-label col-md-5" })
            <div class="col-md-7">
                @Html.DropDownListFor(model => Model.RtcgFaceplateStyle, Model.FacePlateStyles)
                @Html.ValidationMessageFor(model => model.RtcgFaceplateStyle)
            </div>
        </div>
        <div class="form-group">
            @Html.LabelFor(model => model.RtcgScreenPrinting, "Screenprinting", new { @class = "control-label col-md-5" })
            <div class="col-md-7">
                @Html.DropDownListFor(model => Model.RtcgScreenPrinting, Model.ScreenPrintOptions)
                @Html.ValidationMessageFor(model => model.RtcgScreenPrinting)
            </div>
        </div>
        <div class="form-group">
            @Html.LabelFor(model => model.RtcgApplication, "Fibre Type", new { @class = "control-label col-md-5" })
            <div class="col-md-7">
                @Html.DropDownListFor(model => Model.RtcgApplication, Model.Applications)
                @Html.ValidationMessageFor(model => model.RtcgApplication)
            </div>
        </div>
        <div class="form-group">
            @Html.LabelFor(model => model.RtcgVerminProof, "Vermin Proof", new { @class = "control-label col-md-5" })
            <div class="col-md-7">
                @Html.DropDownListFor(model => Model.RtcgVerminProof, Model.VerminProofing)

                @Html.ValidationMessageFor(model => model.RtcgVerminProof)
            </div>
        </div>
        <div class="form-group">
            <div class="col-md-offset-5 col-md-10">
                <input  value="Get Quotation" class="btn btn-primary" style="float: none" onclick="submitajax()" />
            </div>
        </div>
    </div>
}

<script type="text/javascript">
    function changed() {
        var tokensource = $('#_AFT');
        var formData = $('form').serialize();
        var token = $('input[name="__RequestVerificationToken"]', tokensource).val();
        $.extend(formData, { '__RequestVerificationToken': token });
        $.ajax({
           url: '/Rtcg/Ajax_DropdownChanged',
            type: "POST",
            data: formData,
            success: function (result) {
                $('#config-form').html(result);
            },
        })
    }

    function submitajax() {
        var tokensource = $('#_AFT');
        var formData = $('form').serialize();
        var token = $('input[name="__RequestVerificationToken"]', tokensource).val();
        $.extend(formData, { '__RequestVerificationToken': token });
        $.ajax({
            url: '/Rtcg/updateDB',
            type: "POST",
            data: formData,
            success: function (result) {

            },
        })
    }
</script>

RTCG_Configurator(渲染主体时呈现的“索引”视图)

@model HomeWeb.Models.RtcgConfigurationModel
@{
    ViewBag.Title = "RTCG Configurator";
}

<section >
    <div class="col-md-6 pv" id="config-form">
        @Html.Partial("_config")
    </div>    
    <div class="col-md-6" >
    </div> 
</section>
<div style="clear:both">
    <br /><br />
    <p><a href="~/Home/Index" class="btn btn-primary btn-sm">&laquo; Back Home </a></p>
</div>

控制器 - Ajax_DropdownChanged()

// POST: handle dropdown changes from Ajax
      [HttpPost]
      [ValidateAntiForgeryToken]
        public ActionResult Ajax_DropdownChanged(RtcgConfigurationModel formData)
        {  
            //handle 'postback' displaying the fields. 
            if (ModelState.IsValid)
            { 
                RtcgConfigurationModel model = new RtcgConfigurationModel();
                using (var db = new ApplicationDbContext())
                {
                    model.Cabinets = DisplayElement.Cabinets(formData);
                    model.Adaptors = DisplayElement.Adaptors(formData);
                    model.AdaptorQtys = DisplayElement.AdaptorQuantities(formData);
                    model.TerminationMethods = DisplayElement.Termination(formData);
                    model.FacePlateStyles = DisplayElement.Faceplates(formData);
                    model.ScreenPrintOptions = DisplayElement.ScreenPrinting(formData);
                    model.Applications = DisplayElement.Application(formData);
                    model.VerminProofing = DisplayElement.VerminProof(formData);   
                }
                return PartialView("_config", model);
                //return View(model);
            }
            else
            {
                return PartialView();
            }
        }

【问题讨论】:

  • $('form').serialize(); 更改为$('#xxx').serialize(),其中xxx 是表单的id 属性(这样您就只序列化您想要的表单,而不是序列化所有表单,因此也序列化所有防伪令牌)
  • 我觉得自己好蠢。有人这么说是有道理的,但在过去的 2 个小时里,我一直盯着屏幕,处于收益递减的糟糕情况。它有效...谢谢一百万。
  • 嗯,我确实在我的 cmets 中对你的最后一个问题说过 :) 也许这次我最好添加一个答案 :)
  • 刚刚检查过,是的,你做到了,我错过了信息的 sn-p 并再次学会了艰难而有点尴尬的方式。再次感谢。

标签: jquery ajax asp.net-mvc post antiforgerytoken


【解决方案1】:

您对var formData = $('form').serialize(); 的使用将序列化视图中所有表单中所有表单控件的值,包括防伪令牌,这就是您提交多个令牌的原因。

确保每个表单都有一个id 属性,然后使用id="configform" 仅提交表单中的值(和令牌),使用

var formData = $('#configform').serialize();

请注意,您不需要

var token = $('input[name="__RequestVerificationToken"]', tokensource).val();
$.extend(formData, { '__RequestVerificationToken': token });

因为.serialize() 已经包含令牌的名称/值对(假设@Html.AntiForgeryToken() 包含在&lt;form&gt; 标记之间)

【讨论】:

    【解决方案2】:

    问题是一个菜鸟的错误。

    正如斯蒂芬在回应中所说的那样。我没有特别处理表格。添加 $('#configform').serialize() 就可以了。

    我将令牌添加到每个表单并将其从布局页面中删除。

    这是我的 ajax 代码。

    <script type="text/javascript">
        function changed() {
            $.ajax({
               url: '/Rtcg/Ajax_DropdownChanged',
                type: "POST",
                data: $('#configform').serialize(),
                success: function (result) {
                    $('#config-form').html(result);
                },
            })
        }
    </script>
    

    斯蒂芬应该穿着斗篷。

    【讨论】:

      猜你喜欢
      • 2015-01-11
      • 2023-04-09
      • 2015-05-11
      • 2014-10-03
      • 1970-01-01
      • 2014-04-26
      • 2014-01-23
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多