-
C#中关于ASP.NET Core MVC——项目创建与路由配置
第三部分:Web应用开发
-
ASP.NET Core MVC——项目创建与路由配置
实例介绍
之前写电商Web接口时,路由全靠硬编码:要访问订单列表得写/api/Order/GetList,改个功能要改URL、控制器、前端请求三处,烦得要死;用户反馈“想找后台管理页面,输/admin跳404”,SEO也差,搜索引擎爬不到/Product/Details?id=123这种vb.net教程C#教程python教程SQL教程access 2010教程
动态参数URL。后来用ASP.NET Core MVC重构:路由用模板统一配置,/admin/orders直接映射到Admin区域的Orders控制器,/product/123这种RESTful URL既友好又利于SEO,改路由只要改模板,全项目生效。这节就带你从零创建MVC项目,搞定路由配置的核心痛点。
需求分析
ASP.NET Core MVC的路由要解决“URL友好、结构清晰、易于维护”的问题,具体需求如下:
1.项目结构规范:Controllers(控制器)、Views(视图)、Models(模型)分层,代码不混乱;
2.友好路由:支持RESTful风格URL(如/products/123代替/Product/Details?id=123);
3.参数传递:路由参数(如/products/123)、查询参数(如/products?category=phone)都能处理;
4.区域路由:前台和后台(Admin)代码分离,路由自动加/admin前缀;
5.自定义路由:支持SEO友好的URL(如/product/iphone-15);
6.路由约束:限制参数类型(如ID必须是数字),避免无效请求;
7.错误处理:路由匹配失败时返回友好的404页面;
8.热重载:修改代码后无需重启项目,直接刷新页面生效。
代码实现
前置条件:.NET 6+ SDK、VS 2022(或VS Code);需掌握C#基础、HTTP协议基本概念。
场景1:创建ASP.NET Core MVC项目
两种创建方式:VS图形界面(适合新手)和命令行(高效)。
方式1:VS 2022创建项目
1.打开VS 2022 → 新建项目 → 搜索“ASP.NET Core Web App (Model-View-Controller)”;
2.输入项目名称(如ECommerceWeb)→ 选择保存路径;
3.框架选择“.NET 6(长期支持)”或更高版本 → 勾选“启用热重载” → 创建。
方式2:命令行创建项目
打开终端(CMD/PowerShell),执行以下命令:
bash
# 创建MVC项目
dotnet new mvc -n ECommerceWeb
# 进入项目目录
cd ECommerceWeb
# 运行项目
dotnet run
运行后访问https://localhost:5001或http://localhost:5000,看到默认的Home页面即创建成功。
场景2:基础路由配置(默认路由+RESTful路由)
.NET 6+的MVC项目路由配置在Program.cs中(之前版本在Startup.cs),核心是MapControllerRoute和MapControllers。
Program.cs完整代码
csharp
var builder = WebApplication.CreateBuilder(args);
// 1. 添加MVC服务(控制器+视图),启用Razor热重载(修改视图无需重启)
builder.Services.AddControllersWithViews()
.AddRazorRuntimeCompilation(); // 开发环境启用热重载,生产环境自动禁用
var app = builder.Build();
// 2. 开发环境启用异常页面(方便调试)
if (app.Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error"); // 生产环境异常跳转错误页
app.UseHsts(); // 强制HTTPS
}
app.UseHttpsRedirection(); // HTTP重定向到HTTPS
app.UseStaticFiles(); // 启用静态文件(如CSS、JS、图片)
app.UseRouting(); // 启用路由中间件(解析URL)
app.UseAuthorization(); // 启用授权(后面讲身份验证时用)
// 3. 配置路由端点(匹配URL到控制器动作)
app.MapControllerRoute(
name: "default", // 路由名称(唯一)
pattern: "{controller=Home}/{action=Index}/{id?}", // 路由模板
defaults: new { controller = "Home", action = "Index" } // 默认值(可选,和模板里的=等效)
);
// 4. 启用RESTful API路由(如果需要写API接口)
// app.MapControllers(); // 匹配控制器上的[Route]特性
app.Run(); // 启动Web服务器
Home控制器(Controllers/HomeController.cs)
csharp
using Microsoft.AspNetCore.Mvc;
namespace ECommerceWeb.Controllers
{
public class HomeController : Controller
{
// 默认动作:对应URL /Home/Index 或 /(因为默认路由的controller=Home,action=Index)
public IActionResult Index()
{
return View(); // 返回Views/Home/Index.cshtml视图
}
// 带参数的动作:对应URL /Home/Details/123 或 /Home/Details?id=123
public IActionResult Details(int? id) // id是可选参数(因为路由模板里的id?)
{
if (id == null)
{
return NotFound(); // 返回404
}
ViewBag.ProductId = id;
return View();
}
}
}
场景3:区域路由(Admin后台分离)
当项目有前台和后台(Admin)两个模块时,用区域(Area)分离代码,路由自动加/admin前缀。
步骤1:创建Admin区域
1.右键项目 → Add → Area → 输入名称Admin → 确定;
2.自动生成Areas/Admin目录,包含Controllers、Views、Models子目录;
步骤2:Admin区域的Home控制器(Areas/Admin/Controllers/HomeController.cs)
csharp
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace ECommerceWeb.Areas.Admin.Controllers
{
[Area("Admin")] // 标记为Admin区域的控制器
public class HomeController : Controller
{
// 对应URL /Admin/Home/Index 或 /Admin(因为区域路由的默认值)
public IActionResult Index()
{
return View(); // 返回Areas/Admin/Views/Home/Index.cshtml视图
}
// 对应URL /Admin/Orders/List
public IActionResult List()
{
return View();
}
}
}
步骤3:Program.cs中配置区域路由
在app.MapControllerRoute之前添加区域路由(路由匹配顺序是从上到下,所以区域路由要放在默认路由前面):
csharp
// 配置Admin区域路由
app.MapAreaControllerRoute(
name: "Admin", // 路由名称
areaName: "Admin", // 区域名称
pattern: "Admin/{controller=Home}/{action=Index}/{id?}", // 路由模板,自动加/Admin前缀
defaults: new { controller = "Home", action = "Index" }
);
// 原来的默认路由
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}"
);
场景4:自定义路由与路由约束
实现SEO友好的URL(如/product/iphone-15),并限制参数类型(如ID必须是数字)。
方式1:控制器动作上的自定义路由
csharp
public class ProductController : Controller
{
// 自定义路由:匹配 /product/{slug}(slug是产品别名,如iphone-15)
[Route("product/{slug}")] // 路由模板,优先级高于默认路由
public IActionResult DetailsBySlug(string slug)
{
ViewBag.ProductSlug = slug;
return View();
}
// 路由约束:ID必须是整数,匹配 /product/123,不匹配 /product/abc
[Route("product/{id:int}")] // :int是路由约束,限制id为整数
public IActionResult DetailsById(int id)
{
ViewBag.ProductId = id;
return View();
}
}
方式2:全局自定义路由(Program.cs)
csharp
app.MapControllerRoute(
name: "product-slug",
pattern: "product/{slug}", // 全局路由模板
defaults: new { controller = "Product", action = "DetailsBySlug" }
);
常用路由约束
约束类型 作用说明 示例
:int 匹配整数 {id:int}
:string 匹配非空字符串(默认) {slug:string}
:guid 匹配GUID {id:guid}
:regex(^\d{4}$) 匹配正则表达式(4位数字) {year:regex(^\d{4}$)}
:min(1) 匹配大于等于1的整数 {id:min(1)}
:max(100) 匹配小于等于100的整数 {id:max(100)}
场景5:404错误处理
路由匹配失败时,返回友好的404页面。
步骤1:Home控制器添加Error动作
csharp
// 对应URL /Home/Error/404
public IActionResult Error(int? statusCode)
{
ViewBag.StatusCode = statusCode ?? 500; // 默认500
return View();
}
步骤2:Views/Home/Error.cshtml视图
html
@{
ViewData["Title"] = "错误页面";
}
<div class="container mt-5">
<h1 class="text-danger">@ViewBag.StatusCode 错误</h1>
<p class="lead">
@(ViewBag.StatusCode == 404 ? "抱歉,您访问的页面不存在!" : "服务器内部错误,请稍后重试!")
</p>
<a href="/" class="btn btn-primary">返回首页</a>
</div>
步骤3:Program.cs配置404跳转
在app.UseExceptionHandler后面添加:
csharp
app.UseStatusCodePagesWithReExecute("/Home/Error/{0}"); // 状态码跳转,{0}是状态码(如404)
逐行讲解
场景2:基础路由核心代码
1.AddControllersWithViews():添加MVC的控制器和视图服务,AddRazorRuntimeCompilation()启用Razor视图热重载(开发环境修改.cshtml后直接刷新页面生效,无需重启项目);
2.UseDeveloperExceptionPage():开发环境显示详细异常页面,包含路由匹配信息、堆栈跟踪,方便调试;
3.UseRouting():启用路由中间件,负责解析URL、匹配路由模板;
4.MapControllerRoute():配置默认路由,参数说明:
1.name:路由名称(唯一,用于生成URL时指定路由);
2.pattern:路由模板,{controller}、{action}、{id?}是占位符,?表示可选参数;
3.defaults:默认值,当URL中没有对应部分时使用(如访问/时,默认控制器是Home,动作是Index);
5.Run():启动Kestrel Web服务器,监听配置的端口(默认5000/5001)。
场景3:区域路由
1.[Area("Admin")]:控制器上的特性,标记该控制器属于Admin区域;
2.MapAreaControllerRoute():配置区域路由,pattern中的Admin/会自动添加到URL前面,访问/Admin时默认跳转到Admin区域的Home/Index动作;
3.路由匹配顺序:从上到下匹配,所以区域路由要放在默认路由前面,否则/Admin会被默认路由解析为控制器Admin(不存在),导致404。
场景4:自定义路由与约束
1.[Route("product/{slug}")]:控制器动作上的路由特性,优先级高于全局路由,直接匹配/product/iphone-15到DetailsBySlug动作;
2.路由约束:{id:int}限制id必须是整数,避免无效参数(如/product/abc不会匹配这个路由,会走404);
3.全局自定义路由:适合多个动作共用同一个路由模板的场景,比如所有产品相关的URL都以/product/开头。
基础知识拓展
-
路由匹配原理
中间件顺序:UseRouting()必须在UseEndpoints()之前,因为先解析URL再匹配端点;
匹配顺序:路由按注册顺序从上到下匹配,匹配到第一个符合条件的路由就停止;
路由模板语法:
{param}:必选参数;
{param?}:可选参数;
{param:constraint}:带约束的参数;
{param=default}:带默认值的参数;
:通配符(匹配任意字符,如{catchall})。 -
RESTful路由最佳实践
HTTP动词对应动作:
GET:获取资源(如/products获取列表,/products/123获取详情);
POST:创建资源(如/products);
PUT:更新资源(如/products/123);
DELETE:删除资源(如/products/123);
控制器命名:用复数名词(如ProductsController),动作命名为Index(列表)、Details(详情)、Create(创建)、Edit(编辑)、Delete(删除);
用特性指定HTTP动词:
csharp
[HttpGet] // 匹配GET请求
public IActionResult Index() { ... }
[HttpGet("{id:int}")] // 匹配GET /products/123
public IActionResult Details(int id) { ... }
[HttpPost] // 匹配POST /products
public IActionResult Create(Product model) { ... }
-
路由调试工具
Developer Exception Page:开发环境访问不存在的URL,查看“Routing”部分,显示所有注册的路由和匹配情况;
Route Debugger:安装NuGet包Microsoft.AspNetCore.Routing.Debugger,然后在Program.cs中添加app.UseRouteDebugger(),访问/route-debugger查看所有路由。 -
生成URL(反向路由)
在视图或控制器中,用Url.Action()或Html.ActionLink()生成URL(根据路由模板,避免硬编码):
csharp
// 生成URL /Home/Details/123
var url = Url.Action("Details", "Home", new { id = 123 });
// 生成Admin区域的URL /Admin/Orders/List
var adminUrl = Url.Action("List", "Orders", new { area = "Admin" });
在Razor视图中:
html
<!-- 生成<a href="/Home/Details/123">查看详情</a> -->
@Html.ActionLink("查看详情", "Details", "Home", new { id = 123 }, null)
总结
ASP.NET Core MVC的路由核心是约定优于配置,通过路由模板统一管理URL,解决了硬编码路由的痛点。关键要点:
1.项目结构:Controllers(处理请求)、Views(渲染页面)、Models(数据模型)分层清晰;
2.路由配置:默认路由适合简单项目,区域路由适合多模块项目,自定义路由适合SEO友好的URL;
3.路由约束:限制参数类型,避免无效请求,提升安全性;
4.调试工具:用Developer Exception Page和Route Debugger排查路由匹配问题;
5.反向路由:用Url.Action()生成URL,避免硬编码,便于维护。
比如电商项目中,前台用默认路由(/products/123),后台用区域路由(/admin/orders),产品详情用自定义路由(/product/iphone-15),既友好又易于维护。掌握这些技巧,你可以轻松构建结构清晰、URL友好的Web项目!
本站原创,转载请注明出处:










