Ok, im developing a MVC CMS like website and when declaring routes i used following pattern. I encapsulate action name and controller name into a class like so
public class UrlUtilsUnhandledErrorsExtensions
{
private readonly UrlHelper _urlHelper;
public UrlUtilsUnhandledErrorsExtensions(UrlHelper urlHelper)
{
_urlHelper = urlHelper;
}
public String GetLatestErrors()
{
return _urlHelper.Action("GetLatestErrors", "UnhandledErrors");
}
}
Then instead of writing
@Url.Action("GetLatestErrors", "UnhandledErrors")
I write
@Url.Action(Url.Utils().UnhandledErrors().GetLatestErrors())
I find this approach much more easier to maintain, because if controller name changes i only have to change one class.
This works fine with any links, controller redirects (return Redirect(...)) and just anything that accept virtual path which is returned by
public String GetLatestErrors()
{
return _urlHelper.Action("GetLatestErrors", "UnhandledErrors");
}
But here comes the problem : i cant use Html.Action() with this approach. It requires controller name and action name, but instead i want it to use virtual path. After digging around and studying MVC source code i realized that i will need to write my own Html.Action extension method that will just accept virtual path.
So here is my solution
public void ActionFromUrl(this HtmlHelper htmlHelper, String url)
{
RouteValueDictionary rvd = null;
rvd = new RouteValueDictionary();
String action = String.Empty;
String controller = String.Empty;
foreach (Route route in htmlHelper.RouteCollection)
{
if (route.Url == url.Substring(1)) // url starts with / for some reason
{
action = route.Defaults["action"] as String;
controller = route.Defaults["controller"] as String;
break;
}
}
RequestContext rc = ((MvcHandler)HttpContext.Current.CurrentHandler).RequestContext;
rc.RouteData.Values["action"] = action;
rc.RouteData.Values["controller"] = controller;
IControllerFactory factory = ControllerBuilder.Current.GetControllerFactory();
IController controllerImpl = factory.CreateController(rc, controller);
controllerImpl.Execute(rc);
}
It works, but since its based on Html.RenderAction method it just writes directly to output, so in my view when i write following code
@{ Html.ActionFromUrl(Url.Utils().UnhandledErrors().GetLatestErrors()); }
It renders my partial first, all above everything and then rest of html follows. This is not the result i want, so i have to find out the way of rendering the result to string as Html.Action do. I already looked into the source code with dotPeek but coudn't figure out how to mix it altogether.
My question is: Am i doing something wrong ? Or how can i write Html.Action overload so it accepts virtual path and returns MvcHtmlString ?
in CMS, you probably do not need the whole convention-based view rendering, you will (sooner or later) want render custom templates "to string" and merge result layout by your (most probably dynamic/configurable) rules. Take a look on RazorEngine project.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.