【问题标题】:Mixing ASP.Net MVC custom routing and partial views in a ASP.Net web forms project在 ASP.Net Web 表单项目中混合 ASP.Net MVC 自定义路由和部分视图
【发布时间】:2014-02-25 00:59:44
【问题描述】:

我正在尝试从用 ASP.Net Web 表单编写的 CMS 缓慢迁移到 ASP.Net MVC。为此,我需要将 Web 部件(自定义控件)的功能替换为部分视图的功能。 CMS 页面包含许多 Web 部件,每个部件最终都应从 MVC 局部视图呈现一些 html。我还需要一种方法来传递每个 Web 部件中设置的参数,并基于这些来构造局部视图。整个目的是将业务逻辑与表示分离,以消除对 CMS 本身的依赖。在此过程中,所有 Web 部件应该仍然可以工作。 您对实现这一目标的有效方法有任何想法或建议吗?

这是我的出发点。我正在尝试将 MVC 部分视图检索为 ASP.Net 管道之外的字符串。为此,我需要使用 HttpContext。由于我没有这个上下文(我只想使用通过某些接口传递的最少属性),我试图创建一个假的。

public class TestController : Controller, ICmsTranslator
{
    public ActionResult Index()
    {
        return View();
    }

    public string RenderAsString(IHttpRequest request, IContext context)
    {
        return RenderRazorViewToString("~/Views/Shared/_TestPartialView.cshtml", new TestModels(), request, context);
    }

    public string RenderRazorViewToString(string viewName, object model, IHttpRequest request, IContext context)
    {
        ViewData.Model = model;
        using (var sw = new StringWriter())
        {
            // create a fake HttpContext
            var fakeRouteData = new RouteData();
            if (!fakeRouteData.Values.ContainsKey("controller") && !fakeRouteData.Values.ContainsKey("Controller"))
            {
                fakeRouteData.Values.Add("controller", GetType().Name
                                                             .ToLower()
                                                             .Replace("controller", ""));
            }
            HttpContextBase offlineContextBase = new OfflineHttpContext(viewName, request, context);
            var offlineControllerContext = new ControllerContext(offlineContextBase, fakeRouteData, this);

            // try to use the fake context to get the partial view
            var viewResult = ViewEngines.Engines.FindPartialView(offlineControllerContext, viewName);
            var viewContext = new ViewContext(offlineControllerContext, viewResult.View, ViewData, TempData, sw);
            viewResult.View.Render(viewContext, sw);
            viewResult.ViewEngine.ReleaseView(offlineControllerContext, viewResult.View);
            return sw.GetStringBuilder().ToString();
        }
    }
}




public class OfflineHttpContext : HttpContextBase
{   
    private readonly Hashtable _items = new Hashtable();
    private IPrincipal _user;
    private readonly IHttpRequest _request;
    private readonly IContext _context;
    private readonly string _relativeUrl;

    public OfflineHttpContext(string relativeUrl, IHttpRequest request, IContext context)
    {
        _relativeUrl = relativeUrl;
        _request = request;
        _context = context;
    }

    public override Exception[] AllErrors
    {
        get { return new Exception[0]; }
    }

    public override Cache Cache
    {
        get { return null; }
    }

    public override HttpRequestBase Request
    {
        get
        {
            var serverVariables = new NameValueCollection
                                      {
                                          {"HTTP_HOST", "www.example.com"},
                                          {"APPL_PHYSICAL_PATH", _context.PhysicalPath}
                                      };

            var headers = new NameValueCollection();
            foreach (var header in _request.Headers)
            {
                headers.Add(header.Key, header.Value);
            }
            var formParams = new NameValueCollection();
            var queryStringParams = new NameValueCollection();
            var cookies = new HttpCookieCollection();

            HttpRequestBase httpContext = new OfflineHttpRequest(_relativeUrl, _request.Method, formParams, queryStringParams, cookies, serverVariables, headers);
            return httpContext;
        }
    }

    public override HttpResponseBase Response
    {
        get
        { 
            var httpResponse = new HttpResponse(new StringWriter());
            return new HttpResponseWrapper(httpResponse);
        }
    }

    public override IDictionary Items
    {
        get { return _items; }
    }

