RenderRazorViewToString用ModelState数据覆盖ViewModel数据-是预期的吗?

问题描述

我一直在使用一种流行的SO帖子中的方法How to render an ASP.NET MVC view as a string?

我没有将方法用作扩展方法,而是将其放在BaseController上,然后所有控制器都继承了base。这是我的实现:

public string RenderRazorViewToString(string viewName,object model = null)
{
    ViewData.Model = model;
    using (var sw = new StringWriter())
    {
        var viewResult = ViewEngines.Engines.FindPartialView(ControllerContext,viewName);
        var viewContext = new ViewContext(ControllerContext,viewResult.View,ViewData,TempData,sw);
        viewResult.View.Render(viewContext,sw);
        viewResult.ViewEngine.ReleaseView(ControllerContext,viewResult.View);
        return sw.GetStringBuilder().ToString();
    }
}

它一直运行良好(我认为/希望),直到昨天。 我有一个JavaScript函数,可对控制器进行AJAX调用,并提供参数“ firstName”。控制器接受它,创建一个新的视图模型,设置FirstName属性,然后呈现模式(bootstrap3)局部视图。模态使用@Html.EditorFor来呈现viewmodel的FirstName属性。但是,该值不是由控制器上设置的显示文本,而是由Javascript传递的参数。本质上,当AJAX GET请求的参数与viewmodel中的属性名称匹配(不区分大小写)时,提供的参数优先。这是我一直在测试的代码

function testModelBindingTempData() {
    var $modalContainer = $('#modalContainer');
    $.ajax({
        url: "@Url.Action("GetTestModal")",method: "GET",data: {
            firstName: "Michael is the bees knees."
        },success: function(data) {
            $modalContainer.html(data).find('.modal').modal('show');
        },error: function(xhr,status,err) {
            parseJsonHeaders(xhr,{ notificationLocation: '.notification-section' });
        }
     });
}
public JsonResult GetTestModal(string firstName)
{
    TestModalviewmodel vm = new TestModalviewmodel();
    vm.FirstName = "Michael is okay";
    //just render and hope the best
    return Json(RenderRazorViewToString("~/Views/Ajax/_TestModal.cshtml",vm));
}
@model Sample.Web.viewmodels.Ajax.TestModalviewmodel

<div class="modal modal-primary" tabindex="-1" role="dialog">
    <div class="modal-dialog" role="document">
        <div class="modal-content">
            <div class="modal-header"><span>Primary Simple Modal</span></div>
            <div class="modal-body">
                Used to inform the user of important stuff!
                @Html.EditorFor(m => m.FirstName)
            </div>
            <div class="modal-footer">
                <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
            </div>
        </div>
    </div>
</div>

结果:modal displays value supplied from the AJAX call instead of value set in the ViewModel

我发现问题值在ViewData.ModelState键值对中,但我不知道为什么要设置该值,而不是在控制器和HTML帮助器中的表达式中设置的值。

我的期望是,当您在控制器中设置一个值时,它应该优先于模型状态值或任何其他可用数据。

有人知道为什么吗?任何建议表示赞赏,谢谢!

/ edit:经过额外的测试,这似乎也发生在常规的ActionResults / Views中。 :/

解决方法

这是预期的,因为您正在以下位置调用JSONResult:

public JsonResult GetTestModal(string firstName)

这是什么意思? ASP.NET MVC将使用JSON格式返回响应。如果要传递ViewData数据,请将其作为匿名对象的属性包含在JSONResult中,以便可以在AJAX成功处理程序中填充它:

Json(new { MyViewData = firstName },JsonRequestBehavior.AllowGet);

然后在AJAX成功处理程序上,您可以提取属性MyViewData来填充HTML标记:

success: function(data) {
        $modalContainer.html(data.MyViewData).find('.modal').modal('show');
    },

如果要使用ViewBag,请不要使用JSONResult,而应使用ViewResult:

return View("ViewName",ModelName);