ASP.NET Core 实战:构建带有版本控制的 API 接口

前言

        在上一篇的文章中,主要是搭建了咱们的开发环境,同时建立了咱们的项目模板框架。在整个先后端分离的项目中,后端的 API 接口相当重要,它是前端与后端之间进行沟通的媒介,如何构建一个 “好用” 的 API 接口,是须要咱们后端人员好好思考的。html

        在系统迭代的整个过程当中,不可避免的会添加新的资源,或是修改现有的资源,后端接口做为暴露给外界的服务,变更的越小,对服务的使用方形成的印象就越小,所以,如何对咱们的 API 接口进行合适的版本控制,咱们势必须要首先考虑。前端

        系列目录地址:ASP.NET Core 项目实战git

        仓储地址:github.com/Lanesra712/…程序员

Step by Step

        项目老是在不断迭代的,某些时候,由于业务发展的须要,须要将现有的接口进行升级,而原有的接口却不能马上中止使用。好比说,你开发了一个接口提供给爱啪啪 1.0 版本使用,后来爱啪啪的版本迭代了,须要接口返回的数据与原先 1.0 版本返回的数据不一样了,这时候,接口确定是须要升级的,但是若是直接升级原有的接口,还在使用 1.0 版本的用户不就 GG 了,所以,如何作到既可让 1.0 版本的用户使用,也可让 2.0 版本的用户使用就须要好好考虑了,常见的解决方案,主要有如下几种。github

        一、 使用不一样的 API 名称shell

        最简单粗暴,须要变动接口逻辑时就从新起个 API 名称,新的版本调用新的 API 名称,旧的版本调用旧的 API 名称。编程

https://yuiter.com/api/Secret/Login ##爱啪啪 1.0
https://yuiter.com/api/Secret/NewLogin ##爱啪啪 2.0
复制代码

        二、 在 Url 中标明版本号json

        直接将 API 版本信息添加到请求的 Url 中,调用不一样版本的 API ,就在 URL 中直接标明使用的是哪一个版本。后端

https://yuiter.com/api/v1/Secret/Login ##爱啪啪 1.0
https://yuiter.com/api/v2/Secret/Login ##爱啪啪 2.0
复制代码

        三、 请求参数中添加版本信息api

        将 API 的版本信息做为请求的一个参数传递,经过指定参数值来肯定请求的 API 版本。

https://yuiter.com/api/Secret/Login?version=1 ##爱啪啪 1.0
https://yuiter.com/api/Secret/Login?version=2 ##爱啪啪 2.0
复制代码

        四、 在 header 中标明版本号

        前端在请求 API 接口时,在 header 中添加一个参数用来代表请求的版本信息,后端经过前端在 header 中设置的参数来判断,从而执行不一样的业务逻辑分支。

POST https://yuiter.com/api/Secret/Login
Host: yuiter.com  
api-version: v1   ##爱啪啪 1.0

POST https://yuiter.com/api/Secret/Login
Host: yuiter.com  
api-version: v2   ##爱啪啪 2.0
复制代码

        在 Grapefruit.VuCore 这个项目中,我选择将 API 的版本信息添加到请求的地址中,从而明确的指出当前请求的接口版本信息。

        1、 Swagger 集成

        后端完成了接口以后,确定须要告诉前端,无论是整理成 txt/excel/markdown 文档,亦或是写完一个接口就直接发微信告诉前端,老是要多作一步的事情,而 Swagger 则能够帮咱们省去这一步。经过配置以后,Swagger 就能够根据咱们的接口自动生成 API 的接口文档,省时,省力。固然,若是前端小姐姐单身可撩,而你碰巧有意的话,另谈。

        Swagger 是一个能够将接口文档自动生成,同时能够对接口功能进行测试的开源框架,在 ASP.NET Core 环境下,主流的有 Swashbuckle.AspNetCore 和 NSwag 这两个开源框架帮助咱们生成 Swagger documents。这里,我采用的是 Swashbuckle.AspNetCore

        在使用 Swashbuckle.AspNetCore 以前,首先咱们须要在 API(Grapefruit.WebApi) 项目中添加对于 Swashbuckle.AspNetCore 的引用。你能够直接右键选中 API 项目选择管理 Nuget 程序包进行加载引用,也能够经过程序包管理控制台进行添加引用,这里注意,使用程序包管理控制台时,你须要将默认的项目修改为 API(Grapefruit.WebApi) 项目。当引用添加完成后,咱们就能够在项目中配置 Swagger 了。