    public override IPrincipal User
    {
        get
        {
            if (_user == null)
            {
                // User is logged in
                return new GenericPrincipal(new GenericIdentity("public"), new string[0]);
                // User is logged out
                // return new GenericPrincipal(new GenericIdentity(""), new string[0]);
            }
            return _user;
        }
        set { _user = value; }
    }
}


public class OfflineHttpRequest : HttpRequestBase
{
    private readonly HttpCookieCollection _cookies;
    private readonly NameValueCollection _formParams;
    private readonly NameValueCollection _queryStringParams;
    private readonly NameValueCollection _headers;
    private readonly NameValueCollection _serverVariables;
    private readonly string _relativeUrl;
    private readonly Uri _url;
    private readonly Uri _urlReferrer;
    private readonly string _httpMethod;

    public OfflineHttpRequest(
        string relativeUrl, 
        string httpMethod,
        NameValueCollection formParams, 
        NameValueCollection queryStringParams,
        HttpCookieCollection cookies, 
        NameValueCollection serverVariables, 
        NameValueCollection headers)
    {
        _httpMethod = httpMethod;
        _relativeUrl = relativeUrl;
        _formParams = formParams;
        _queryStringParams = queryStringParams;
        _cookies = cookies;
        _serverVariables = serverVariables;
        _headers = headers;

        //ensure collections are not null
        if (_formParams == null)
        {
            _formParams = new NameValueCollection();
        }
        if (_queryStringParams == null)
        {
            _queryStringParams = new NameValueCollection();
        }
        if (_cookies == null)
        {
            _cookies = new HttpCookieCollection();
        }
        if (_serverVariables == null)
        {
            _serverVariables = new NameValueCollection();
        }
        if (_headers == null)
        {
            _headers = new NameValueCollection();
        }
    }

    public OfflineHttpRequest(
        string relativeUrl,
        string httpMethod, 
        Uri url, 
        Uri urlReferrer,
        NameValueCollection formParams, 
        NameValueCollection queryStringParams,
        HttpCookieCollection cookies, 
        NameValueCollection serverVariables,
        NameValueCollection headers)
        : this(relativeUrl, httpMethod, formParams, queryStringParams, cookies, serverVariables, headers)
    {
        _url = url;
        _urlReferrer = urlReferrer;
        _headers = headers;
    }

    public OfflineHttpRequest(
        string relativeUrl, 
        Uri url, 
        Uri urlReferrer)
        : this(relativeUrl, HttpVerbs.Get.ToString("g"), url, urlReferrer, null, null, null, null, null)
    {
    }

    public override NameValueCollection ServerVariables
    {
        get
        {
            return _serverVariables;
        }
    }

    public override NameValueCollection Form
    {
        get { return _formParams; }
    }

    public override NameValueCollection QueryString
    {
        get { return _queryStringParams; }
    }

    public override NameValueCollection Headers
    {
        get { return _headers; }
    }

    public override HttpCookieCollection Cookies
    {
        get { return _cookies; }
    }

    public override string AppRelativeCurrentExecutionFilePath
    {
        get { return _relativeUrl; }
    }

    public override Uri Url
    {
        get
        {
            return _url;
        }
    }

    public override Uri UrlReferrer
    {
        get
        {
            return _urlReferrer;
        }
    }

    public override string Path
    {
        get
        {
            if (_relativeUrl != null && _relativeUrl.StartsWith("~/"))
            { 
                return _relativeUrl.Remove(0, 1);
            }
            return null;
        }
    }

    public override string PathInfo
    {
        get
        {
            return "";
        }
    }

    public override string PhysicalApplicationPath
    {
        get
        {
            return _serverVariables.Get("APPL_PHYSICAL_PATH");
        }
    }

    public override string PhysicalPath
    {
        get
        {
            var file = "";
            if (Path != null && Path.StartsWith("/"))
            {
                file = Path.Remove(0, 1);
            }
            if (PhysicalApplicationPath != null)
            {
                return PhysicalApplicationPath + "\\" + file.Replace('/', '\\');
            }
            return null;
        }
    }

    public override string HttpMethod
    {
        get
        {
            return _httpMethod;
        }
    }

    public override string UserHostAddress
    {
        get { return null; }
    }

    public override string RawUrl
    {
        get { return null; }
    }

    public override bool IsSecureConnection
    {
        get { return false; }
    }

