ASP.NET MVC3:在父视图中显示子视图中的验证错误

时间:2011-08-11 14:15:53

标签: c# asp.net-mvc-3 validation parent-child

我正在尝试添加表单,以允许用户对我的博客应用程序上的帖子发表评论。到目前为止,我已经在帖子详细信息视图中添加了一个表单,我可以提交注释,将它们正确添加到我的数据库中。但是,我在向用户显示验证错误时遇到问题。注释表单包含在局部视图中,并使用帖子详细信息视图中的Html.RenderAction进行呈现。我想强调一点,我不想使用AJAX,因为我想从渐进式增强的角度来看待它。

以下是相关的发布操作:

[HttpPost, Authorize]
public ActionResult AddComment(CommentViewModel newComment)
{
    if (ModelState.IsValid)
    {
        Comment comment = new Comment(_userRepository.GetByUsername(User.Identity.Name));
        Mapper.Map(newComment, comment);

        _commentRepository.Add(comment);

        _postsRepository.CommentAdded(comment.Article);

        return RedirectToAction("Index", new { id = newComment.PostID });
    }

    // What do I do here?
}

我已经尝试了几种返回视图的方法,但是我在父操作中进行的一些控制器参数验证使我的问题更加复杂:

//
// GET: /Posts/5/this-is-a-slug

public ActionResult Index(int id, string slug)
{
    PostViewModel viewModel = new PostViewModel();
    var model = _postsRepository.GetByID(id);

    if (model != null)
    {
        if (slug == null || slug.CompareTo(model.Slug) != 0)
        {
            return RedirectToActionPermanent("Index", new { id, slug = model.Slug });
        }
        else
        {
            _postsRepository.PostVisited(model);

            Mapper.Map(model, viewModel);

            viewModel.AuthorName = _userRepository.GetById(model.AuthorID);
        }
    }

    return View(viewModel);
}

此操作基本上模仿了SO的URL如何工作。如果提供了帖子ID,则会从数据库中提取帖子以及创建帖子时创建的slug。如果URL中的slug与数据库中的slug不匹配,则重定向以包含slug。这很好用,但它确实意味着我在尝试填充我的父视图模型时遇到问题,如下所示:

public class PostViewModel
{
    public int PostID { get; set; }
    public string Title { get; set; }
    public string Body { get; set; }
    public string Slug { get; set; }
    public DateTime DatePublished { get; set; }
    public int NumberOfComments { get; set; }
    public int AuthorID { get; set; }
    public string AuthorName { get; set; }

    public List<CommentViewModel> Comments { get; set; }
    public CommentViewModel NewComment { get; set; }
}

我希望能够工作的是填充PostViewModel.NewComment,测试它是否有数据然后用它来显示任何模型错误。不幸的是,我失去了如何实现这一目标。 This question帮助我塑造了我的方法,但它并没有完全解决我的问题。

有人能给我一个正确的方向吗?如果我的方法看起来不合理,我很想知道为什么以及可能的解决方案。

非常感谢提前。

1 个答案:

答案 0 :(得分:2)

忘了在这里填写我的答案。对于碰巧遇到此问题的任何人,答案是使用TempData来存储ModelState错误,然后在相关控制器操作中重新填充ModelState

首先,我在控制器中声明了一个用于引用TempData内部数据的键。我决定将其基于CommentViewModel类型,因为两个操作都取决于它。

public class PostsController : Controller
{
    private static readonly string commentFormModelStateKey = typeof(CommentViewModel).FullName;
    // Rest of class.
}

在第一个操作中,代码会检查TempData是否包含分配给密钥的数据。如果是,则将其复制到ModelState

// GET: /posts/comment
[ChildActionOnly]
public PartialViewResult Comment(PostViewModel viewModel)
{
    viewModel.NewComment = new CommentViewModel(viewModel.PostID, viewModel.Slug);

    if (TempData.ContainsKey(commentFormModelStateKey))
    {
        ModelStateDictionary commentModelState = TempData[commentFormModelStateKey] as ModelStateDictionary;
        foreach (KeyValuePair<string, ModelState> valuePair in commentModelState)
            ModelState.Add(valuePair.Key, valuePair.Value);
    }

    return PartialView(viewModel.NewComment);
}

此操作确定在向数据库添加注释之前ModelState是否有效。如果ModelState无效,则会将其复制到TempData,这使其可用于第一个操作。

// POST: /posts/comment
[HttpPost, Authorize]
public ActionResult Comment(CommentViewModel newComment)
{
    if (!ModelState.IsValid)
    {
        TempData.Add(commentFormModelStateKey, ModelState);
        return Redirect(Url.ShowPost(newComment.PostID, newComment.Slug));
    }

    // Code to add a comment goes here.
}