自定义路由不适用于默认 mvc 路由
Custom route doesn't work with default mvc route
我正在按照 Microsoft Contoso University Tutorial 学习 MVC。
到目前为止我已经创建了 3 个模型
- 课程
- 注册人数
- 学生
我有 2 个控制器
- 家庭控制器
- 学生控制器
我可以查看学生列表、学生的详细信息(包括课程内容和成绩)、编辑学生详细信息以及添加新学生。
在这一点上,我想了解更多关于路由的知识,因为我一直在使用默认的 Map Route
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
我有一个学生数据库,所有学生的姓氏都是唯一的。我知道通常不建议这样做,因为姓氏不是唯一的,但它适用于这个小型学习项目。所以我想要实现的是能够输入 /students/{action}/{lastname}
并查看有关学生的详细信息。所以我在我的学生控制器中创建了一个新的操作方法。
public ActionResult Grab(string studentName)
{
if (studentName == null)
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
var student = db.Students.FirstOrDefault(x => x.LastName.ToLower() == studentName.ToLower());
if (student == null)
return HttpNotFound();
return View(student);
}
并且我在 RouteConfig.cs 文件中添加了一条新路线
//Custom route
routes.MapRoute(
name: null,
url: "{controller}/{action}/{studentName}",
defaults: new { controller = "Student", action = "Grab", id = UrlParameter.Optional }
);
最后,我创建了一个名为 Grab 的新视图,用于显示学生的详细信息。
现在,当我输入 /student/grab/Norman
时,它会向我提供姓氏为 Norman
的学生的详细信息
太棒了。但现在我有一个问题。当我尝试使用我的一些原始网址时,例如 /Student/Details/1
他们不再工作了。我收到 400 错误。
我做的第一件事是将我的默认路线移到我制作的自定义路线上方
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
//Custom route
routes.MapRoute(
name: null,
url: "{controller}/{action}/{studentName}",
defaults: new { controller = "Student", action = "Grab", id = UrlParameter.Optional }
);
这解决了这个问题,但在我之前工作的抓取路线上导致了 400 错误。我怎样才能同时使用这两个而不会出现 400 错误?
这是我的完整 RouteConfig.cs 文件
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
namespace ContosoUniversity
{
public class RouteConfig
{
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 }
);
//Custom route
routes.MapRoute(
name: null,
url: "{controller}/{action}/{studentName}",
defaults: new { controller = "Student", action = "Grab", id = UrlParameter.Optional }
);
}
}
}
这是我的整个 StudentController.cs 文件
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Linq;
using System.Net;
using System.Web;
using System.Web.Mvc;
using ContosoUniversity.DAL;
using ContosoUniversity.Models;
namespace ContosoUniversity.Controllers
{
public class StudentController : Controller
{
private SchoolContext db = new SchoolContext();
// GET: Student
public ActionResult Index()
{
return View(db.Students.ToList());
}
// GET: Student/Details/5
public ActionResult Details(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Student student = db.Students.Find(id);
if (student == null)
{
return HttpNotFound();
}
return View(student);
}
public ActionResult Grab(string studentName)
{
if (studentName == null)
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
var student = db.Students.FirstOrDefault(x => x.LastName.ToLower() == studentName.ToLower());
if (student == null)
return HttpNotFound();
return View(student);
}
// GET: Student/Create
public ActionResult Create()
{
return View();
}
// POST: Student/Create
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "LastName, FirstMidName, EnrollmentDate")]Student student)
{
try
{
if (ModelState.IsValid)
{
db.Students.Add(student);
db.SaveChanges();
return RedirectToAction("Index");
}
}
catch (DataException /* dex */)
{
//Log the error (uncomment dex variable name and add a line here to write a log.
ModelState.AddModelError("", "Unable to save changes. Try again, and if the problem persists see your system administrator.");
}
return View(student);
}
// GET: Student/Edit/5
public ActionResult Edit(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Student student = db.Students.Find(id);
if (student == null)
{
return HttpNotFound();
}
return View(student);
}
// POST: Student/Edit/5
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost, ActionName("Edit")]
[ValidateAntiForgeryToken]
public ActionResult EditPost(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
var studentToUpdate = db.Students.Find(id);
if (TryUpdateModel(studentToUpdate, "",
new string[] { "LastName", "FirstMidName", "EnrollmentDate" }))
{
try
{
db.SaveChanges();
return RedirectToAction("Index");
}
catch (DataException /* dex */)
{
//Log the error (uncomment dex variable name and add a line here to write a log.
ModelState.AddModelError("", "Unable to save changes. Try again, and if the problem persists, see your system administrator.");
}
}
return View(studentToUpdate);
}
// GET: Student/Delete/5
public ActionResult Delete(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Student student = db.Students.Find(id);
if (student == null)
{
return HttpNotFound();
}
return View(student);
}
// POST: Student/Delete/5
[HttpPost, ActionName("Delete")]
[ValidateAntiForgeryToken]
public ActionResult DeleteConfirmed(int id)
{
Student student = db.Students.Find(id);
db.Students.Remove(student);
db.SaveChanges();
return RedirectToAction("Index");
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
db.Dispose();
}
base.Dispose(disposing);
}
}
}
我会提供任何其他需要的详细信息。我再次希望能够输入 http://localhost:49706/Student/Details/1
或 http://localhost:49706/Student/Grab/Alexander
并获得完全相同的详细信息,因为 Alexander 的学生 ID 为 1。
您想将自定义路由与默认路由区分开来url
public static void RegisterRoutes(RouteCollection routes) {
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
//Custom route
routes.MapRoute(
name: "Students",
url: "Student/{action}/{id}",
defaults: new { controller = "Student", action = "Grab", id = UrlParameter.Optional }
);
//General default route
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
操作参数也应与路由模板参数相匹配
// GET: Student/Details/5
public ActionResult Details(int? id) {
if (id == null) {
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
var student = db.Students.Find(id);
if (student == null) {
return HttpNotFound();
}
return View(student);
}
public ActionResult Grab(string id) {
if (id == null)
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
var student = db.Students.FirstOrDefault(x => x.LastName.ToLower() == id.ToLower());
if (student == null)
return HttpNotFound();
return View(student);
}
现在可以正确匹配以下内容
Student/Details/1
Student/Grab/Alexander
提供姓氏为 Alexander 的学生的 studentId 为 1
两条路线相同(从模式匹配的角度来看):
{controller}/{action}/{id}
{controller}/{action}/{studentName}
id
和 studentName
只是占位符。它们除此之外没有任何意义,所以你基本上定义了一个路由两次:
{controller}/{action}/{someindicator}
最终发生的事情是,首先命中的任何路由都会为请求提供服务。
因此,如果你想使用相同的 "pattern",你需要以某种方式区分路由。我建议直接在路线中指明动作:
routes.MapRoute(
name: "StudentDetails",
url: "{controller}/Details/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
//Custom route
routes.MapRoute(
name: "GrabStudent",
url: "{controller}/Grab/{studentName}",
defaults: new { controller = "Student", action = "Grab", id = UrlParameter.Optional }
);
我正在按照 Microsoft Contoso University Tutorial 学习 MVC。
到目前为止我已经创建了 3 个模型
- 课程
- 注册人数
- 学生
我有 2 个控制器
- 家庭控制器
- 学生控制器
我可以查看学生列表、学生的详细信息(包括课程内容和成绩)、编辑学生详细信息以及添加新学生。
在这一点上,我想了解更多关于路由的知识,因为我一直在使用默认的 Map Route
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
我有一个学生数据库,所有学生的姓氏都是唯一的。我知道通常不建议这样做,因为姓氏不是唯一的,但它适用于这个小型学习项目。所以我想要实现的是能够输入 /students/{action}/{lastname}
并查看有关学生的详细信息。所以我在我的学生控制器中创建了一个新的操作方法。
public ActionResult Grab(string studentName)
{
if (studentName == null)
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
var student = db.Students.FirstOrDefault(x => x.LastName.ToLower() == studentName.ToLower());
if (student == null)
return HttpNotFound();
return View(student);
}
并且我在 RouteConfig.cs 文件中添加了一条新路线
//Custom route
routes.MapRoute(
name: null,
url: "{controller}/{action}/{studentName}",
defaults: new { controller = "Student", action = "Grab", id = UrlParameter.Optional }
);
最后,我创建了一个名为 Grab 的新视图,用于显示学生的详细信息。
现在,当我输入 /student/grab/Norman
时,它会向我提供姓氏为 Norman
太棒了。但现在我有一个问题。当我尝试使用我的一些原始网址时,例如 /Student/Details/1
他们不再工作了。我收到 400 错误。
我做的第一件事是将我的默认路线移到我制作的自定义路线上方
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
//Custom route
routes.MapRoute(
name: null,
url: "{controller}/{action}/{studentName}",
defaults: new { controller = "Student", action = "Grab", id = UrlParameter.Optional }
);
这解决了这个问题,但在我之前工作的抓取路线上导致了 400 错误。我怎样才能同时使用这两个而不会出现 400 错误?
这是我的完整 RouteConfig.cs 文件
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
namespace ContosoUniversity
{
public class RouteConfig
{
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 }
);
//Custom route
routes.MapRoute(
name: null,
url: "{controller}/{action}/{studentName}",
defaults: new { controller = "Student", action = "Grab", id = UrlParameter.Optional }
);
}
}
}
这是我的整个 StudentController.cs 文件
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Linq;
using System.Net;
using System.Web;
using System.Web.Mvc;
using ContosoUniversity.DAL;
using ContosoUniversity.Models;
namespace ContosoUniversity.Controllers
{
public class StudentController : Controller
{
private SchoolContext db = new SchoolContext();
// GET: Student
public ActionResult Index()
{
return View(db.Students.ToList());
}
// GET: Student/Details/5
public ActionResult Details(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Student student = db.Students.Find(id);
if (student == null)
{
return HttpNotFound();
}
return View(student);
}
public ActionResult Grab(string studentName)
{
if (studentName == null)
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
var student = db.Students.FirstOrDefault(x => x.LastName.ToLower() == studentName.ToLower());
if (student == null)
return HttpNotFound();
return View(student);
}
// GET: Student/Create
public ActionResult Create()
{
return View();
}
// POST: Student/Create
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "LastName, FirstMidName, EnrollmentDate")]Student student)
{
try
{
if (ModelState.IsValid)
{
db.Students.Add(student);
db.SaveChanges();
return RedirectToAction("Index");
}
}
catch (DataException /* dex */)
{
//Log the error (uncomment dex variable name and add a line here to write a log.
ModelState.AddModelError("", "Unable to save changes. Try again, and if the problem persists see your system administrator.");
}
return View(student);
}
// GET: Student/Edit/5
public ActionResult Edit(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Student student = db.Students.Find(id);
if (student == null)
{
return HttpNotFound();
}
return View(student);
}
// POST: Student/Edit/5
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost, ActionName("Edit")]
[ValidateAntiForgeryToken]
public ActionResult EditPost(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
var studentToUpdate = db.Students.Find(id);
if (TryUpdateModel(studentToUpdate, "",
new string[] { "LastName", "FirstMidName", "EnrollmentDate" }))
{
try
{
db.SaveChanges();
return RedirectToAction("Index");
}
catch (DataException /* dex */)
{
//Log the error (uncomment dex variable name and add a line here to write a log.
ModelState.AddModelError("", "Unable to save changes. Try again, and if the problem persists, see your system administrator.");
}
}
return View(studentToUpdate);
}
// GET: Student/Delete/5
public ActionResult Delete(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Student student = db.Students.Find(id);
if (student == null)
{
return HttpNotFound();
}
return View(student);
}
// POST: Student/Delete/5
[HttpPost, ActionName("Delete")]
[ValidateAntiForgeryToken]
public ActionResult DeleteConfirmed(int id)
{
Student student = db.Students.Find(id);
db.Students.Remove(student);
db.SaveChanges();
return RedirectToAction("Index");
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
db.Dispose();
}
base.Dispose(disposing);
}
}
}
我会提供任何其他需要的详细信息。我再次希望能够输入 http://localhost:49706/Student/Details/1
或 http://localhost:49706/Student/Grab/Alexander
并获得完全相同的详细信息,因为 Alexander 的学生 ID 为 1。
您想将自定义路由与默认路由区分开来url
public static void RegisterRoutes(RouteCollection routes) {
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
//Custom route
routes.MapRoute(
name: "Students",
url: "Student/{action}/{id}",
defaults: new { controller = "Student", action = "Grab", id = UrlParameter.Optional }
);
//General default route
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
操作参数也应与路由模板参数相匹配
// GET: Student/Details/5
public ActionResult Details(int? id) {
if (id == null) {
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
var student = db.Students.Find(id);
if (student == null) {
return HttpNotFound();
}
return View(student);
}
public ActionResult Grab(string id) {
if (id == null)
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
var student = db.Students.FirstOrDefault(x => x.LastName.ToLower() == id.ToLower());
if (student == null)
return HttpNotFound();
return View(student);
}
现在可以正确匹配以下内容
Student/Details/1
Student/Grab/Alexander
提供姓氏为 Alexander 的学生的 studentId 为 1
两条路线相同(从模式匹配的角度来看):
{controller}/{action}/{id}
{controller}/{action}/{studentName}
id
和 studentName
只是占位符。它们除此之外没有任何意义,所以你基本上定义了一个路由两次:
{controller}/{action}/{someindicator}
最终发生的事情是,首先命中的任何路由都会为请求提供服务。
因此,如果你想使用相同的 "pattern",你需要以某种方式区分路由。我建议直接在路线中指明动作:
routes.MapRoute(
name: "StudentDetails",
url: "{controller}/Details/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
//Custom route
routes.MapRoute(
name: "GrabStudent",
url: "{controller}/Grab/{studentName}",
defaults: new { controller = "Student", action = "Grab", id = UrlParameter.Optional }
);