Nuget 包管理器

Install-Package Swashbuckle.AspNetCore
复制代码

程序包管理控制台
         ASP.NET Core 的本质上能够当作是一个控制台程序,在咱们建立好的 ASP.NET Core Web API 项目中,存在着两个类文件:Program.cs 以及 Startup.cs。与控制台应用同样,Program 类中的 Main 方法是整个程序的入口,在这个方法中,咱们将配置好的 IWebHostBuilder 对象,构建成 IWebHost 对象,并运行该 IWebHost 对象从而达到运行 Web 项目的做用。

        在框架生成的 Program 类文件中,在配置 IWebHostBuilder 的过程时,框架默认为咱们添加了一些服务,固然,这里你能够注释掉默认的写法,去本身建立一个 WebHostBuilder 对象。同时,对于一个 ASP.NET Core 程序来讲,Startup 类是必须的(你能够删除生成的 Startup 类,从新建立一个新的类,可是,这个新建立的类必须包含 Configure 方法,以后只须要在 UseStartup 中将该类配置为 Startup 类便可),这里若是不指定 Startup 类会致使启动失败。

        在 Startup 类中,存在着 ConfigureServices 和 Configure 这两个方法,在 ConfigureServices 方法中,咱们将自定义服务经过依赖注入的方式添加到 IServiceCollection 容器中,而这些容器中的服务,最终均可以在 Configure 方法中进行使用;而 Configure 方法则用于指定 ASP.NET Core 应用程序将如何响应每个 HTTP 请求,咱们能够在这里将咱们本身建立的中间件(Middleware)绑定到 IApplicationBuilder 上,从而添加到 HTTP 请求管道中。

        这里只是很粗略的说明了 ASP.NET Core 项目的启动过程,想要仔细了解启动过程的推荐园子里的这篇文章 =》ASP.NET Core 2.0 : 七.一张图看透启动背后的秘密,由于 ASP.NET Core 2.1 版本相比于 2.0 版本又有些改变,这里有一些不同的地方须要你去注意。

        当咱们简单了解了启动过程后,就能够配置咱们的 Swagger 了。Swashbuckle.AspNetCore 帮咱们构建好了使用 Swagger 的中间件,咱们只须要直接使用便可。

        首先咱们须要在 ConfigureServices 方法中,将咱们的服务添加到 IServiceCollection 容器中,这里,咱们须要为生成的 Swagger Document 进行一些配置。

services.AddSwaggerGen(s =>
{
    s.SwaggerDoc("v1", new Info
    {
        Contact = new Contact
        {
            Name = "Danvic Wang",
            Email = "danvic96@hotmail.com",
            Url = "https://yuiter.com"
        },
        Description = "A front-background project build by ASP.NET Core 2.1 and Vue",
        Title = "Grapefruit.VuCore",
        Version = "v1"
    });
});
复制代码

        以后,咱们就能够在 Configure 方法中启用咱们的 Swagger 中间件。

app.UseSwagger();
app.UseSwaggerUI(s =>
{
    s.SwaggerEndpoint("/swagger/v1/swagger.json", "Grapefruit.VuCore API V1.0");
});
复制代码

        此时,当你运行程序,在域名后面输入/swagger 便可访问到咱们的 API 文档页面。由于项目启动时默认访问的是咱们 api/values 的 GET 请求接口,这里咱们能够打开 Properties 下的 launchSetting.json 文件去配置咱们的程序默认打开页面。

配置程序启动页面
        从上面的图能够看出,无论是使用 IIS 或是程序自托管,咱们默认打开的 Url 都是 api/values,这里咱们将两种启动方式的 launchUrl 值都修改为 swagger 以后再次运行咱们的项目,能够发现,程序默认的打开页面就会变成咱们的 API 文档页面。
API 文档
        咱们使用 API 文档的目的,就是为了让前端知道请求的方法地址是什么,须要传递什么参数,而如今,并无办法显示出咱们对于参数以及方法的注释,经过查看 Swashbuckle.AspNetCore 的 github 首页能够看到,咱们能够经过配置,将生成的 json 文件中包含咱们对于 Controller or Action 的 Xml 注释内容,从而达到显示注释信息的功能(最终呈现的 Swagger Doc 是根据以前咱们定义的这个 “/swagger/v1/swagger.json” json 文件来生成的)。

        右键咱们的 API 项目,属性 =》生产,勾选上 XML 文档文件,系统会默认帮咱们建立生成 XML 文件的地址,这时候,咱们从新生成项目,则会发现,当前项目下会多出这个 XML 文件。在从新生成项目的过程当中,你会发现,错误列表会显示不少警告信息,提示咱们一些方法没有添加 XML 注释。若是你和我同样强迫症的话,能够把 1591 这个错误添加到上面的禁止显示警告中,这样就能够再也不显示这个警告了。

