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

 一、前言

   在上一篇的文章中,主要是搭建了我们的开发环境,同时创建了我们的项目模板框架。在整个前后端分离的项目中,后端的 API 接口至关重要,它是前端与后端之间进行沟通的媒介,如何构建一个 “好用” 的 API 接口,是需要我们后端人员好好思考的。
  在系统迭代的整个过程中,不可避免的会添加新的资源,或是修改现有的资源,后端接口作为暴露给外界的服务,变动的越小,对服务的使用方造成的印象就越小,因此,如何对我们的 API 接口进行合适的版本控制,我们势必需要首先考虑。

  系列目录地址:ASP.NET Core 项目实战
  仓储地址:https://github.com/Lanesra712/Grapefruit.VuCore

 二、Step by Step

  项目总是在不断迭代的,某些时候,因为业务发展的需要,需要将现有的接口进行升级,而原有的接口却不能立刻停止使用。比如说,你开发了一个接口提供给爱啪啪 1.0 版本使用,后来爱啪啪的版本迭代了,需要接口返回的数据与原先 1.0 版本返回的数据不同了,这时候,接口肯定是需要升级的,可是如果直接升级原有的接口,还在使用 1.0 版本的用户不就 GG 了,因此,如何做到既可以让 1.0 版本的用户使用,也可以让 2.0 版本的用户使用就需要好好考虑了,常见的解决方案,主要有以下几种。

  a)使用不同的 API 名称

  最简单粗暴,需要变更接口逻辑时就重新起个 API 名称,新的版本调用新的 API 名称,旧的版本调用旧的 API 名称。

https://yuiter.com/api/Secret/Login ##爱啪啪 1.0
https://yuiter.com/api/Secret/NewLogin ##爱啪啪 2.0

  b)在 Url 中标明版本号

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

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

  c)请求参数中添加版本信息

  将 API 的版本信息作为请求的一个参数传递,通过指定参数值来确定请求的 API 版本。

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

  d)在 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 了。

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 类会导致启动失败。

  在 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 文档的目的,就是为了让前端知道请求的方法地址是什么,需要传递什么参数,而现在,并没有办法显示出我们对于参数以及方法的注释,通过查看 Swashbuckle.AspNetCore 的 github 首页可以看到,我们可以通过配置,将生成的 json 文件中包含我们对于 Controller or Action 的 Xml 注释内容,从而达到显示注释信息的功能(最终呈现的 Swagger Doc 是根据之前我们定义的这个 “/swagger/v1/swagger.json” json 文件来生成的)。

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

  创建好 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。

  当 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
{
}

  

  这样的话,和最开始直接在路由信息中写死版本信息其实也就没什么差别了,上网搜了搜,发现巨硬爸爸,也早已为我们准备好了实现版本控制 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 中已经带上了版本信息,但是 API 文档上显示的请求地址却是不准确的,强迫症,不能忍。这里,需要我们修改生成 Swagger 文档的配置代码,将路由中的版本信息进行替换。重新运行我们的项目,可以发现,文档显示的 Url 地址也已经正确了,自此,我们创建带有版本控制的 API 也就完成了。

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

        //Show the api version in url address
      s.DocInclusionPredicate((version, apiDescription) =>
      {
        if (!version.Equals(apiDescription.GroupName))
          return false;         var values = apiDescription.RelativePath
          .Split('/')
          .Select(v => v.Replace("v{version}", apiDescription.GroupName));         apiDescription.RelativePath = string.Join("/", values);
        return true;
      );
    });
}

 三、总结

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

