通过调用控制器的操作将部分视图转换为字符串
Transform partial view into string with calling controller's action
我有 MVC 控制器,其中包含 returns 部分视图的操作。 Action包含很多逻辑,通过ViewBag对象在view中发送数据。
我需要将此操作的结果呈现为字符串,然后作为电子邮件发送。我找到的所有解决方案都在不调用操作的情况下呈现视图。
是否可以在我的代码中的某处初始化控制器,调用操作方法并将 ActionResult(即部分视图)转换为字符串?
感谢
我做到了。非常痛苦。
首先我们需要在没有上下文的情况下初始化控制器。为此,我使用了 Rick Strahl 的方法 CreateController
public static T CreateController<T>(RouteData routeData = null)
where T : Controller, new()
{
// create a disconnected controller instance
T controller = new T();
// get context wrapper from HttpContext if available
HttpContextBase wrapper = null;
if (HttpContext.Current != null)
wrapper = new HttpContextWrapper(System.Web.HttpContext.Current);
else
throw new InvalidOperationException(
"Can't create Controller Context if no active HttpContext instance is available.");
if (routeData == null)
routeData = new RouteData();
// add the controller routing if not existing
if (!routeData.Values.ContainsKey("controller") && !routeData.Values.ContainsKey("Controller"))
routeData.Values.Add("controller", controller.GetType().Name
.ToLower()
.Replace("controller", ""));
controller.ControllerContext = new ControllerContext(wrapper, routeData, controller);
return controller;
}
但是如果你在HttpContext的时候调用这个方法会抛出异常。为了防止我使用此代码初始化假上下文:.
然后我初始化控制器并调用动作。此操作的结果包含 viewData 以及从操作传递的所有数据。我们可以通过反射(像忍者一样)得到它。
然后我们可以使用传递的模型将视图渲染为字符串。
var controller = CreateController<ControllerName>();
var result = controller.ActionName(param1, param2);
object viewData = result.GetType().GetProperty("ViewData").GetValue(result);
object values = viewData.GetType().GetProperty("Values").GetValue(viewData);
var str = ec.RenderRazorViewToString("ViewName", values);
return str;
此方法将视图呈现为字符串:
public static string RenderRazorViewToString(this Controller controller, string viewName, object model)
{
controller.ViewData.Model = model;
controller.ViewBag.Model = model;
using (var sw = new StringWriter())
{
var viewResult = ViewEngines.Engines.FindPartialView(controller.ControllerContext, viewName);
var viewContext = new ViewContext(controller.ControllerContext, viewResult.View, controller.ViewData, controller.TempData, sw);
viewResult.View.Render(viewContext, sw);
viewResult.ViewEngine.ReleaseView(controller.ControllerContext, viewResult.View);
return sw.GetStringBuilder().ToString();
}
}
看起来这不是最好的方法,但它确实有效。所以大家有什么想法一定要写出来
我有 MVC 控制器,其中包含 returns 部分视图的操作。 Action包含很多逻辑,通过ViewBag对象在view中发送数据。
我需要将此操作的结果呈现为字符串,然后作为电子邮件发送。我找到的所有解决方案都在不调用操作的情况下呈现视图。
是否可以在我的代码中的某处初始化控制器,调用操作方法并将 ActionResult(即部分视图)转换为字符串?
感谢
我做到了。非常痛苦。
首先我们需要在没有上下文的情况下初始化控制器。为此,我使用了 Rick Strahl 的方法 CreateController
public static T CreateController<T>(RouteData routeData = null)
where T : Controller, new()
{
// create a disconnected controller instance
T controller = new T();
// get context wrapper from HttpContext if available
HttpContextBase wrapper = null;
if (HttpContext.Current != null)
wrapper = new HttpContextWrapper(System.Web.HttpContext.Current);
else
throw new InvalidOperationException(
"Can't create Controller Context if no active HttpContext instance is available.");
if (routeData == null)
routeData = new RouteData();
// add the controller routing if not existing
if (!routeData.Values.ContainsKey("controller") && !routeData.Values.ContainsKey("Controller"))
routeData.Values.Add("controller", controller.GetType().Name
.ToLower()
.Replace("controller", ""));
controller.ControllerContext = new ControllerContext(wrapper, routeData, controller);
return controller;
}
但是如果你在HttpContext的时候调用这个方法会抛出异常。为了防止我使用此代码初始化假上下文:.
然后我初始化控制器并调用动作。此操作的结果包含 viewData 以及从操作传递的所有数据。我们可以通过反射(像忍者一样)得到它。 然后我们可以使用传递的模型将视图渲染为字符串。
var controller = CreateController<ControllerName>();
var result = controller.ActionName(param1, param2);
object viewData = result.GetType().GetProperty("ViewData").GetValue(result);
object values = viewData.GetType().GetProperty("Values").GetValue(viewData);
var str = ec.RenderRazorViewToString("ViewName", values);
return str;
此方法将视图呈现为字符串:
public static string RenderRazorViewToString(this Controller controller, string viewName, object model)
{
controller.ViewData.Model = model;
controller.ViewBag.Model = model;
using (var sw = new StringWriter())
{
var viewResult = ViewEngines.Engines.FindPartialView(controller.ControllerContext, viewName);
var viewContext = new ViewContext(controller.ControllerContext, viewResult.View, controller.ViewData, controller.TempData, sw);
viewResult.View.Render(viewContext, sw);
viewResult.ViewEngine.ReleaseView(controller.ControllerContext, viewResult.View);
return sw.GetStringBuilder().ToString();
}
}
看起来这不是最好的方法,但它确实有效。所以大家有什么想法一定要写出来