生成注释 XML 文件
        建立好 XML 的注释文件后,咱们就能够配置咱们的 Swagger 文档,从而达到显示注释的功能。这里,由于我会在 Grapefruit.Application 类库中建立各类的 Dto 对象,而接口中是会调用到这些 Dto 对象的。所以,为了显示这些 Dto 上的注释信息,这里咱们也须要生成 Grapefruit.Application 项目的 XML 注释文件。

        PS:这里我是将每一个项目生成的注释信息 xml 文档地址都放在了程序的基础路径下,若是你将 xml 文档生成在别的位置,这里获取 xml 的方法就须要你进行修改。

services.AddSwaggerGen(s =>
{
    //...

    //Add comments description
    //
    var basePath = Path.GetDirectoryName(AppContext.BaseDirectory);//get application located directory
    var apiPath = Path.Combine(basePath, "Grapefruit.WebApi.xml");
    var dtoPath = Path.Combine(basePath, "Grapefruit.Application.xml");
    s.IncludeXmlComments(apiPath, true);
    s.IncludeXmlComments(dtoPath, true);
});
复制代码

包含注释信息
        当咱们把 Swagger 配置完成以后,咱们就能够建立具备版本控制的 API 接口了。

        2、 带有版本控制的 API 接口实现

        在请求的 API Url 中标明版本号,我不知道你第一时间看到这个实现方式,会想到什么,对于我来讲,直接在路由信息中添加版本号不就能够了。。。em,这样过于投机取巧了。。。。

[Route("api/v1/[controller]")]//添加版本信息为v1
[ApiController]
public class ValuesController : ControllerBase
{
}
复制代码

        想了想,在 Url 中添加版本号,这个版本号是否是很像咱们在 MVC 中使用的 Area。

        Area 是 MVC 中常用到的一个功能,咱们一般会将某些小的模块拆分红一个个的 Area,而这一个个的小 Area 其实就是这个 MVC 项目中的 MVC。经过为 controller 和 action 添加另外一个路由参数 area,从而达到建立具备层次路由的结构。好比,这里,咱们能够建立一个 Area 叫 v1,用来存储咱们 1.x 版本的 API 接口,以后若是有新的 API 版本,新增一个 Area 便可,是否是很简单,嗯,说干就干。

        右击咱们的 API 项目,选择添加区域,新增的 Area 名称为 v1。

添加 Area
        当 ASP.NET Core 的脚手架程序添加完成 Area 后,则会打开一个文件提示咱们须要在 MVC 中间件中建立适用于 Area 的路由定义。

app.UseMvc(routes =>
{
  routes.MapRoute(
    name : "areas",
    template : "{area:exists}/{controller=Home}/{action=Index}/{id?}"
  );
});
复制代码

        当咱们添加好路由规则定义后,咱们在 Area 的 Controllers 文件夹下添加一个 WebAPI Controller。不一样于 ASP.NET 中的 Area ,当咱们在 ASP.NET Core 建立好一个 Area 以后,脚手架生成的文件中再也不有 XXXAreaRegistration(XXX 为 Area 的名称)文件去注册这个 Area,而咱们只须要在 Area 中的 Controller 中添加 Area 特性,便可告诉系统框架,这个 Controller 是在当前的 Area 下的。

        若是你有本身尝试的话,就会发现,当咱们建立好一个 v1 的 Area 后,这个请求的地址并无按照咱们的想法会体如今路由信息中,咱们最后仍是须要在 Route 中手动指明 API 版本。

