ASP.NET MVC中Area的另一种用法
ASP.NET MVC中Area的另一种用法
【摘要】本文只是为一行代码而分享
context.MapRoute("API", "api/{controller}/{action}", new { }, newstring[] { "CNBlogs.UcHome.Web.Controllers.Api" });
我们在ASP.NET MVC中使用Area时通常这么干:
在Web项目中创建Areas文件夹,在其中创建对应的Area文件夹,在其下创建Controllers文件夹。然后在Area文件夹中创建AreaRegistration的子类用于注册Area路由,在Controllers文件夹中创建所需的Controller。
这么干有个前提,就是你的Web项目类型要是WebApplication。
而我们所处的场景是:Web项目类型是WebSite。之前在使用MVC时,将所有的Controller放在了一个单独的类库项目中。
我们今天有一个需求,需要用area来解决。为什么要用area?举个例子来说明。
比如我们在页面中添加网摘时,访问的网址是 home.cnblogs.com/wz/add ,而供其他应用调用网摘API的网址是 home.cnblogs.com/api/wz/add 。这里的wz对应的控制器名称都是WzController,Action都是add,实际也的确存在两个WzController,放在不同的文件夹中。如上图,一个在项目根文件夹,一个在Api文件夹。所以在网址中通过api前缀路径来区分,在程序中也要让ASP.NET路由找到对应的Controller。
这时Area就发挥作用了,但由于Conroller不在Web项目中,所以要找其他方法解决这个问题。方法来自Migrating a large web application to ASP.NET MVC(关键在最后一行代码):
When AreaRegistration.RegisterAllAreas() is called, ASP.NET will search for all AreaRegistration subclasses in all assemblies in the bin dir so that we do not have to modify the global.asax when we add new areas.
In the AreaRegistration subclass, we would do something like:
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"Services",
"Sub1/Sub11/Sub111/{controller}.mvc/{action}/{id}",
new { action = "Index", id = "" },
new string[] { "My.Custom.Namespace.Controllers" }
);
}
AreaRegistration.RegisterAllAreas()是global.asax中调用的,它会找到所有的AreaRegistration的子类,不管是在Web项目中,还是在其他类库项目中。所以我们在CNBlogs.UcHome.Web.Controllers项目的Api文件夹中放一个AreaRegistration的子类,也是能被找到的,然后在注册Area时,在参数中传递Controller所在的命名空间,问题就解决了。
【更新】
虽然通过ApiArea解决了api的路由问题,但是这时我们访问非api的路径(比如/wz/my),出现下面的错误:
Multiple types were found that match the controller named 'wz'. This can happen if the route that services this request ('{controller}/{action}/{id}') does not specify namespaces to search for a controller that matches the request. If this is the case, register this route by calling an overload of the 'MapRoute' method that takes a 'namespaces' parameter.
The request for 'wz' has found the following matching controllers:
CNBlogs.UcHome.Web.Controllers.Api.WzController
CNBlogs.UcHome.Web.Controllers.WzController
也就是说,如果有两个同名的Controller,必须在路由时指定命名空间,routes.MapRoute的第5个参数就是用于指定命名空间,我开始时不知道有这个参数,于是试图用Area来解决问题,实际是一个错误的解决方法,正确的解决方法是在Global.asax中针对不同的命名添加两个不同的路由,示例代码如下:

routes.MapRoute("API",
"api/{controller}/{action}",
new { },
null,
new string[] { "CNBlogs.UcHome.Web.Controllers.Api" }
); routes.MapRoute("DefaultMvc",
"{controller}/{action}/{id}",
new { id = UrlParameter.Optional },
null,
new string[] { "CNBlogs.UcHome.Web.Controllers" }
);

