简体   繁体   中英

Render Controller Action To String (Using Virtual Path, No action/controller name) ASP.NET MVC 3

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.

http://razorengine.codeplex.com/

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM