zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    二分专题
    数据结构-图
    Linux文件基本属性(以ls -l输出为例解释)
    shell脚本版素数筛
    Linux whereis,which
    Linux外网代理配置
    Linux三剑客
    Elasticsearch集群搭建(Linux)
    测试之路
    我的另一半
  • 原文地址:https://www.cnblogs.com/Dincat/p/13447363.html
Copyright © 2011-2022 走看看