使用 Swagger UI 进行文档和测试 API





4.00/5 (5投票s)
开发人员在测试 API 时,经常通过浏览器请求或使用 POSTMAN、Advanced Rest Client (ARC) 等客户端进行测试。
引言
开发人员在测试 API 时,经常通过浏览器请求或使用 POSTMAN、Advanced Rest Client (ARC) 等客户端进行测试。为了展示 API 的功能,我们通常还需要通过一些需要额外工作的手段来暴露方法、描述以及相关数据结构。为了补充其中一个或两个功能,Swagger 会派上用场,它可以通过配置提供 API 文档和 API 测试。
Swagger 是一组围绕 OpenAPI 规范构建的开源工具,可用于自动生成 API 文档。Swagger UI 是一个可以在 API 生命周期中使用的工具。Swagger 提供易于导航的 API 资源文档和/或可视化,并允许直接在应用程序内部与 API 进行交互,从而使开发和测试工作以及最终用户体验无缝顺畅。在本文中,我将讨论如何在 API 中实现 Swagger,并举例说明 Swagger UI 的一些用例。我将使用 .NET Core 2.0 Web API 应用程序和 Visual Studio 2017 IDE。我创建了一个具有单个控制器和四个方法的示例 API 应用程序作为演示 可供下载。
配置
将 Swagger 集成到应用程序中相当简单,只需四个简单步骤即可完成,即安装、导入、注册和端点启用。
可以通过程序包管理器控制台安装该程序包,或者通过“NuGet 程序包管理器”菜单进行安装。
Install-Package Swashbuckle.AspNetCore
安装完成后,必须将 Swagger 导入到 Startup.cs 文件中。
using Swashbuckle.AspNetCore.Swagger;
然后,必须在 Startup.cs 文件的 ConfigureServices
方法中注册 Swagger 作为服务。
services.AddSwaggerGen(c =>
{
c.SwaggerDoc(_version, new Info { Title = _applicationName, Version = _version });
});
最后,应用程序必须在 Startup.cs 文件的 Configure
方法中启用 Swagger 的 JSON 和 UI 端点,以便最终用户可以通过 Swagger
UI 与 API 方法进行交互。
// Enable Swagger JSON endpoint.
app.UseSwagger();
// Enable swagger-ui (HTML, JS, CSS, etc.)
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint($"/swagger/{_version}/swagger.json",${_applicationName} {_version}");
});
Startup.cs 文件的完整列表显示在代码片段 1 中。
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Swashbuckle.AspNetCore.Swagger;
namespace Api
{
public class Startup
{
private string _applicationName;
private string _version;
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
var config = Configuration.GetSection("ApplicationSetting").Get<ApplicationSetting>();
_applicationName = config.Name;
_version = config.Version;
// Register the Swagger
services.AddSwaggerGen(c =>
{
c.SwaggerDoc(_version, new Info { Title = _applicationName, Version = _version });
// Set the comments path for the Swagger JSON and UI.
var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
c.IncludeXmlComments(xmlPath);
});
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseHsts();
}
// Enable Swagger JSON endpoint.
app.UseSwagger();
// Enable swagger-ui (HTML, JS, CSS, etc.), with the Swagger JSON endpoint.
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint($"/swagger/{_version}/swagger.json", $"{_applicationName} {_version}");
});
app.UseHttpsRedirection();
app.UseMvc();
}
}
}
安全
API 是已安全保护的,因此对 Swagger UI 及其端点进行安全保护,以防止未经授权的用户通过 Swagger UI 查看 API 文档,这是很有意义的。在这种情况下,必须使用常规的身份验证机制。例如,可以使用基于 WS-Federation 的身份验证,通过单点登录页面重定向用户进行身份验证。假设已放置了类似 WS-Federation 的身份验证,则 Configure
方法必须在 'app.UserSwagger();
' 代码行之前包含以下代码。
//Enable authentication
app.UseAuthentication();
app.Use(async (context, next) =>
{
if (!context.User.Identity.IsAuthenticated && context.Request.Path != "/signin-wsfed")
{
await context.ChallengeAsync(WsFederationDefaults.AuthenticationScheme);
}
else
{
await next();
}
});
// Enable Swagger JSON endpoint.
app.UseSwagger();
// Enable swagger-ui (HTML, JS, CSS, etc.), with the Swagger JSON endpoint.
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint($"/swagger/{_version}/swagger.json", $"{_applicationName} {_version}");
});
此外,ConfigureServices
方法必须包含以下代码,以触发与 WsFederation
相关的身份验证中间件。
//Authentication
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = WsFederationDefaults.AuthenticationScheme;
})
.AddCookie()
.AddWsFederation(options =>
{
options.Wtrealm = "ABC"; //use the site name
options.MetadataAddress = "XYZ"; //use the server that does the SSO
});
在身份验证到位后,对 Swagger UI 或 Swagger JSON 端点的调用必须经过身份验证中间件,因此 Swagger UI 仅限于合法用户。
可视化
完成上述四个步骤后,Swagger 即可使用。通过 https://:5001/swagger/v1/swagger.json 浏览即可在浏览器或 POSTMAN、Advanced Rest Client (ARC) 等客户端中以 JSON 格式获取数据。返回的 JSON 对象提供了 REST 方法(按控制器分隔)以及 API 使用的对象规范。响应示例为代码片段 2。
{
"swagger": "2.0",
"info": {
"version": "v1",
"title": "SmartStock API"
},
"paths": {
"/api/Stock": {
"get": {
"tags": [
"Stock"
],
"operationId": "GetStock",
"consumes": [],
"produces": [
"text/plain",
"application/json",
"text/json"
],
"parameters": [],
"responses": {
"200": {
"description": "Success",
"schema": {
"uniqueItems": false,
"type": "array",
"items": {
"$ref": "#/definitions/Stock"
}
}
}
}
},
"post": {
"tags": [
"Stock"
],
"operationId": "PostStock",
"consumes": [
"application/json-patch+json",
"application/json",
"text/json",
"application/*+json"
],
"produces": [
"text/plain",
"application/json",
"text/json"
],
"parameters": [
{
"name": "stock",
"in": "body",
"required": false,
"schema": {
"$ref": "#/definitions/Stock"
}
}
],
"responses": {
"200": {
"description": "Success",
"schema": {
"$ref": "#/definitions/Stock"
}
}
}
}
},
"/api/Stock/{symbol}": {
"get": {
"tags": [
"Stock"
],
"operationId": "GetStock",
"consumes": [],
"produces": [
"text/plain",
"application/json",
"text/json"
],
"parameters": [
{
"name": "symbol",
"in": "path",
"required": true,
"type": "string"
}
],
"responses": {
"200": {
"description": "Success",
"schema": {
"uniqueItems": false,
"type": "array",
"items": {
"$ref": "#/definitions/Stock"
}
}
}
}
}
},
"/api/Stock/{id}": {
"delete": {
"tags": [
"Stock"
],
"operationId": "DeleteStock",
"consumes": [],
"produces": [
"text/plain",
"application/json",
"text/json"
],
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"type": "string",
"format": "uuid"
}
],
"responses": {
"200": {
"description": "Success",
"schema": {
"type": "boolean"
}
}
}
}
},
"/api/Values": {
"get": {
"tags": [
"Values"
],
"operationId": "Get",
"consumes": [],
"produces": [
"text/plain",
"application/json",
"text/json"
],
"parameters": [],
"responses": {
"200": {
"description": "Success",
"schema": {
"uniqueItems": false,
"type": "array",
"items": {
"type": "string"
}
}
}
},
"deprecated": true
},
"post": {
"tags": [
"Values"
],
"operationId": "Post",
"consumes": [
"application/json-patch+json",
"application/json",
"text/json",
"application/*+json"
],
"produces": [],
"parameters": [
{
"name": "value",
"in": "body",
"required": false,
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "Success"
}
},
"deprecated": true
}
},
"/api/Values/{id}": {
"get": {
"tags": [
"Values"
],
"operationId": "Get",
"consumes": [],
"produces": [
"text/plain",
"application/json",
"text/json"
],
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"type": "integer",
"format": "int32"
}
],
"responses": {
"200": {
"description": "Success",
"schema": {
"type": "string"
}
}
},
"deprecated": true
},
"put": {
"tags": [
"Values"
],
"operationId": "Put",
"consumes": [
"application/json-patch+json",
"application/json",
"text/json",
"application/*+json"
],
"produces": [],
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"type": "integer",
"format": "int32"
},
{
"name": "value",
"in": "body",
"required": false,
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "Success"
}
},
"deprecated": true
},
"delete": {
"tags": [
"Values"
],
"operationId": "Delete",
"consumes": [],
"produces": [],
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"type": "integer",
"format": "int32"
}
],
"responses": {
"200": {
"description": "Success"
}
},
"deprecated": true
}
}
},
"definitions": {
"Stock": {
"type": "object",
"properties": {
"id": {
"format": "uuid",
"type": "string"
},
"ticker": {
"type": "string"
},
"company": {
"type": "string"
},
"price": {
"format": "double",
"type": "number"
}
}
}
}
}
可以通过导航到 https://:5001/swagger/index.html 来访问 Swagger
UI,用户可以在其中以交互式格式可视化 JSON 响应中提供的数据。此 UI 还支持实际执行 REST 方法。
测试
Swagger 提供了无需任何工具即可测试 API 的功能。例如,单击 GET
(图 2 中的第一个选项卡)会展开该方法。通过单击“Try it Out”(试用),然后单击“Execute”(执行),Swagger 会触发对 /api/stock 中 'get' 方法的调用。请注意,演示应用程序中有一个名为 'StockController
' 的控制器。结果显示在 UI 中,也可以下载。图 3 显示了 get
方法的结果屏幕。UI 中暴露的任何方法都可以在 UI 本身中执行,从而使我们能够直接从 API 本身测试 API。
对属性的支持
路由属性、Http* 属性以及任何其他数据注解都得到默认支持。图 4 显示了用 HttpGet
、HttpPost
、HttpDelete
装饰的方法如何在 Swagger
UI 中显示。
Swagger
与 .NET 中的属性同步。例如,如果一个控制器类用 [Obsolete]
属性进行装饰,UI 将反映该控制器是“过时的”。图 5 显示 ValuesController
被标记为“Obsolete
”,并且 Swagger
UI 反映了这一点,因此无法单击。当 API 在不破坏现有代码的情况下淘汰某些功能时,此功能非常有用。
支持 XML 文档
通过一些配置更改,Swagger 支持 UI 中的文档。通过将以下代码行添加到 *.csproj 文件中,即可生成 XML 文档。
<Property Group>
....
<GenerateDocumentationFile>
true
</GenerateDocumentationFile>
<NoWarn>
$(NoWarn);1591
</NoWarn>
<Property Group>
services.AddSwaggerGen(c =>
{
...
var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
c.IncludeXmlComments(xmlPath);
}
);
通过使用 XML 路径的更改,Swagger 在 UI 中公开了 XML 文档。请注意,可以通过 XML 注释来为代码添加文档,这些注释直接放在代码块之前。例如,以下代码块显示了 GetStock() 方法的 XML 注释,该注释在 Swagger UI 中查看时,会为该方法简单显示“此方法返回股票列表”的描述,如图 6 所示。
<summary>/// This method returns a list of stocks /// </summary>
[HttpGet] public IEnumerable<stock> GetStock()
{
return Data.DbContext.GetStocks();
}
Swagger 支持所有主流浏览器,并且可以在本地或 Web 环境中运行。UI 的外观和感觉是可自定义的。
我之前展示了它在本地计算机上的工作方式。Swagger
UI 在云中也能很好地工作。部署到 Azure 的同一应用程序 https://smartstockapi.azurewebsites.net/swagger/index.html 的运行方式与在我的本地计算机上一样。
资源/材料/参考
- swagger.io
- 开始使用 Swashbuckle 和 ASP.NET Core
- 使用 WS-Federation 在 ASP.NET Core 中对用户进行身份验证
- GitHub 中的代码
- 通过 Swagger 文档改进 API 的开发人员体验