ASP.NET MVC 뷰를 문자열로 렌더링하는 방법은 무엇입니까?
두 가지 다른보기 (하나는 이메일로 전송되는 문자열)와 다른 하나는 사용자에게 표시되는 페이지를 출력하고 싶습니다.
ASP.NET MVC 베타에서 가능합니까?
여러 예제를 시도했습니다.
1. ASP.NET MVC 베타에서 RenderPartial을 문자열로
이 예제를 사용하면 "HTTP 헤더를 보낸 후 리디렉션 할 수 없습니다."라는 메시지가 나타납니다.
이것을 사용하면 redirectToAction을 수행 할 수없는 것 같습니다.보기가 존재하지 않을 수 있습니다. 보기를 반환하면 완전히 엉망이되어 전혀 보이지 않습니다.
누구든지 내가 가지고있는이 문제에 대한 아이디어 / 솔루션이 있거나 더 나은 문제에 대한 제안이 있습니까?
많은 감사합니다!
아래는 예입니다. 내가하려는 것은 GetViewForEmail 메서드를 만드는 것입니다 .
public ActionResult OrderResult(string ref)
{
//Get the order
Order order = OrderService.GetOrder(ref);
//The email helper would do the meat and veg by getting the view as a string
//Pass the control name (OrderResultEmail) and the model (order)
string emailView = GetViewForEmail("OrderResultEmail", order);
//Email the order out
EmailHelper(order, emailView);
return View("OrderResult", order);
}
Tim Scott의 답변 (나에 의해 조금 변경되고 형식화 됨) :
public virtual string RenderViewToString(
ControllerContext controllerContext,
string viewPath,
string masterPath,
ViewDataDictionary viewData,
TempDataDictionary tempData)
{
Stream filter = null;
ViewPage viewPage = new ViewPage();
//Right, create our view
viewPage.ViewContext = new ViewContext(controllerContext, new WebFormView(viewPath, masterPath), viewData, tempData);
//Get the response context, flush it and get the response filter.
var response = viewPage.ViewContext.HttpContext.Response;
response.Flush();
var oldFilter = response.Filter;
try
{
//Put a new filter into the response
filter = new MemoryStream();
response.Filter = filter;
//Now render the view into the memorystream and flush the response
viewPage.ViewContext.View.Render(viewPage.ViewContext, viewPage.ViewContext.HttpContext.Response.Output);
response.Flush();
//Now read the rendered view.
filter.Position = 0;
var reader = new StreamReader(filter, response.ContentEncoding);
return reader.ReadToEnd();
}
finally
{
//Clean up.
if (filter != null)
{
filter.Dispose();
}
//Now replace the response filter
response.Filter = oldFilter;
}
}
사용법 예
Site.Master 위치를 통과하여 주문 확인 이메일을 받기 위해 컨트롤러에서 전화를받는다고 가정합니다.
string myString = RenderViewToString(this.ControllerContext, "~/Views/Order/OrderResultEmail.aspx", "~/Views/Shared/Site.Master", this.ViewData, this.TempData);
여기에 내가 생각해 낸 것이 있으며 나를 위해 일하고 있습니다. 컨트롤러 기본 클래스에 다음 방법을 추가했습니다. (내가 생각하는 매개 변수로 컨트롤러를 허용하는 다른 곳 에서이 정적 메소드를 항상 만들 수 있습니다)
MVC2 .ascx 스타일
protected string RenderViewToString<T>(string viewPath, T model) {
ViewData.Model = model;
using (var writer = new StringWriter()) {
var view = new WebFormView(ControllerContext, viewPath);
var vdd = new ViewDataDictionary<T>(model);
var viewCxt = new ViewContext(ControllerContext, view, vdd,
new TempDataDictionary(), writer);
viewCxt.View.Render(viewCxt, writer);
return writer.ToString();
}
}
면도기 .cshtml 스타일
public string RenderRazorViewToString(string viewName, object model)
{
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();
}
}
편집 : 면도기 코드가 추가되었습니다.
이 대답은 내 길에 없습니다. 이것은 원래 https://stackoverflow.com/a/2759898/2318354 에서 가져온 것이지만 여기서는 "Static"키워드와 함께 사용하여 모든 컨트롤러에 공통으로 사용하는 방법을 보여주었습니다.
이를 위해서는 static
클래스 파일에서 클래스 를 만들어야 합니다. (클래스 파일 이름은 Utils.cs라고 입력하십시오)
이 예는 For Razor입니다.
Utils.cs
public static class RazorViewToString
{
public static string RenderRazorViewToString(this Controller controller, string viewName, object model)
{
controller.ViewData.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();
}
}
}
이제 "this"를 매개 변수로 Controller에 전달하여 다음과 같이 Controller File에 NameSpace를 추가하여 컨트롤러에서이 클래스를 호출 할 수 있습니다.
string result = RazorViewToString.RenderRazorViewToString(this ,"ViewName", model);
@Sergey가 제안한 대로이 확장 방법은 아래에 나와있는 것처럼 cotroller에서 호출 할 수도 있습니다
string result = this.RenderRazorViewToString("ViewName", model);
이것이 코드를 깨끗하고 깔끔하게 만드는 데 도움이되기를 바랍니다.
이것은 나를 위해 작동합니다 :
public virtual string RenderView(ViewContext viewContext)
{
var response = viewContext.HttpContext.Response;
response.Flush();
var oldFilter = response.Filter;
Stream filter = null;
try
{
filter = new MemoryStream();
response.Filter = filter;
viewContext.View.Render(viewContext, viewContext.HttpContext.Response.Output);
response.Flush();
filter.Position = 0;
var reader = new StreamReader(filter, response.ContentEncoding);
return reader.ReadToEnd();
}
finally
{
if (filter != null)
{
filter.Dispose();
}
response.Filter = oldFilter;
}
}
현재 HttpContext의 Response 스트림을 엉망으로 만들지 않고 문자열로 뷰를 렌더링하는 새로운 솔루션을 찾았습니다 (응답의 ContentType 또는 다른 헤더를 변경할 수 없음).
기본적으로 뷰 자체가 렌더링되도록 가짜 HttpContext를 작성하기 만하면됩니다.
/// <summary>Renders a view to string.</summary>
public static string RenderViewToString(this Controller controller,
string viewName, object viewData) {
//Create memory writer
var sb = new StringBuilder();
var memWriter = new StringWriter(sb);
//Create fake http context to render the view
var fakeResponse = new HttpResponse(memWriter);
var fakeContext = new HttpContext(HttpContext.Current.Request, fakeResponse);
var fakeControllerContext = new ControllerContext(
new HttpContextWrapper(fakeContext),
controller.ControllerContext.RouteData,
controller.ControllerContext.Controller);
var oldContext = HttpContext.Current;
HttpContext.Current = fakeContext;
//Use HtmlHelper to render partial view to fake context
var html = new HtmlHelper(new ViewContext(fakeControllerContext,
new FakeView(), new ViewDataDictionary(), new TempDataDictionary()),
new ViewPage());
html.RenderPartial(viewName, viewData);
//Restore context
HttpContext.Current = oldContext;
//Flush memory and return output
memWriter.Flush();
return sb.ToString();
}
/// <summary>Fake IView implementation used to instantiate an HtmlHelper.</summary>
public class FakeView : IView {
#region IView Members
public void Render(ViewContext viewContext, System.IO.TextWriter writer) {
throw new NotImplementedException();
}
#endregion
}
이것은 ASP.NET MVC 1.0에서 ContentResult, JsonResult 등과 함께 작동합니다. 원래 HttpResponse에서 헤더를 변경하면 " HTTP 헤더를 보낸 후 서버에서 컨텐츠 유형을 설정할 수 없습니다 "예외가 발생 하지 않습니다 .
업데이트 : ASP.NET MVC 2.0 RC StringWriter
에서 뷰를 작성하는 데 사용되어야 하기 때문에 코드가 약간 변경 됩니다 ViewContext
.
//...
//Use HtmlHelper to render partial view to fake context
var html = new HtmlHelper(
new ViewContext(fakeControllerContext, new FakeView(),
new ViewDataDictionary(), new TempDataDictionary(), memWriter),
new ViewPage());
html.RenderPartial(viewName, viewData);
//...
이 문서에서는 다른 시나리오에서 뷰를 문자열로 렌더링하는 방법을 설명합니다.
- 다른 자체 ActionMethods를 호출하는 MVC Controller
- 다른 MVC 컨트롤러의 ActionMethod를 호출하는 MVC 컨트롤러
- MVC 컨트롤러의 ActionMethod를 호출하는 WebAPI 컨트롤러
솔루션 / 코드는 ViewRenderer 라는 클래스로 제공됩니다 . GitHub 의 Rick Stahl WestwindToolkit 의 일부입니다 .
사용법 (3.-WebAPI 예) :
string html = ViewRenderer.RenderView("~/Areas/ReportDetail/Views/ReportDetail/Index.cshtml", ReportVM.Create(id));
MVC를 완전히 버리고 싶다면 모든 HttpContext 혼란을 피하십시오 ...
using RazorEngine;
using RazorEngine.Templating; // For extension methods.
string razorText = System.IO.File.ReadAllText(razorTemplateFileLocation);
string emailBody = Engine.Razor.RunCompile(razorText, "templateKey", typeof(Model), model);
이것은 멋진 오픈 소스 Razor Engine을 사용합니다 : https://github.com/Antaris/RazorEngine
이 방법으로 문자열로 볼 수 있습니다.
protected string RenderPartialViewToString(string viewName, object model)
{
if (string.IsNullOrEmpty(viewName))
viewName = ControllerContext.RouteData.GetRequiredString("action");
if (model != null)
ViewData.Model = model;
using (StringWriter sw = new StringWriter())
{
ViewEngineResult viewResult = ViewEngines.Engines.FindPartialView(ControllerContext, viewName);
ViewContext viewContext = new ViewContext(ControllerContext, viewResult.View, ViewData, TempData, sw);
viewResult.View.Render(viewContext, sw);
return sw.GetStringBuilder().ToString();
}
}
우리는이 방법을 두 가지 방법으로 부릅니다
string strView = RenderPartialViewToString("~/Views/Shared/_Header.cshtml", null)
또는
var model = new Person()
string strView = RenderPartialViewToString("~/Views/Shared/_Header.cshtml", model)
MVC 1.0 RTM을 사용하고 있으며 위의 솔루션 중 어느 것도 나를 위해 일하지 않았습니다. 그러나 이것은했다 :
Public Function RenderView(ByVal viewContext As ViewContext) As String
Dim html As String = ""
Dim response As HttpResponse = HttpContext.Current.Response
Using tempWriter As New System.IO.StringWriter()
Dim privateMethod As MethodInfo = response.GetType().GetMethod("SwitchWriter", BindingFlags.NonPublic Or BindingFlags.Instance)
Dim currentWriter As Object = privateMethod.Invoke(response, BindingFlags.NonPublic Or BindingFlags.Instance Or BindingFlags.InvokeMethod, Nothing, New Object() {tempWriter}, Nothing)
Try
viewContext.View.Render(viewContext, Nothing)
html = tempWriter.ToString()
Finally
privateMethod.Invoke(response, BindingFlags.NonPublic Or BindingFlags.Instance Or BindingFlags.InvokeMethod, Nothing, New Object() {currentWriter}, Nothing)
End Try
End Using
Return html
End Function
다른 웹 사이트에서 MVC 3 및 Razor에 대한 구현을 보았습니다.
public static string RazorRender(Controller context, string DefaultAction)
{
string Cache = string.Empty;
System.Text.StringBuilder sb = new System.Text.StringBuilder();
System.IO.TextWriter tw = new System.IO.StringWriter(sb);
RazorView view_ = new RazorView(context.ControllerContext, DefaultAction, null, false, null);
view_.Render(new ViewContext(context.ControllerContext, view_, new ViewDataDictionary(), new TempDataDictionary(), tw), tw);
Cache = sb.ToString();
return Cache;
}
public static string RenderRazorViewToString(string viewName, object model)
{
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);
return sw.GetStringBuilder().ToString();
}
}
public static class HtmlHelperExtensions
{
public static string RenderPartialToString(ControllerContext context, string partialViewName, ViewDataDictionary viewData, TempDataDictionary tempData)
{
ViewEngineResult result = ViewEngines.Engines.FindPartialView(context, partialViewName);
if (result.View != null)
{
StringBuilder sb = new StringBuilder();
using (StringWriter sw = new StringWriter(sb))
{
using (HtmlTextWriter output = new HtmlTextWriter(sw))
{
ViewContext viewContext = new ViewContext(context, result.View, viewData, tempData, output);
result.View.Render(viewContext, output);
}
}
return sb.ToString();
}
return String.Empty;
}
}
더에 면도기 render- MVC3보기 문자열로 렌더링
ControllerContext를 전달하지 않고 서비스 계층에서 문자열로보기를 렌더링하려면 여기 에 일반 컨트롤러를 작성하는 Rick Rick의 기사 ( http://www.codemag.com/Article/1312081) 가 있습니다. 아래 코드 요약 :
// Some Static Class
public static string RenderViewToString(ControllerContext context, string viewPath, object model = null, bool partial = false)
{
// first find the ViewEngine for this view
ViewEngineResult viewEngineResult = null;
if (partial)
viewEngineResult = ViewEngines.Engines.FindPartialView(context, viewPath);
else
viewEngineResult = ViewEngines.Engines.FindView(context, viewPath, null);
if (viewEngineResult == null)
throw new FileNotFoundException("View cannot be found.");
// get the view and attach the model to view data
var view = viewEngineResult.View;
context.Controller.ViewData.Model = model;
string result = null;
using (var sw = new StringWriter())
{
var ctx = new ViewContext(context, view, context.Controller.ViewData, context.Controller.TempData, sw);
view.Render(ctx, sw);
result = sw.ToString();
}
return result;
}
// In the Service Class
public class GenericController : Controller
{ }
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;
if (System.Web.HttpContext.Current != null)
wrapper = new HttpContextWrapper(System.Web.HttpContext.Current);
else
throw new InvalidOperationException("Cannot 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;
}
그런 다음 서비스 클래스에서보기를 렌더링하십시오.
var stringView = RenderViewToString(CreateController<GenericController>().ControllerContext, "~/Path/To/View/Location/_viewName.cshtml", theViewModel, true);
빠른 팁
강력한 형식의 Model의 경우 RenderViewToString에 전달하기 전에 ViewData.Model 속성에 추가하십시오. 예 :
this.ViewData.Model = new OrderResultEmailViewModel(order);
string myString = RenderViewToString(this.ControllerContext, "~/Views/Order/OrderResultEmail.aspx", "~/Views/Shared/Site.Master", this.ViewData, this.TempData);
더 알려지지 않은 질문을 반복하려면 MvcIntegrationTestFramework를 살펴 보십시오 .
결과를 스트리밍하기 위해 자신의 헬퍼를 작성하는 것을 절약하고 충분히 잘 작동하는 것으로 입증되었습니다. 나는 이것이 테스트 프로젝트에 있다고 가정하고 보너스 로이 설정을하면 다른 테스트 기능을 사용할 수 있습니다. 주요 성가심은 아마도 종속성 체인을 정렬하는 것일 것입니다.
private static readonly string mvcAppPath =
Path.GetFullPath(AppDomain.CurrentDomain.BaseDirectory
+ "\\..\\..\\..\\MyMvcApplication");
private readonly AppHost appHost = new AppHost(mvcAppPath);
[Test]
public void Root_Url_Renders_Index_View()
{
appHost.SimulateBrowsingSession(browsingSession => {
RequestResult result = browsingSession.ProcessRequest("");
Assert.IsTrue(result.ResponseText.Contains("<!DOCTYPE html"));
});
}
다음은 ASP.NETCore RC2를 위해 작성한 클래스입니다. Razor를 사용하여 html 전자 메일을 생성 할 수 있도록 사용합니다.
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Mvc.ViewEngines;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.AspNetCore.Routing;
using System.IO;
using System.Threading.Tasks;
namespace cloudscribe.Web.Common.Razor
{
/// <summary>
/// the goal of this class is to provide an easy way to produce an html string using
/// Razor templates and models, for use in generating html email.
/// </summary>
public class ViewRenderer
{
public ViewRenderer(
ICompositeViewEngine viewEngine,
ITempDataProvider tempDataProvider,
IHttpContextAccessor contextAccesor)
{
this.viewEngine = viewEngine;
this.tempDataProvider = tempDataProvider;
this.contextAccesor = contextAccesor;
}
private ICompositeViewEngine viewEngine;
private ITempDataProvider tempDataProvider;
private IHttpContextAccessor contextAccesor;
public async Task<string> RenderViewAsString<TModel>(string viewName, TModel model)
{
var viewData = new ViewDataDictionary<TModel>(
metadataProvider: new EmptyModelMetadataProvider(),
modelState: new ModelStateDictionary())
{
Model = model
};
var actionContext = new ActionContext(contextAccesor.HttpContext, new RouteData(), new ActionDescriptor());
var tempData = new TempDataDictionary(contextAccesor.HttpContext, tempDataProvider);
using (StringWriter output = new StringWriter())
{
ViewEngineResult viewResult = viewEngine.FindView(actionContext, viewName, true);
ViewContext viewContext = new ViewContext(
actionContext,
viewResult.View,
viewData,
tempData,
output,
new HtmlHelperOptions()
);
await viewResult.View.RenderAsync(viewContext);
return output.GetStringBuilder().ToString();
}
}
}
}
위의 방법으로 오류가 발생했을 때 면도기보기 페이지를 렌더링하는 더 좋은 방법을 찾았습니다.이 양식은 웹 양식 환경과 mvc 환경 모두에 적합합니다. 컨트롤러가 필요하지 않습니다.
다음은 코드 예제입니다.이 예제에서는 비동기 http 핸들러를 사용하여 mvc 작업을 시뮬레이션했습니다.
/// <summary>
/// Enables processing of HTTP Web requests asynchronously by a custom HttpHandler that implements the IHttpHandler interface.
/// </summary>
/// <param name="context">An HttpContext object that provides references to the intrinsic server objects.</param>
/// <returns>The task to complete the http request.</returns>
protected override async Task ProcessRequestAsync(HttpContext context)
{
if (this._view == null)
{
this.OnError(context, new FileNotFoundException("Can not find the mvc view file.".Localize()));
return;
}
object model = await this.LoadModelAsync(context);
WebPageBase page = WebPageBase.CreateInstanceFromVirtualPath(this._view.VirtualPath);
using (StringWriter sw = new StringWriter())
{
page.ExecutePageHierarchy(new WebPageContext(new HttpContextWrapper(context), page, model), sw);
await context.Response.Output.WriteAsync(sw.GetStringBuilder().ToString());
}
}
ASP NET CORE에 대한 추가 팁 :
상호 작용:
public interface IViewRenderer
{
Task<string> RenderAsync<TModel>(Controller controller, string name, TModel model);
}
이행:
public class ViewRenderer : IViewRenderer
{
private readonly IRazorViewEngine viewEngine;
public ViewRenderer(IRazorViewEngine viewEngine) => this.viewEngine = viewEngine;
public async Task<string> RenderAsync<TModel>(Controller controller, string name, TModel model)
{
ViewEngineResult viewEngineResult = this.viewEngine.FindView(controller.ControllerContext, name, false);
if (!viewEngineResult.Success)
{
throw new InvalidOperationException(string.Format("Could not find view: {0}", name));
}
IView view = viewEngineResult.View;
controller.ViewData.Model = model;
await using var writer = new StringWriter();
var viewContext = new ViewContext(
controller.ControllerContext,
view,
controller.ViewData,
controller.TempData,
writer,
new HtmlHelperOptions());
await view.RenderAsync(viewContext);
return writer.ToString();
}
}
에 등록 Startup.cs
...
services.AddSingleton<IViewRenderer, ViewRenderer>();
...
그리고 컨트롤러에서의 사용법 :
public MyController: Controller
{
private readonly IViewRenderer renderer;
public MyController(IViewRendere renderer) => this.renderer = renderer;
public async Task<IActionResult> MyViewTest
{
var view = await this.renderer.RenderAsync(this, "MyView", model);
return new OkObjectResult(view);
}
}
참고 URL : https://stackoverflow.com/questions/483091/how-to-render-an-asp-net-mvc-view-as-a-string
'Programming' 카테고리의 다른 글
.CPP 파일에 C ++ 템플릿 함수 정의 저장 (0) | 2020.02.14 |
---|---|
React.js 인라인 스타일 모범 사례 (0) | 2020.02.14 |
파이썬 파일의 일반적인 헤더 형식은 무엇입니까? (0) | 2020.02.14 |
벡터에 주어진 요소가 포함되어 있는지 테스트 (0) | 2020.02.14 |
HTML로 Base64 이미지를 표시하는 방법? (0) | 2020.02.14 |