[Area("v1")]
[Route("api/v1/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
}
复制代码

使用 Area 建立版本控制 API
        这样的话,和最开始直接在路由信息中写死版本信息其实也就没什么差异了,上网搜了搜,发现巨硬爸爸,也早已为咱们准备好了实现版本控制 API 的利器 - Microsoft.AspNetCore.Mvc.Versioning。

        和上面使用 Swashbuckle.AspNetCore 的方式相同,在咱们使用 Versioning 以前,须要在咱们的 API 项目中添加对于该 dll 的引用。这里须要注意下安装的版本问题,由于 Grapefruit.VuCore 这个框架距离如今搭建也有几个月的时间了,在这个月初的时候 .NET Core 2.2 也已经发布了,若是你和我同样仍是采用的 .NET Core 2.1 版本的话,这里安装的 Versioning 版本最高只能到 2.3。

Install-Package Microsoft.AspNetCore.Mvc.Versioning
复制代码

        当咱们安装完成以后,就能够进行配置了。

public void ConfigureServices(IServiceCollection services)
{
    services.AddApiVersioning(o =>
    {
        o.ReportApiVersions = true;//return versions in a response header
        o.DefaultApiVersion = new ApiVersion(1, 0);//default version select 
        o.AssumeDefaultVersionWhenUnspecified = true;//if not specifying an api version,show the default version
    });
}
复制代码

        ReportApiVersions:这个配置是可选的,当咱们设置为 true 时,API 会在响应的 header 中返回版本信息。

        DefaultApiVersion:指定在请求中未指明版本时要使用的默认 API 版本。这将默认版本为1.0。

        AssumeDefaultVersionWhenUnspecified:这个配置项将用于在没有指明 API 版本的状况下提供请求,默认状况下,会请求默认版本的 API,例如,这里就会请求 1.0 版本的 API。

        这里,删除咱们以前的建立的 Area 和默认的 ValuesController,在 Controllers 文件夹下新增一个 v1 文件夹,将全部 v1 版本的 Controller 都建在这个目录下。新建一个 Controller,添加上 ApiVersion Attribute 指明当前的版本信息。由于我采用的方案是在 Url 中指明 API 版本,因此,咱们还须要在 Route 中修改咱们的路由属性以对应 API 的版本。这里的 v 只是一个默认的惯例,你也能够不添加。

[ApiVersion("1.0")]
[Route("api/v{version:apiVersion}/[controller]")]
[ApiController]
public class VaulesController : ControllerBase
{
}
复制代码

        当咱们修改好咱们的 Controller 以后,运行咱们的项目,你会发现,API 文档中显示的请求地址是不对的,难道是咱们的配置没起做用吗?经过 Swagger 自带的 API 测试工具测试下咱们的接口,原来这里请求的 Url 中已经包含了咱们定义的版本信息,当咱们指定错误的版本信息时,工具也会告诉咱们这个版本的接口不存在。

在 Url 中添加版本信息
        虽然咱们请求的 Url 中已经带上了版本信息,可是 API 文档上显示的请求地址倒是不许确的,强迫症,不能忍。这里,须要咱们修改生成 Swagger 文档的配置代码,将路由中的版本信息进行替换。从新运行咱们的项目,能够发现,文档显示的 Url 地址也已经正确了,自此,咱们建立带有版本控制的 API 也就完成了。

public void ConfigureServices(IServiceCollection services)
{
    services.AddSwaggerGen(s =>
    {
        //...

        //Show the api version in url address
        s.DocInclusionPredicate((version, apiDescription) =>
        {
            var values = apiDescription.RelativePath
                .Split('/')
                .Select(v => v.Replace("v{version}", version));

            apiDescription.RelativePath = string.Join("/", values);

            return true;
        });
    });
}
复制代码

修改 API 文档显示的请求 Url

总结

        本章使用了 Microsoft.AspNetCore.Mvc.Versioning 这一组件来实现咱们对于 API 版本控制的功能实现,可能你会有疑问,咱们直接在路由中写明版本信息不是更简单吗?在我看来,使用这一组件的目的,在于咱们能够以多种的方式实现 API 版本控制的目的,若是哪天你不想在 Url 中指明版本信息后,你能够很快的使用别的形式来完成 API 的版本控制。另外,直接在路由中写上版本信息,是否是会显得咱们比较 ‘low’,哈哈哈,开玩笑,最后祝你们双蛋快乐~~~

占坑

        做者:墨墨墨墨小宇
        我的简介:96年生人,出生于安徽某四线城市,毕业于Top 10000000 院校。.NET程序员,枪手死忠,喵星人。于2016年12月开始.NET程序员生涯,微软.NET技术的坚决坚持者,立志成为云养猫的少年中面向谷歌编程最厉害的.NET程序员。
        我的博客:yuiter.com
        博客园博客:www.cnblogs.com/danvic712

相关文章
相关标签/搜索