0

我有以下型号:

public class Expense
{
    [Key]
    [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
    public int ID { get; set; }
    [Required]
    public string Name { get; set; }
    [Required]
    [Column(TypeName = "Money")]
    public decimal Limit { get; set; }
    [Required]
    [ForeignKey("UserProfile")]
    public int UserProfileId { get; set; }
    [Required]
    public virtual UserProfile UserProfile { get; set; }
}

[Table("UserProfile")]
public class UserProfile
{
    [Key]
    [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
    public int UserId { get; set; }
    public string UserName { get; set; }
    public virtual ICollection<Expense> Expenses { get; set; }
}

我有一个登录用户,我希望该用户能够添加新的费用。创建控制器操作如下所示:

    //
    // GET: /Expenses/Create

    public ActionResult Create()
    {

        return View();
    }

    //
    // POST: /Expenses/Create

    [HttpPost]
    public ActionResult Create(Expense expense)
    {            
        if (ModelState.IsValid)
        {
            UserProfile user = db.UserProfiles.Single(u => u.UserName == User.Identity.Name)
            user.Expenses.Add(expense);                
            db.SaveChanges();
            return RedirectToAction("Index");
        }

        return View(expense);
    }

有观点:

@model MoneyDrainPlug.Models.Expense

@{
    ViewBag.Title = "Create";
}

<h2>Create</h2>

@using (Html.BeginForm()) {
    @Html.ValidationSummary(true)

    <fieldset>
        <legend>Expense</legend>

        <div class="editor-label">
            @Html.LabelFor(model => model.Name)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.Name)
            @Html.ValidationMessageFor(model => model.Name)
        </div>

        <div class="editor-label">
            @Html.LabelFor(model => model.Limit)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.Limit)
            @Html.ValidationMessageFor(model => model.Limit)
        </div>        
        <p>
            <input type="submit" value="Create" />
        </p>
    </fieldset>
}

<div>
    @Html.ActionLink("Back to List", "Index")
</div>

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

当帖子发生时,控制器中的 ModelState.IsValid 为 false。这是因为费用中没有设置 UserProfileId。如果我在调用 ModelState.IsValid 之前设置了 UserProfile 和\或 UserProfileId,这将无济于事。

处理此问题的正确和安全方法是什么?我当然不应该相信从客户端发送的 UserProfileId 吗?

万一这很重要,我使用的是 asp.net MVC 4。

提前致谢

4

2 回答 2

1

可能的解决方案

删除Required属性UserProfileId(DataAnnotation 仅与视图相关:如果您不希望视图中有某些内容,请不要将其标记为RequiredRequiredAttribute不是数据库约束)

或者

删除UserProfileId财产(你UserProfile有财产,这可能就足够了)

或者

创建一个ViewModel没有UserProfileId属性

编辑:要清楚:您可以完美地使用 ViewModel。请注意,必需的属性与您的数据库没有直接关系。如果您不Expense直接在视图中使用该类,则可以删除 中的所有Required属性Expense,它们将永远不会被使用。

于 2012-10-05T22:16:44.210 回答
1

一方面,您应该确保这些页面只能由授权用户访问(在需要用户登录的控制器中的每个方法上方添加 [Authorize])

实际上有几种方法可以做到这一点:

一种是隐藏字段,其名称为 UserProfileId 和登录用户 ID 的值。这将是最不安全的方法,因为 ID 将从客户端发布到服务器,因此它可以由客户端操作。

我注意到这些字段具有必需的注释,因此要么删除 [Required] 以使其验证,要么创建具有所有这些属性的新视图模型,而无需 UserProfileId(显然您不包含虚拟属性,因为它不需要在此案子)。

结果会是这样的:

public class ExpenseModel
{
    [Required]
    public string Name { get; set; }
    [Required]
    public decimal Limit { get; set; }
    public int UserProfileId { get; set; }
}

最后,您更改视图期望的模型:

@model MoneyDrainPlug.Models.ExpenseModel

以及控制器期望在发布后收到的模型:

[Authorize]
[HttpPost]
    public ActionResult Create(ExpenseModel expense)
    {            
        if (ModelState.IsValid)
        {
            UserProfile user = db.UserProfiles.Single(u => u.UserName == User.Identity.Name)
            _expense = new Expense() { Name = expense.Name, Limit = expense.Limit, UserProfile = user };
            user.Expenses.Add(_expense);                
            db.SaveChanges();
            return RedirectToAction("Index");
        }

        return View(expense);
    }
于 2012-10-05T22:17:42.963 回答