RESTful日#4:使用MVC 4 Web api中的属性路由自定义URL重写/路由

表的内容 目录介绍路线图路由现有设计和问题属性路由设置REST端点/ WebAPI项目定义路由更多的路由约束 范围正则表达式可选参数和默认参数 版本控制:覆盖运行应用程序结论引用的默认路由 介绍 我们已经学了很多关于WebAPI的知识。我已经解释了如何创建WebAPI,如何使用实体框架将其与数据库连接,如何使用Unity容器和MEF解析依赖关系。在我们的所有示例应用程序中,我们使用MVC为CRUD操作提供的默认路由。在本文中,我将解释如何使用属性路由编写自己的自定义路由。我们将处理操作级路由和控制器级路由。我将通过一个示例应用程序详细解释这一点。我的新读者可以使用任何Web API示例,也可以使用我们在我以前的文章中开发的示例应用程序。 路线图 让我们回顾一下我在Web API上开始的路线图, 下面是我学习RESTful api的路线图, ,RESTful日#1:企业级应用程序架构,使用实体框架、通用存储库模式和工作单元的Web api。RESTful日#2:使用Unity容器和引导程序在Web api中使用依赖注入实现控制反转。RESTful日#3:使用Unity容器和可管理扩展框架(MEF)在Asp.net Web api中使用控制反转和依赖注入来解决依赖关系的依赖关系。RESTful日#4:使用MVC 4 Web api中的属性路由自定义URL重写/路由。RESTful日#5:使用操作过滤器的Web api中基于基本身份验证和令牌的自定义授权。RESTful日#6:使用操作过滤器、异常过滤器和NLog在Web api中进行请求日志记录和异常处理/日志记录。RESTful日#7:使用NUnit和Moq框架在WebAPI中进行单元测试和集成测试(第一部分)。使用NUnit和Moq框架在WebAPI中进行单元测试和集成测试(第二部分)。净Web api。RESTful日#10:创建自托管的ASP。NET WebAPI与CRUD操作在Visual Studio 2010 我有意使用Visual Studio 2010和。net Framework 4.0,因为在。net Framework 4.0中很少有很难找到的实现,但我将通过演示如何实现来简化它。 路由 图片来源:http://www.victig.com/wp-content/uploads/2010/12/routing.jpg 对于任何服务、API、网站来说,路由都是一种模式定义系统,它试图映射来自客户端的所有请求,并通过提供对该请求的响应来解析该请求。在WebAPI中,我们可以在WebAPIConfig文件中定义路由,这些路由在一个内部路由表中定义。我们可以在该表中定义多个路由集。 现有设计及问题 我们已经有了一个现有的设计。如果你打开解决方案,你会看到下面提到的结构, 在我们现有的应用程序中,我们用WebAPI项目的App_Start文件夹中的WebApiConfig文件中提到的默认路由创建了WebAPI。寄存器方法中提到的路径为, 隐藏,复制Code

config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );

不要被MVC路由搞糊涂了,因为我们在使用MVC项目,我们也在RouteConfig.cs文件中定义了MVC路由, 隐藏,复制Code

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 }
            );
        }

我们需要关注第一个,也就是WebAPI路由。如下图所示,每个属性表示的是, 我们有一个路由名称,有一个用于所有路由的通用URL模式,还有一个选项来提供可选参数。 由于我们的应用程序没有特定的不同动作名称,而且我们使用HTTP动词作为动作名称,所以我们不太关心路由。我们的行动名称是, 隐藏,复制Code

1.	public HttpResponseMessage Get()
2.	public HttpResponseMessage Get(int id)
3.	public int Post([FromBody] ProductEntity productEntity)
4.	public bool Put(int id, [FromBody]ProductEntity productEntity)
5.	public bool Delete(int id)

Default route defined不考虑HTTP动词操作名称,并将它们视为默认操作,因此在routeTemplate中没有提到{action}。但这没有限制,我们可以在WebApiConfig中定义自己的路由,例如,看看下面的路由, 隐藏,复制Code

public static void Register(HttpConfiguration config)
      {
          config.Routes.MapHttpRoute(
              name: "DefaultApi",
              routeTemplate: "api/{controller}/{id}",
              defaults: new { id = RouteParameter.Optional }
          );

          config.Routes.MapHttpRoute(
             name: "ActionBased",
             routeTemplate: "api/{controller}/{action}/{id}",
             defaults: new { id = RouteParameter.Optional }
         );
          config.Routes.MapHttpRoute(
            name: "ActionBased",
            routeTemplate: "api/{controller}/action/{action}/{id}",
            defaults: new { id = RouteParameter.Optional }
        );
      }