    public override bool IsAuthenticated
    {
        get
        {
            return false;
        }
    }

    public override string UserAgent
    {
        get
        {
            var agent = _headers.Get("User-Agent") ??
                           "Mozilla/5.0+(compatible;+Googlebot/2.1;++http://www.google.com/bot.html)";
            return agent;
        }
    }

    public override HttpBrowserCapabilitiesBase Browser
    {
        get
        {
            var browser = new HttpBrowserCapabilities
            {
                Capabilities = new Hashtable { { string.Empty, UserAgent } }
            };
            var factory = new BrowserCapabilitiesFactory();
            factory.ConfigureBrowserCapabilities(new NameValueCollection(), browser);

            return new HttpBrowserCapabilitiesWrapper(browser);
        }
    }
}

【问题讨论】:

  • 所以这就是我正在尝试使用的,但是抛出了一个与 VirtualPath 为空相关的异常。当我在这里使用我的假上下文时: ... ViewEngines.Engines.FindPartialView(offlineControllerContext, viewName); ...在后台 VirtualPathProvider 使用的是 ASP.Net HttpContext,而不是我的假上下文。

标签: c# asp.net-mvc partial-views web-parts httpcontext


【解决方案1】:

这是我想在 Web 表单页面中呈现 PartialViews 或 ChildActions 时使用的 MvcUtility 类,但是我认为我没有在 UserControl 中使用它。

不确定您使用的是哪个 MVC 版本,但我知道这适用于 MVC 3 和 Razor Views。

public static class MvcUtility
{
        public static void RenderPartial(string partialViewName, object model)
        {
            // Get the HttpContext
            HttpContextBase httpContextBase = new HttpContextWrapper(HttpContext.Current);
            // Build the route data, pointing to the Some controller
            RouteData routeData = new RouteData();
            routeData.Values.Add("controller", typeof(Controller).Name);
            // Create the controller context
            ControllerContext controllerContext = new ControllerContext(new RequestContext(httpContextBase, routeData), new Controller());
            // Find the partial view
            IView view = FindPartialView(controllerContext, partialViewName);
            // create the view context and pass in the model
            ViewContext viewContext = new ViewContext(controllerContext, view, new ViewDataDictionary { Model = model }, new TempDataDictionary(), httpContextBase.Response.Output);
            // finally, render the view
            view.Render(viewContext, httpContextBase.Response.Output);
        }

        private static IView FindPartialView(ControllerContext controllerContext, string partialViewName)
        {
            // try to find the partial view
            ViewEngineResult result = ViewEngines.Engines.FindPartialView(controllerContext, partialViewName);
            if (result.View != null)
            {
                return result.View;
            }
            // wasn't found - construct error message
            StringBuilder locationsText = new StringBuilder();
            foreach (string location in result.SearchedLocations)
            {
                locationsText.AppendLine();
                locationsText.Append(location);
            }
            throw new InvalidOperationException(String.Format("Partial view {0} not found. Locations Searched: {1}", partialViewName, locationsText));
        }

        public static void RenderAction(string controllerName, string actionName, object routeValues)
        {
            RenderPartial("RenderActionUtil", new RenderActionVM() { ControllerName = controllerName, ActionName = actionName, RouteValues = routeValues });
        }
    }

要呈现 ChildAction,您将需要共享 MVC 视图文件夹中的局部视图:

@model YourNamespace.RenderActionVM

@{
    Html.RenderAction(Model.ActionName, Model.ControllerName, Model.RouteValues);
}

还有视图模型:

public class RenderActionVM
{
    public string ControllerName { get; set; }
    public string ActionName { get; set; }
    public object RouteValues { get; set; }
}

最后在你的 webforms 页面调用如下:

<% MvcUtility.RenderPartial("_SomePartial", null); %> 

<% MvcUtility.RenderAction("SomeController", "SomeAction", new { accountID = Request.QueryString["id"], dateTime = DateTime.Now }); %>

【讨论】:

  • 谢谢,但在我的场景中 HttpContext.Current 会抛出异常,因为 HttpContext 为空。这就是我试图伪造它的原因。
猜你喜欢
  • 2014-02-14
  • 2016-04-26
  • 1970-01-01
  • 2011-12-11
  • 2018-08-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-04-02
相关资源
最近更新 更多