转载于:https://www.cnblogs.com/danvic712/p/10176823.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
JS JSP ASP .NET J2AM API接口和返回的版本 目前所有版本的JS JSP ASP .NET J2AM 都是提供源代码的,对于一些脚本语言来说,直接解压缩之后就可以使用了,不需要什么安装步骤。另外一些需要编译的语言,则提供了编译用的 shell 文件(Linux/Unix 下使用)和 bat 文件(Windows 下使用),或者直接提供编译好的二进制库文件。 不过为了让读者能够更清楚如何安装,我们还是对每种语言的安装都做详细的讲解,你可以在安装列表里找到你感兴趣的语言的安装方法。 示例 如果你已经把 JS JSP ASP .NET J2AM 安装好了,那么接下来就让我们开始第一个小程序吧。按照惯例,第一个演示程序几乎总是 HelloWorld,我们也不想打破这个惯例,不过对于 PHPRPC 来说,有服务器端就要有客户端,否则我们就没有什么好演示的啦,所以我们的第一个演示程序实际上是两个,一个是服务器端,另一个是客户端。我们都先用 PHP 语言来写好了。 服务器端 view plaincopy to clipboardprint? <?php include ("php/phprpc_server.php"); function HelloWorld() { return 'Hello World!'; } $server = new PHPRPC_Server(); $server->add('HelloWorld'); $server->start(); ?> 客户端 view plaincopy to clipboardprint? <?php include ("php/phprpc_client.php"); $client = new PHPRPC_Client('http://127.0.0.1/server.php'); echo $client->HelloWorld(); ?> 对于服务器端程序,我们应该将它命名为 server.php(这是因为客户端调用时用的是这个名字,而不是 PHPRPC 的什么规定),然后把它放在本地 Web 服务器的根目录下,并保证服务器可以正常运行 PHP 程序,之后在浏览器或命令行下运行客户端程序,你就可以看到结果了。 这两个程序几乎简单到无需解释的地步,所以如果你已经明白它们的意思,那么就可以直接跳过下面的解释,继续看后面的例子。 服务器端第 1 句是将 它的服务器端程序包含到你的程序里,之后的 2 - 4 句是定义一个远程调用的函数,你会发现它与本地函数没有任何区别。第 5 句是创建服务器端对象,第 6 句是添加要发布的方法,这里添加的就是刚刚定义的 HelloWorld 函数,在 PHP 中,添加的发布方法是函数名的字符串表示,在其它语言中可能略有不同。第 7 句是启动服务。 客户端就更简单了,第 1 句是将 它的客户端程序包含到你的程序里。第 2 句是创建客户端对象,其中的参数就是服务器端的地址。第 3 句是对远程方法(函数)的调用,之后通过 echo 将它显示出来。如果顺利的话,执行后你就会看到输出的 Hello World!。 上面的例子是发布的是函数,下面我们来看一下类中的静态方法如何发布: view plaincopy to clipboardprint? <?php include ("php/phprpc_server.php"); class Hello { static function HelloWorld() { return 'Hello World!'; } } $server = new PHPRPC_Server(); $server->add('HelloWorld', 'Hello'); $server->start(); ?> 这个服务器端只要它的名字与发布的地址与上面那个发布函数的例子一样的话,上面的那个客户端就可以得到同样的结果,也就是说,在客户端看来是没有任何区别的。 它并不是只可以在 PHP 中使用,它同样支持其它语言的服务器和客户端,而且还可以无差别的相互调用。 现在我们来看一下如何在 Java 中调用这个 PHP 的服务器方法: view plaincopy to clipboardprint? import org.phprpc.*; interface IHello { public String helloWorld(); } public class HelloWorld
ASP API 接口接收与返回 是一个轻型的、安全的、跨网际的、跨语言的、跨平台的、跨环境的、跨域的、支持复杂对象传输的、支持引用参数传递的、支持内容输出重定向的、支持分级错误处理的、支持会话的、面向服务的高性能远程过程调用协议。 该版本直接解压后就可以使用,其中 属于公共文件。不论是客户端还是服务器端都需要这些文件。 是客户端文件,如果你只需要使用客户端,那么只要有上面那些公共文件和这个文件就可以使用了,使用时,直接在你的程序中包含 phprpc_client.php 就可以,公共文件不需要单独包含。 这三个文件是服务器端需要的文件。 其中 dhparams 目录中包含的是加密传输时用来生成密钥的参数 dhparams.php 是用来读取 dhparams 目录中文件的类。 phprpc_server.php 是服务器端,如果你要使用 PHP 来发布 PHPRPC 服务,只需要包含这个文件就可以了。公共文件和 dhparams.php 都不需要单独包含。 PHP 4.3+、PHP 5、PHP 6 客户端要求开启 socket 扩展。 服务器端需要有 IIS、Apache、lighttpd 等可以运行 PHP 程序的 Web 服务器。 如果服务器端需要加密传输的能力,必须要保证 session 配置正确。 <?php include('php/phprpc_server.php'); //加载文件 function hello($name) { return'Hello ' . $name; } $server = new PHPRPC_Server(); //创建服务端 $server->add(array('hello', 'md5', 'sha1')); //数组形式一次注册多个函数 $server->add('trim'); //单一注册 $server->start(); //开启服务 ?> <?php include ("php/phprpc_client.php"); //加载文件 $client = new PHPRPC_Client('http://127.0.0.1/server.php'); //创建客户端 并连接服务端文件 echo$client->Hello("word"); //调用方法 返回 hello word ?> -------------------------------------------------- --------------------------------------------------- ------------------------------ 服务端其他说明: <?php include('php/phprpc_server.php'); //加载文件 function hello($name) { return'Hello ' . $name; } class Example1 { staticfunction foo() { return'foo'; } function bar() { return'bar'; } } $server = new PHPRPC_Server(); //创建服务端 $server->add('foo', 'Example1'); //静态方法直接调用 $server->add('bar', new Example1()); //非静态方法 需要实例化 //注册别名调用 $server->add('hello', NULL, 'hi'); //第三参数是函数的别名 客户端通过别名来调用函数 $server->add('foo', 'Example1', 'ex1_foo'); $server->add('bar', new Example1(), 'ex1_bar'); $server->setCharset('UTF-8'); //设置编码 $server->setDebugMode(true); //打印错误 $server->setEnableGZIP(true); //启动压缩输出虽然可以让传输的数据量减少,但是它会占用更多的内存和 CPU,因此它默认是关闭的。 $server->start(); //开启服务 ?> -------------------------------------------------- --------------------------------------------------- ---------------------------
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值