ASP.NET MVC 在控制器中获取某个视图动态的HTML代码
如果我们需要动态的用AJAX从服务器端获取HTML代码,拼接字符串是一种不好的方式,所以我们将HTML代码写在cshtml文件中,然后通过代码传入model,动态获取cshtml中的HTML代码
当然,我们想要使用通用的方法去获取cshtml,就必须重写RazorViewEngine视图引擎,配置视图搜索位置
在查找一个视图时,Razor视图引擎遵循了MVC框架早期版本建立起来的约定。例如,如果你请求与Home控制器相关的Index视图,Razor会审查这样的视图列表:
~/Views/Home/Index.cshtml
● ~/Views/Home/Index.vbhtml
● ~/Views/Shared/Index.cshtml
● ~/Views/Shared/Index.vbhtml
正如你现在知道的,Razor实际上不会在磁盘上查找这些视图文件,因为它们还没有被编译成C#类。Razor查找的是表示这些视图的编译类。.cshtml文件是含有C#语句的模板(我们正在使用的这种),而.vbhtml文件含有Visual Basic语句。
你可以通过生成一个RazorViewEngine子类,来改变Razor搜索的这种视图文件。这个类是Razor的IViewEngine实现。它建立于一组基类之上,这些类定义一组用来确定搜索哪种视图文件的属性。这些属性如表所描述。
Property 属性 |
Description 描述 |
Default Value 默认值 |
ViewLocationFormats MasterLocationFormats PartialViewLocationFormats |
The locations to look for views, partial views, and layouts 查找视图、分部视图、以及布局的位置 |
"~/Views/{1}/{0}.cshtml", "~/Views/{1}/{0}.vbhtml", "~/Views/Shared/{0}.cshtml", "~/Views/Shared/{0}.vbhtml" |
AreaViewLocationFormats AreaMasterLocationFormats AreaPartialViewLocationFormats |
The locations to look for views, partial views, and layouts for an area 查找一个区域的视图、分部视图、及布局的位置 |
"~/Areas/{2}/Views/{1}/{0}.cshtml", "~/Areas/{2}/Views/{1}/{0}.vbhtml", "~/Areas/{2}/Views/Shared/{0}.cshtml", "~/Areas/{2}/Views/Shared/{0}.vbhtml" |
这些属性先于Razor的引入,这是每组三个属性具有相同值的原因。每个属性是一个字符串数组,它们是用复合字符串格式化符号来表示的。以下是与占位符对应的参数值:
● {0} represents the name of the view.
{0} 表示视图名
● {1} represents the name of the controller.
{1} 表示控制器名
● {2} represents the name of the area.
{2} 表示区域名
为了修改搜索位置,你要生成一个派生于RazorViewEngine的新类,并修改表所描述的一个或多个属性值。
在Infrastructure文件夹中新建一个CustomRazorViewEngine类
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; namespace MvcApplication1.Infrastructure { public class CustomRazorViewEngine : RazorViewEngine { public CustomRazorViewEngine() { ViewLocationFormats = new string[] { "~/Views/{1}/{0}.cshtml", "~/Views/Shared/{0}.cshtml", "~/Views/Shared_PartialView/{0}.cshtml"//指定查找某个文件的路径 }; PartialViewLocationFormats = new string[] { "~/Views/{1}/{0}.cshtml", "~/Views/Shared/{0}.cshtml", "~/Views/Shared_PartialView/{0}.cshtml"////指定查找某个文件的路径 }; } } }
我们在Global.asax的Application_Start方法中,用ViewEngines.Engines集合来注册我们的这个派生视图引擎,像这样:
protected void Application_Start() { AreaRegistration.RegisterAllAreas(); ViewEngines.Engines.Clear(); ViewEngines.Engines.Add(new CustomRazorViewEngine()); WebApiConfig.Register(GlobalConfiguration.Configuration); FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); RouteConfig.RegisterRoutes(RouteTable.Routes); BundleConfig.RegisterBundles(BundleTable.Bundles); }
获取html字符串的方法以及如何调用
public class HomeController : Controller { // // GET: /Home/ public ActionResult Index() { string html = this.ControllerContext.RenderViewToString("_CommonPartial", new UserViewModel() { UserName="haha"}); return View(new UserViewModel() { IsEnable = false, UserCode = "aa" }); } } public static class HelperExtensions { public static string RenderViewToString(this ControllerContext context, string viewName, object model) { if (string.IsNullOrEmpty(viewName)) viewName = context.RouteData.GetRequiredString("action"); context.Controller.ViewData.Model = model; using (var sw = new StringWriter()) { ViewEngineResult viewResult = ViewEngines.Engines.FindPartialView(context, viewName); var viewContext = new ViewContext(context, viewResult.View, context.Controller.ViewData, context.Controller.TempData, sw); try { viewResult.View.Render(viewContext, sw); } catch (Exception ex) { throw; } return sw.GetStringBuilder().ToString(); } } }
ASP.NET MVC学习之路由篇(2)
继ASP.NET MVC学习之路由篇(1)后继续学习。
7.解决与物理路径的冲突
当发送一个请求至ASP.NET MVC时,其实会检查网站中存不存在这个请求的物理路径文件,如果存在的话,就会直接将这个物理文件返回。但是有时候我们需要它执行控制器的某个方法,而不是直接将这个物理文件返回。那么我们就需要这节知识。下面我们先在网站根目录中新建一个 Test.html ,在其中随便写上一些内容,然后访问。再在RouteConfig.cs中写入如下代码:
1 public class RouteConfig 2 { 3 public static void RegisterRoutes(RouteCollection routes) 4 { 5 routes.RouteExistingFiles = true; 6 7 routes.MapRoute( 8 name: "Default2", 9 url: "Test.html", 10 defaults: new { controller = "Home", action = "List" } 11 ); 12 } 13 }
这个时候我们再重新刷新浏览器,那么我们就可以看到控制器返回的结果了,这样我们就解决了物理路径和路由之间的冲突问题了。
8.绕过路由系统
如果我们有一些URL路径不希望通过路由系统,那么我们就可以利用这节知识。
下面是RouteConfig.cs的内容:
1 public class RouteConfig 2 { 3 public static void RegisterRoutes(RouteCollection routes) 4 { 5 routes.IgnoreRoute("webResources.axd{*pathinfo}"); 6 7 routes.MapRoute( 8 name: "Default2", 9 url: "Test.html", 10 defaults: new { controller = "Home", action = "List" } 11 ); 12 } 13 }
9.自定义路由系统
如果上面的技术还无法解决你的问题,那么我们下面将会介绍如何自定义路由系统。自定义路由系统只需要继承RouteBase并实现两个方法,如下:
(1):GetRouteData
(2):GetVirtualPath
下面是我写的一个简单示例,用来判断访问者是否是移动设备,如果是移动设备则路由到前缀为M的控制器处理请求,否则返回NULL交由默认的路由
处理,下面为我的源码:
1 public class CustomRouteBase : RouteBase 2 { 3 private List<String> userAgent; 4 5 public CustomRouteBase(params String[] userAgents) 6 { 7 userAgent = userAgents.ToList(); 8 } 9 10 public override RouteData GetRouteData(HttpContextBase httpContext) 11 { 12 RouteData rd = new RouteData(this, new MvcRouteHandler()); 13 Regex r = new Regex(@"/(\w+)", RegexOptions.IgnoreCase); 14 MatchCollection mc = r.Matches(httpContext.Request.Path); 15 String agent = httpContext.Request.UserAgent.ToLower(); 16 foreach (String item in userAgent) 17 { 18 if (agent.Contains(item)) 19 { 20 if (mc.Count >= 2) 21 { 22 rd.Values.Add("controller", "M" + mc[0].Groups[1].Value.ToString()); 23 rd.Values.Add("action", mc[1].Groups[1].Value.ToString()); 24 } 25 else 26 { 27 rd.Values.Add("controller", "MHome"); 28 rd.Values.Add("action", "Index"); 29 } 30 return rd; 31 } 32 } 33 return null; 34 } 35 36 public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values) 37 { 38 return null; 39 } 40 }
最后我们将该自定义路由添加进来(RouteConfig.cs):
1 public class RouteConfig 2 { 3 public static void RegisterRoutes(RouteCollection routes) 4 { 5 routes.Add(new CustomRouteBase("iphone", "ipad", "android")); 6 7 routes.MapRoute( 8 name: "Default2", 9 url: "Test.html", 10 defaults: new { controller = "Home", action = "List" } 11 ); 12 } 13 }
现在你新建一个MHome的控制器,然后访问看看(建议使用Google浏览器,并通过开发者工具改变UserAgent即可快速看到效果)。
10.自定义路由处理程序
或许你会觉得ASP.NET MVC的控制器太麻烦,相比而言有些功能你更希望使用一般处理程序。但是访问的时候必须使用物理路径又发觉麻烦,那么通过学习这节,你将可以将一般处理程序也加入到路由中,并且可控性很强。
首先我们必须新建一个实现IRouteHandler接口的类:
1 public class DonwloadHandler : IRouteHandler 2 { 3 4 public IHttpHandler GetHttpHandler(RequestContext requestContext) 5 { 6 return new CustomHandler(); 7 } 8 } 9 10 public class CustomHandler : IHttpHandler 11 { 12 13 public bool IsReusable 14 { 15 get { return false; } 16 } 17 18 public void ProcessRequest(HttpContext context) 19 { 20 context.Response.Write("download model"); 21 } 22 }
你们可以看到上面的源码,我是直接在下面新建了一个实现IHttpHandler的类,并且在GetHttpHandler中将该类的实例返回,当然你还可以在这个方法中进行判断以便根据实际情况交由不同的一般处理程序去处理请求。
下面就是将这个路由处理程序添加到路由中,这里我们映射到MyTest这个路径:
1 public class RouteConfig 2 { 3 public static void RegisterRoutes(RouteCollection routes) 4 { 5 routes.Add(new Route("MyTest",new DonwloadHandler())); 6 7 routes.MapRoute( 8 name: "Default2", 9 url: "Test.html", 10 defaults: new { controller = "Home", action = "List" } 11 ); 12 } 13 }
接着我们访问http://localhost:2392/MyTest就可以看到结果了。