ASP.NET MVC允许使用 Area(区域)来组织Web应用程序,每个Area代表应用程序的不同功能模块。这对于大的工程非常有用,Area 使每个功能模块都有各自的文件夹,文件夹中有自己的Controller、View和Model,但对于管理也增加了一定的难度。
本文目录
创建Area
右键工程选择 添加->区域,弹出如下填写Area的对话框:
点击添加后,工程目录结构如下:
和创建一个空MVC工程结构类似,Admin Area 有自己的 Controllers、Models 和 Views 文件夹,不一样的地方就是多了一个 AdminAreaRegistration.cs 文件,这个文件中定义了一个叫 AdminAreaRegistration 的类,它的内容如下:
namespace MvcApplication1.Areas.Admin {
public class AdminAreaRegistration : AreaRegistration {
public override string AreaName {
get {
return "Admin";
}
} public override void RegisterArea(AreaRegistrationContext context) {
context.MapRoute(
"Admin_default",
"Admin/{controller}/{action}/{id}",
new { action = "Index", id = UrlParameter.Optional }
);
}
}
}
系统自动生成的 AdminAreaRegistration 类继承至抽象类 AreaRegistration,并重写了 AreaName 属性和 RegisterArea 方法。在 RegisterArea 方法中它为我们定义了一个默认路由,我们也可在这个方法中定义专属于Admin Area的的其他路由。但有一点要注意,在这如果要给路由起名字,一定要确保它和整个应用程序不一样。
AreaRegistrationContext 类的 MapRoute 方法和 RouteCollection 类的 MapRoute 方法的使用是一样的,只是 AreaRegistrationContext 类限制了注册的路由只会去匹配当前 Area 的 controller,所以,如果你把在 Area 中添加的 controller 的默认命名空间改了,路由系统将找不到这个controller 。
RegisterArea 方法不需要我们手动去调用,在 Global.asax 中的 Application_Start 方法已经有下面这样一句代码为我们做好了这件事:
protected void Application_Start() {
AreaRegistration.RegisterAllAreas(); WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
调用 AreaRegistration.RegisterAllAreas 方法让MVC应用程序在启动后会寻找所有继承自 AreaRegistration 的类,并为每个这样的类调用它们的 RegisterArea 方法。
注意:不要轻易改变 Application_Start 中注册方法的顺序,如果你把RouteConfig.RegisterRoutes方法放到AreaRegistration.RegisterAllAreas方法之前,Area 路由的注册将会在路由注册之后,路由系统是按顺序来匹配的,所以这样做会让请求 Area 的 Controller 匹配到错误的路由。
Area的运行
在Area中添加controller、view和model和一般的添加是一样的。在这,我们在Admin Area中添加一个名为 Home 的controller,代码如下:
public class HomeController : Controller { public ActionResult Index() {
return View();
}
}
然后我们再为Index Acton添加一个View,代码如下:
@{
ViewBag.Title = "Index";
Layout = null;
} <!DOCTYPE html> <html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Index</title>
</head>
<body>
<div>
<h2>Admin Area Index</h2>
</div>
</body>
</html>
运行应用程序,然后将URL定位到/Admin/Home/Index,下面是运行结果:
到这,我们已经看到,Area中的的工作流程其实就是和根目录下的流程是一样的。但Area并不是一个完全独立的工作空间,我们下面来看看。
Controller的歧义问题
试想一下,如果我们现在在根目录的 Controller 文件夹中也添加一个名为 Home 的 Controller,然后我们通过把URL定位到 /Home/Index,路由系统能匹配到根目录下的 Controller 吗?
在根目录的 Controllers 文件夹中添加好 HomeController 后,为Index添加View,内容随意:
...
<body>
<div>
<h2>Root Index</h2>
</div>
</body>
...
路由不改动,我们使用 RouteConfig.cs 文件中系统定义的默认路由:
public static void RegisterRoutes(RouteCollection routes) {
routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
运行程序,将URL定位到 /Home/Index。结果我们会看到如下错误信息:
出现这个问题是因为路由系统进行匹配的时候出现了Controller同名的歧义。
当Area被注册的时候,Area中定义的路由被限制了只寻找 Area 中的Controller,所以我们请求 /Admin/Home/Index 时能正常得到 MvcApplication1.Areas.Admin.Controllers 命名空间的 HomeController。然而我们在RouteConfig.cs文件的RegisterRoutes方法中定义的路由并没有类似的限制。
为了解决这个问题,我们需要在RouteConfig.cs文件中定义的路由中加上对应的 namespaces 参数。RouteConfig.cs 中修改后的路由如下:
public static void RegisterRoutes(RouteCollection routes) {
routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional },
namespaces: new[] { "MvcApplication1.Controllers" }
);
}
运行程序,如下结果说明解决了同名歧义问题:
添加了 namespaces 参数后,路由系统在对这个路由进行匹配时,优先匹配指定命名空间的controller,如果匹配到则即刻停止查找,如果在指定的命名空间下没有匹配到对应的controller,再按照一般的方式进行匹配。
生成Area URL链接
关于Area的URL链接生成,可以分为这么三种情况:第一种是在当前Area生成指向当前Area的链接;第二种是生成指向其他Area的链接;第三种是在某个Area中生成指向根目录的链接。下面是这三种情况生成链接的方法,使用的路由定义是系统默认的。
如果要在Area中生成当前Area的URL链接,直接用下面的方法就行:
@Html.ActionLink("Click me", "About")
它根据当前所在的Area和Controller会生成如下Html代码:
<a href="/Admin/Home/About">Click me</a>
如果要生成其他Area的URL链接,则需要在Html.ActionLink方法的匿名参数中使用一个名为area的变量来指定要生成链接的Area名称,如下:
@Html.ActionLink("Click me to go to another area", "Index", new { area = "Support" })
它会根据被指定的Area去找路由的定义,假定在Support Area中定义了对应的路由,那么它会生成如下链接:
<a href="/Support/Home/Index">Click me to go to another area</a>
如果要在当前Area生成指根目录某个controller的链接,那么只要把area变量置成空字符串就行,如下:
@Html.ActionLink("Click me to go to top-level part", "Index", new { area = "" })
它会生成如下Html链接:
<a href="/Home/Index">Click me to go to top-level part</a>
参考:《Pro ASP.NET MVC 4 4th Edition》
ASP.NET MVC中Area的另一种用法的更多相关文章
- MVC中Area的另一种用法
[摘要]本文只是为一行代码而分享 context.MapRoute("API", "api/{controller}/{action}", new { }, n ...
- 如何在asp.net mvc中添加自定义的HTML辅助种方法
很久没在博客园发表文章了,今天来总结一下如何在asp.net mvc中添加自定义的HTML辅助方法.我们现在设计这么一个目前,利用自定义的HTML方法来渲染一个普通的img标记.直接进入主题吧: 首先 ...
- ASP.NET MVC教程四:ASP.NET MVC中页面传值的几种方式
准备 在Models文件夹里面新添加Student实体类,用来模拟从Controller向View传递数据,Student类定义如下: using System; using System.Colle ...
- ASP.NET MVC 中@html.ActionLink的几种参数格式
一 Html.ActionLink("linkText","actionName") 该重载的第一个参数是该链接要显示的文字,第二个参数是对应的控制器的方法, ...
- Asp.net mvc 中View 的呈现(二)
[toc] 上一节介绍了 Asp.net mvc 中除 ViewResult 外的所有的 ActionResult,这一节介绍 ViewResult. ViewResultBase ViewResul ...
- 在ASP.NET MVC中使用Area
前言: 这段时间小猪花了不少功夫在研究ASP.NET MVC的源码上面,可谓思想是了解了不少,用的上用不上却是另外一回事了.! 应用场景: ASP.NET MVC中,是依靠某些文件夹以及类的固定命名规 ...
- 在ASP.Net MVC 中,如何在Global.asax中配置一个指向Area内部的默认Route
ASP.Net MVC 中配置Route的时候可以设置一个默认的Route. 比如我要在输入http://localhost的时候默认进入http://localhost/home/index.可以在 ...
- ASP.NET没有魔法——ASP.NET MVC使用Area开发一个管理模块
之前的My Blog提供了列表的展示和文章显示功能,但是所有数据仍然只能通过数据库录入,为了完成最初的角色“作者”的用例,本章将介绍如何使用“Area”实现My Blog的管理功能. 根据功能分离代码 ...
- 2.ASP.NET MVC 中使用Crystal Report水晶报表
上一篇,介绍了怎么导出Excel文件,这篇文章介绍在ASP.NET MVC中使用水晶报表. 项目源码下载:https://github.com/caofangsheng93/CrystalReport ...
随机推荐
- IP地址解析
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.N ...
- 最新Oracle 和 mysql 的对比参照----开发篇(转)
Oracle mysql 对比版本 Release 10.2.0.1.0 XE windowsXP 5.0.45-community-nt-log MySQL Community Edition ( ...
- Mac OS X中报:java.io.UnixFileSystem.createFileExclusively(Native Method)的简单原因
这个博客太简单了!想到可能有其它朋友也遇到这个问题,就记录一下. 今天把一个之前在Windows上的Java项目放到Mac OS X上执行,本来认为应该非常easy的事情,结果还是报: Excepti ...
- ASP.NET MVC+EF框架+EasyUI实现权限管理系列(6)- EF上下文实例管理
原文:ASP.NET MVC+EF框架+EasyUI实现权限管理系列(6)- EF上下文实例管理 ASP.NET MVC+EF框架+EasyUI实现权限管系列 (开篇) (1):框架搭建 ( ...
- VPS(Virtual Private Server 虚拟专用服务器)[转自百度]
系统选择 vps上常用的操作系统是linux(有多种发行版).freeBSD,windows server等.一般来说,vps的操作系统不是自由安装的,linux系列vps可以安装多个linux发行版 ...
- SD3.0四个协议解读
前面的文章提到过SD卡主要分为两个操作模式,一是初始化和识别操作模式.还有一种就是这篇文章须要分析的传输数据模式啦. 传输数据模式: 传输数据模式主要有六种状态,各自是Stand-by状态.Trans ...
- android 实现真正意义上的服务及源代码下载
android APP后台服务和长期server长期互动,保证实时数据,这个小项目主要实施app退出后仍然能够执行服务. 使用该系统的Intent.ACTION_TIME_TICK现,这个系统的广播每 ...
- css3布局相关(持续更新)
1三栏布局,两边定宽,中间自适应 2让文字位于div元素的正中央 3不管浏览器窗口如何变化,让一张图片始终显示在浏览器正中央.
- cocos2d-x 颜色
ccBlendFunc cbl = {GL_SRC_ALPHA, GL_ONE}; Sprite *sprite = Sprite::create("128_00002.png") ...
- 解决OUTLOOK 533错误问题
OutLook中“553 sorry, that domain isn‘t in my list of allowed rcpthosts (#5.7.1)”,无法发送邮件错误,解决方法 最近我在给徐 ...