두 가지 다른보기 (하나는 이메일로 전송되는 문자열)와 다른 하나는 사용자에게 표시되는 페이지를 출력하고 싶습니다.
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)