等。在上面提到的路由中,如果我们有自定义操作,我们也可以有操作名称。 所以在WebAPI中定义路由没有限制。但这并没有什么限制,注意,我们讨论的是WebAPI 1,它与Visual Studio 2010中的。net Framework 4.0一起使用。通过包含我将在本文中解释的解决方案,Web API 2克服了这些限制。让我们看看这些路线的局限性, 是的,这些就是我在Web API 1中谈到的限制。 如果我们有路由模板,如routeTemplate:“api/{controller}/{id}”或routeTemplate:“api/{controller}/{action}/{id}”或routeTemplate:“api /{控制器}/行动/{行动}/ {id}”, 我们永远不能有自定义的路由,将不得不灵活地使用MVC提供的旧路由约定。假设项目的客户端希望公开服务的多个端点,他不能这样做。我们也不能为路由定义自己的名称,因此有很多限制。 假设我们想为我的web API端点设置以下路径,我也可以定义版本, v1 /产品/产品/ allproducts v1 /产品/产品/ productid / 1 v1 /产品/产品/ particularproduct / 4 v1 /产品/产品/ myproduct / & lt; range> v1 /产品/产品/创建 v1 /产品/产品/更新/ 3 等等,那么我们不能用现有的模型来实现这一点。 幸运的是,这些问题在WebAPI 2和MVC 5中已经得到了解决,但是对于这种情况,我们有AttributeRouting来解决和克服这些限制。 属性的路由 属性路由是关于在控制器级和操作级创建自定义路由的。使用属性路由,我们可以有多条路由。我们也可以有不同的路线,简而言之,我们有现有问题的解决方案。让我们直接跳到如何在我们的ex辛项目中实现这一点。我没有解释如何创建WebAPI,您可以参考我的第一篇文章。 步骤1:打开解决方案,并打开包管理控制台,如图所示, Goto Tools->Library Packet management ->包管理器控制台 步骤2:在Visual Studio类型的左侧的package manager控制台窗口中,Install-Package AttributeRouting。并选择项目WebApi或您自己的API项目(如果您正在使用任何其他代码示例),然后按回车。 步骤3:一旦安装了包,您将在App_Start文件夹中获得一个名为AttributeRoutingHttpConfig.cs的类。 这个类有自己的RegisterRoutes方法,它在内部映射属性路由。它有一个start方法,它从GlobalConfiguration中选择定义的路由,并调用RegisterRoutes方法, 隐藏,复制Code

using System.Web.Http;
using AttributeRouting.Web.Http.WebHost;

[assembly: WebActivator.PreApplicationStartMethod(typeof(WebApi.AttributeRoutingHttpConfig), "Start")]

namespace WebApi 
{
    public static class AttributeRoutingHttpConfig
	{
		public static void RegisterRoutes(HttpRouteCollection routes) 
		{    
			// See http://github.com/mccalltd/AttributeRouting/wiki for more options.
			// To debug routes locally using the built in ASP.NET development server, go to /routes.axd

            routes.MapHttpAttributeRoutes();
		}

        public static void Start() 
		{
            RegisterRoutes(GlobalConfiguration.Configuration.Routes);
        }
    }
}

我们甚至不需要接触这个类,我们的自定义路由将自动使用这个类来处理。我们只需要专注于定义路线。你现在可以使用路由特定的东西,比如路由名称、动词、约束、可选参数、默认参数、方法、路由区域、区域映射、路由前缀、路由约定等等。 设置REST端点/ WebAPI项目来定义路由 我们90%的工作都完成了。 现在我们需要设置WebAPI项目并定义路由。 我们现有的ProductController类如下所示, 隐藏,收缩,复制Code

using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using BusinessEntities;
using BusinessServices;

namespace WebApi.Controllers
{
    public class ProductController : ApiController
    {

        private readonly IProductServices _productServices;

        #region Public Constructor

        /// <summary>
        /// Public constructor to initialize product service instance
        /// </summary>
        public ProductController(IProductServices productServices)
        {
            _productServices = productServices;
        }

        #endregion

