Razor view - emitting html outside Doctype tag
I am trying to write a module/areas based mvc3 CMS using razor view engine. I have 开发者_JAVA百科two layout views, _site.cshtml and _modules.cshtml. The _site.cshtml has an @RenderBody() section. My application always calls a view called "index.cshtml" which has its layout set to _site.cshtml page. The Problem: The problem is that my modules/areas are rendered before doctype element - and not inside the RenderBody section of layout page.
It looks like what you're trying to do is the equivalent of Html.RenderAction (or Html.Action), so why are you writing all that code yourself instead of using the built-in functionality?
In short, its your Invoke() method! The razor engine works by writing to an in memory stream, and then parses it to the response. The reason you are getting that behaviour is because your Invoke() method is writing straight to the response stream; i.e. before the in memory stream is parsed.
I came across similar behaviour when using the Html.RenderAction(), which pointed to an action that returned a PartialView. The workaround was to use Html.Action(). The difference being is that Action() returns a string which gets appended to the in memory stream, and RenderAction() writes to the directly to the response.
If you post the code for your Invoke() method, I may be able to help you futher!
| -- Edit -- |
OK, this turned out to be more complex that initially anticipated. The problem is that I could not get ProcessRequest() to append to the current response; however, I may have a solution.
public string ProcessRoute(ViewContext viewContext, RouteData routeData)
{
var urlHelper = new UrlHelper(viewContext.RequestContext);
var currentUrl = urlHelper.Action(routeData.Values["action"].ToString(),
routeData.Values["controller"].ToString(), routeData.DataTokens);
var stringWriter = new StringWriter();
var simpleWorkerRequest = new SimpleWorkerRequest(currentUrl, "", stringWriter);
var context = new HttpContext(simpleWorkerRequest);
var contextBase = new HttpContextWrapper(context);
var requestContext = new RequestContext(contextBase, routeData);
var httpHandler = routeData.RouteHandler.GetHttpHandler(requestContext);
httpHandler.ProcessRequest(context);
context.Response.End();
stringWriter.Flush();
return stringWriter.GetStringBuilder().ToString();
}
The code above generates a new Request and returns the Request's HTML as a string. By doing this, the result is appended as part of the current response. You can now rewrite your Invoke() function to return a string, which can be displayed on your View.
public string Invoke(ViewContext viewContext)
{
if (_mvcHandler == null)
{
var routeData = new RouteData(context.RouteData.Route,
context.RouteData.RouteHandler);
routeData.Values.Add("id", _id);
routeData.Values.Add("moduleName", _moduleName);
routeData.Values.Add("controller", _controllerName);
routeData.Values.Add("action", _actionName);
routeData.Values.Add("pageContext", _pageContext);
if (!string.IsNullOrEmpty(_preferredNamespace))
{
routeData.DataTokens.Add("Namespaces", new[] { _preferredNamespace });
}
return ProcessRoute(viewContext, routeData);
}
return string.Empty;
}
You may also have to change;
mr.Invoke(ViewContext);
To;
Html.Raw(mr.Invoke(ViewContext));
In order stop the HTML encoding behaviour.
| -- Note -- |
Since I don't have your ModuleRequests class, I couldn't test this code specifically for your scenario. Instead, I replicated the problem as best as I could and solved it.
Please let me know if you have any questions.
Matt
Let me post a plausible second alternative to your methods while I try and solve the problem above.
There is a html extension in MVC called Html.Partial(), which allows you to use routedata to return the result of a controller action.
So, if you had an AccountController with an Register method that returned a PartialView result, then it would be appended to the current page.
public class AccountController() : Controller
{
public ActionResult Register()
{
return PartialView();
}
}
The call to this action could look something like;
@Html.Partial("Account", "Register");
| -- Note -- |
Don't use @Html.RenderPartial(), or you will have the same problem as above! I am currently posting away from a computer, so I can't test this theory!
Hope this helps!
Matt
精彩评论