【问题标题】:ResetPassword feature doesn't use code from linkResetPassword 功能不使用链接中的代码
【发布时间】:2025-12-11 22:10:01
【问题描述】:

我正在查看一个旧的 MVC 网站。我不理解帐户控制器中的以下代码。 (我相信这是由 ASP.NET 生成的代码。)

控制器

//
// GET: /Account/ResetPassword
[AllowAnonymous]
public ActionResult ResetPassword(string code)
{
    return code == null ? View("Error") : View();
}

//
// POST: /Account/ResetPassword
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> ResetPassword(ResetPasswordViewModel model)
{
    if (!ModelState.IsValid)
    {
        return View(model);
    }
    var user = await UserManager.FindByNameAsync(model.Email);
    if (user == null)
    {
        // Don't reveal that the user does not exist
        return RedirectToAction("ResetPasswordConfirmation", "Account");
    }
    var result = await UserManager.ResetPasswordAsync(user.Id, model.Code, model.Password);
    if (result.Succeeded)
    {
        return RedirectToAction("ResetPasswordConfirmation", "Account");
    }
    AddErrors(result);
    return View();
}

查看

@model OnBoard101.Models.ResetPasswordViewModel
@{
    ViewBag.Title = "Reset password";
}

<h2>@ViewBag.Title</h2>

@using (Html.BeginForm("ResetPassword", "Account", FormMethod.Post, new { @class = "form-horizontal", role = "form" }))
{
    @Html.AntiForgeryToken()
    <h4>Reset your password</h4>
    <hr />
    @Html.ValidationSummary("", new { @class = "text-danger" })
    @Html.HiddenFor(model => model.Code)
    <div class="form-group">
        @Html.LabelFor(m => m.Email, new { @class = "col-md-2 control-label" })
        <div class="col-md-10">
            @Html.TextBoxFor(m => m.Email, new { @class = "form-control" })
        </div>
    </div>
    <div class="form-group">
        @Html.LabelFor(m => m.Password, new { @class = "col-md-2 control-label" })
        <div class="col-md-10">
            @Html.PasswordFor(m => m.Password, new { @class = "form-control" })
        </div>
    </div>
    <div class="form-group">
        @Html.LabelFor(m => m.ConfirmPassword, new { @class = "col-md-2 control-label" })
        <div class="col-md-10">
            @Html.PasswordFor(m => m.ConfirmPassword, new { @class = "form-control" })
        </div>
    </div>
    <div class="form-group">
        <div class="col-md-offset-2 col-md-10">
            <input type="submit" class="btn btn-success" value="Reset" />
        </div>
    </div>
}

@section Scripts {
    @Scripts.Render("~/bundles/jqueryval")
}

当用户使用忘记密码功能然后单击电子邮件中发送给他们的链接时,将调用这些方法。

据我所知,POST 处理程序正确检测到链接中的无效代码(当代码不是值时,它会产生 Invalid Token 错误)。但我不明白怎么做。 GET 处理程序似乎只是简单地丢弃了 code 参数。

我不明白这段代码是如何工作的。 model.Code 是如何被填充的?

【问题讨论】:

  • 这似乎确实是一种奇怪的方法。如果您只是在 GET 阶段检查代码是否为空,则可以使用随机字符串作为查询参数调用它,它只会返回视图。 POST 发生时model.Code 在那里吗?
  • @MarianoLuisVilla:我发现如果代码无效,post 方法实际上会出错。但我看不出它是如何访问该代码的。
  • 有趣。也许有一些自定义的基本控制器或属性进行传递?查看ResetPassword 视图应该可以让您清楚地了解代码的放置位置。
  • 我很确定 get 中的查询字符串保留在 post 中。您可以检查生成的嵌入式表单的操作 url 吗?
  • 当我开始使用旧的 mvc 框架和 asked about it 时,我遇到了这个确切的问题。 urlbuilder 使用现有的路由值,如果它的路由相同,并且如果任何地方有匹配的参数,模型是从查询字符串和正文填充的。

标签: c# asp.net model-view-controller asp.net-identity


【解决方案1】:

ResetPassword 功能确实使用链接中的代码。

模型绑定从各种来源检索数据,例如路由数据、表单字段和查询字符串。

它检查查询字符串参数以匹配视图使用的模型上的属性

虽然看起来 code 似乎被控制器 GET 操作丢弃,但 code 仍然是请求的一部分并被视图使用。

而且由于视图显式绑定到模型

@model OnBoard101.Models.ResetPasswordViewModel

具有匹配的公共Code 属性(不区分大小写)

public string Code { get; set; }

它将在 GET 期间将其绑定到视图中的模型,然后使用它(模型) 填充隐藏的表单字段(如您的视图标记中所示)

@Html.HiddenFor(model => model.Code)

所以现在当提交表单时,POST 操作会将该字段绑定到模型,然后返回到该操作并执行验证

同样的效果也可以用

// GET: /Account/ResetPassword
[AllowAnonymous]
public ActionResult ResetPassword(string code) {
    if (code == null) return View("Error");
    var model = new ResetPasswordViewModel {
        Code = code
    };
    return View(model);
}

但由于内置模型绑定功能会在未提供模型的情况下初始化模型,因此上述代码实际上并没有为框架开箱即用的功能添加任何额外内容。

【讨论】:

  • 太棒了!尽管显式传递代码会使其在维护阶段更容易理解,IMO。
  • 谢谢。尽管视图绑定到模型,但我一直认为它是一个空模型,除非我为视图提供模型实例。我不知道视图会从查询字符串中收集数据。