        // GET api/product
        public HttpResponseMessage Get()
        {
            var products = _productServices.GetAllProducts();
            var productEntities = products as List<ProductEntity> ?? products.ToList();
            if (productEntities.Any())
                return Request.CreateResponse(HttpStatusCode.OK, productEntities);
            return Request.CreateErrorResponse(HttpStatusCode.NotFound, "Products not found");
        }

        // GET api/product/5
        public HttpResponseMessage Get(int id)
        {
            var product = _productServices.GetProductById(id);
            if (product != null)
                return Request.CreateResponse(HttpStatusCode.OK, product);
            return Request.CreateErrorResponse(HttpStatusCode.NotFound, "No product found for this id");
        }

        // POST api/product
        public int Post([FromBody] ProductEntity productEntity)
        {
            return _productServices.CreateProduct(productEntity);
        }

        // PUT api/product/5
        public bool Put(int id, [FromBody] ProductEntity productEntity)
        {
            if (id > 0)
            {
                return _productServices.UpdateProduct(id, productEntity);
            }
            return false;
        }

        // DELETE api/product/5
        public bool Delete(int id)
        {
            if (id > 0)
                return _productServices.DeleteProduct(id);
            return false;
        }
    }
}

其中,控制器名称为产品,动作名称为动词。当我们运行应用程序时,我们只得到以下类型的端点(请忽略端口和本地主机设置)。这是因为我从本地环境运行这个应用程序), 把所有产品: http://localhost:40784/api/Product 通过Id获取产品: http://localhost:40784/api/Product/3 创建产品: http://localhost:40784/api/Product (json主体) 更新产品: http://localhost:40784/api/product/3(带有json主体) 删除产品: http://localhost:40784/api/Product/3 步骤1:向控制器添加两个名称空间, 隐藏,复制Code

using AttributeRouting;
using AttributeRouting.Web.Http;

第二步:用不同的路线装饰你的行动, 与上面的图像一样,我定义了一个名为productid的路由,它以id作为参数。我们还必须在路径中提供动词(GET、POST、PUT、DELETE、PATCH),如图所示。因此它是[GET("productid/{id?}")]。您可以为您的操作定义任何您想要的路由,如[GET("product/id/{id?}")], [GET("myproduct/id/{id?}")]和更多。 现在,当我运行应用程序并导航到/帮助页面时,我得到这个, 例如,我有一个通过id获取产品的路径。当您测试此服务时,您将得到您想要的URL,类似于http://localhost:55959/productid/3,这听起来像真正的REST:) 类似地,用多个路径装饰您的动作,如下所示, 隐藏,复制Code

// GET api/product/5
[GET("productid/{id?}")]
[GET("particularproduct/{id?}")]
[GET("myproduct/{id:range(1, 3)}")]
public HttpResponseMessage Get(int id)
{
    var product = _productServices.GetProductById(id);
    if (product != null)
        return Request.CreateResponse(HttpStatusCode.OK, product);
    return Request.CreateErrorResponse(HttpStatusCode.NotFound, "No product found for this id");
}

因此,我们可以拥有自定义路由名称,以及单个操作的多个端点。这是令人兴奋的。每个端点将是不同的,但将服务于相同的结果集。 {id ?这里?'表示该参数可以是可选的。[GET("myproduct/{id:range(1,3)}"),表示产品id位于这个范围内,将只会显示。 多路由约束 您可以利用属性路由提供的许多路由约束。我举一些例子, 范围 为了使产品在范围内,我们可以定义值,条件是它应该存在于数据库中。 隐藏,复制Code

[GET("myproduct/{id:range(1, 3)}")]
public HttpResponseMessage Get(int id)
{
    var product = _productServices.GetProductById(id);
    if (product != null)
        return Request.CreateResponse(HttpStatusCode.OK, product);
    return Request.CreateErrorResponse(HttpStatusCode.NotFound, "No product found for this id");
}

正则表达式 您可以更有效地将它用于文本/字符串参数。 隐藏,复制Code

        [GET(@"id/{e:regex(^[0-9]$)}")]
        public HttpResponseMessage Get(int id)
        {
            var product = _productServices.GetProductById(id);
            if (product != null)
                return Request.CreateResponse(HttpStatusCode.OK, product);
            return Request.CreateErrorResponse(HttpStatusCode.NotFound, "No product found for this id");
        }

e.g. [GET(@"text/{e:regex(^[A-Z][a-z][0-9]$)}")]

可选参数和默认参数 您还可以将服务参数标记为opti在这条路线上。例如,您想要从数据库中获取一个雇员的详细信息以及他的名字, 隐藏,复制Code

[GET("employee/name/{firstname}/{lastname?}")]
public string GetEmployeeName(string firstname, string lastname="mittal")
{
   …………….
  ……………….
}

在上面提到的代码中,我用问号' ?来获取员工信息。这是我的最终用户希望提供的姓氏或不。 因此,可以通过带有url的GET动词访问上述端点, 隐藏,复制Code

~/employee/name/akhil/mittal
~/employee/name/akhil

如果定义的路由参数标记为可选,则还必须为该方法参数提供默认值。 在上面的示例中,我将' lastname '标记为可选值,因此在方法参数中提供了一个默认值,如果用户不发送任何值,将使用“mittal”。 在。net 4.5 Visual Studio >中;在WebAPI 2中,你也可以将DefaultRoute定义为一个属性,你可以自己尝试一下。使用attribute [DefaultRoute]定义默认路由值。 您可以尝试为所有控制器操作提供自定义路由。 我把我的行为标记为, 隐藏,收缩,复制Code

// GET api/product
[GET("allproducts")]
[GET("all")]
public HttpResponseMessage Get()
{
    var products = _productServices.GetAllProducts();
    var productEntities = products as List<ProductEntity> ?? products.ToList();
    if (productEntities.Any())
        return Request.CreateResponse(HttpStatusCode.OK, productEntities);
    return Request.CreateErrorResponse(HttpStatusCode.NotFound, "Products not found");
}

// GET api/product/5
[GET("productid/{id?}")]
[GET("particularproduct/{id?}")]
[GET("myproduct/{id:range(1, 3)}")]
public HttpResponseMessage Get(int id)
{
    var product = _productServices.GetProductById(id);
    if (product != null)
        return Request.CreateResponse(HttpStatusCode.OK, product);
    return Request.CreateErrorResponse(HttpStatusCode.NotFound, "No product found for this id");
}

// POST api/product
[POST("Create")]
[POST("Register")]
public int Post([FromBody] ProductEntity productEntity)
{
    return _productServices.CreateProduct(productEntity);
}

// PUT api/product/5
[PUT("Update/productid/{id}")]
[PUT("Modify/productid/{id}")]
public bool Put(int id, [FromBody] ProductEntity productEntity)
{
    if (id > 0)
    {
        return _productServices.UpdateProduct(id, productEntity);
    }
    return false;
}

// DELETE api/product/5
[DELETE("remove/productid/{id}")]
[DELETE("clear/productid/{id}")]
[PUT("delete/productid/{id}")]
public bool Delete(int id)
{
    if (id > 0)
        return _productServices.DeleteProduct(id);
    return false;
}

因此得到的路径是, , 得到 发布/发布/删除 在这里检查更多的约束。 您一定在每条路线上都看到了“v1/Products”,这是因为我在控制器级别上使用了RoutePrefix。让我们详细讨论一下RoutePrefix。 RoutePrefix:控制器级别的路由 我们用特定的路由标记我们的动作,但是猜猜看,我们也可以用特定的路由名称标记我们的控制器,我们可以通过使用AttributeRouting的RoutePrefix属性来实现这一点。我们的控制器被命名为Product,我想在我的每个动作之前添加Product /Product,因此在不复制每个动作的代码的情况下,我可以用这个名字装饰我的控制器类,如下所示, 隐藏,复制Code

[RoutePrefix("Products/Product")]
public class ProductController : ApiController
{

现在,由于我们的控制器被标记为这个路由,它也会给每个动作追加相同的内容。例如,后续行动的路线, 隐藏,复制Code

[GET("allproducts")]
[GET("all")]
public HttpResponseMessage Get()
{
    var products = _productServices.GetAllProducts();
    var productEntities = products as List<ProductEntity> ?? products.ToList();
    if (productEntities.Any())
        return Request.CreateResponse(HttpStatusCode.OK, productEntities);
    return Request.CreateErrorResponse(HttpStatusCode.NotFound, "Products not found");
}

现在变成了, 隐藏,复制Code

~/Products/Product/allproducts
~/Products/Product/all

RoutePrefix:版本控制 Route前缀还可以用于端点的版本控制,就像在我的代码中,我在RoutePrefix中提供了“v1”作为版本,如下所示, 隐藏,复制Code

[RoutePrefix("v1/Products/Product")]
public class ProductController : ApiController
{

因此,“v1”将被附加到服务的每个路由/端点。当我们发布下一个版本时,我们当然可以单独维护一个变更日志,并在控制器级别将端点标记为“v2”,这将在所有操作中附加“v2”, 如。 隐藏,复制Code

~/v1/Products/Product/allproducts
~/v1/Products/Product/all

~/v2/Products/Product/allproducts
~/v2/Products/Product/all

RoutePrefix:覆盖 这个功能在。net 4.5和Visual Studio >中都有。2010年使用WebAPI 2。你可以在那里测试它。 在某些情况下,我们不希望对每个操作都使用RoutePrefix。AttributeRouting也提供了这样的灵活性,尽管在控制器级别存在RoutePrefix,单个操作也可以有自己的路由。它只需要覆盖默认路由,如下所示, RoutePrefix在控制器 隐藏,复制Code

 [RoutePrefix("v1/Products/Product")]
public class ProductController : ApiController
{

独立作用路径 隐藏,复制Code

[Route("~/MyRoute/allproducts")]
 public HttpResponseMessage Get()
 {
     var products = _productServices.GetAllProducts();
     var productEntities = products as List<ProductEntity> ?? products.ToList();
     if (productEntities.Any())
         return Request.CreateResponse(HttpStatusCode.OK, productEntities);
     return Request.CreateErrorResponse(HttpStatusCode.NotFound, "Products not found");
 }

禁用默认路由 你一定在想,在服务帮助页面的所有URL列表中,我们得到了一些不同的/其他的路由,我们没有通过属性路由开始定义,如~/api/Product。这些路由是WebApiConfig文件中提供的默认路由的结果,记得吗?如果你想去掉那些不需要的路由,只需在Appi_Start文件夹下的WebApiConfig.cs文件的Register方法中注释所有内容, 隐藏,复制Code

//config.Routes.MapHttpRoute(
//    name: "DefaultApi",
//    routeTemplate: "api/{controller}/{id}",
//    defaults: new { id = RouteParameter.Optional }
//);

您还可以删除完整的Register方法,但是需要从全局中删除它的调用。asax文件。 运行应用程序 运行这个应用程序,我们得到, 我们已经添加了我们的测试客户端,但是对于新的读者,只要去管理Nuget包,通过右击WebAPI项目和在搜索框中输入WebAPITestClient在线包, 您将获得“一个用于ASP的简单测试客户端”。NET Web API”,只要添加它。你将在以下区域得到一个帮助控制器->如下图所示, 我在上一篇文章中已经提供了数据库脚本和数据,您可以使用相同的脚本和数据。 在应用程序url中添加“/help”,您将获得测试客户端, 得到 帖子 把 删除 您可以通过单击每个服务来测试它。单击服务链接后,您将被重定向到测试该特定服务的服务页面。在这个页面的右下角有一个按钮测试API,只要按下那个按钮就可以测试你的服务, 为Get所有产品提供服务, 同样,您可以测试所有服务端点。 图片来源:http://www.rsc.org/eic/sites/default/files/upload/cwtype-Endpoint.jpg和http://www.guybutlerphotography.com/eegdemoconstruction/wp-content/uploads/2013/01/asthmaendpoint_shutterstock_89266522_small.jpg 结论 我们现在知道了如何定义自定义端点,以及它的好处是什么。这个库是由http的作者Tim Call介绍的://attributerouting.net和Microsoft已经默认包含在WebAPI 2中。我的下一篇文章将解释使用WepAPI中的ActionFilters进行基于令牌的身份验证。你还可以从GitHub下载完整的源代码。如果源代码中缺少所需的包,则添加它们。 点击Github Repository浏览完整的源代码。 参考文献 http://blogs.msdn.com/b/webdev/archive/2013/10/17/attribute-routing-in-asp-net-mvc-5.aspx https://github.com/mccalltd/AttributeRouting 我的其他系列文章 MVC: http://www.codeproject.com/Articles/620195/Learning-MVC-Part-Introduction-to-MVC-Architectu OOP: http://www.codeproject.com/Articles/771455/Diving-in-OOP-Day-Polymorphism-and-Inheritance-Ear 本文转载于:http://www.diyabc.com/frontweb/news408.html

原文地址:https://www.cnblogs.com/Dincat/p/13447363.html