两种方案
一、Swagger 配置 web Api 接口文档美化
二、通过NodeJS 发布Swagger UI 配置api 文档
先说一下简单的 Swagger 配置 web Api
Swagger-UI本身只提供在线测试功能,要集成它还需要告诉它本项目提供的各种服务和参数信息。这里就需要一些工作量了,不过好在许多第三方库已经给我们完成了这一工作。我这里用的是Swashbuckle,使用它也比较简单,直接使用Nuget添加其程序包即可:
1、初始化包 PM> Install-Package Swashbuckle
增加该程序包时,它本身会把自己相应的一些注册的代码添加到项目中,虽然我们可以不太关心这些操作,但有的时候还是需要修改一些相关的配置的。
2、初始化包后App_Start会添加 ,SwaggerConfig 代码如下:
using System.Web.Http;
using WebActivatorEx;
using WebApp;
using Swashbuckle.Application;
[assembly: PreApplicationStartMethod(typeof(SwaggerConfig), "Register")]
namespace WebApp
{
public class SwaggerConfig
{
public static void Register()
{
var thisAssembly = typeof(SwaggerConfig).Assembly;
GlobalConfiguration.Configuration
.EnableSwagger(c =>
{
c.SingleApiVersion("v1", "WebApp");
})
.EnableSwaggerUi(c =>
{
GetXmlCommentsPath();
});
}
private static string GetXmlCommentsPath()
{
return $@"{System.AppDomain.CurrentDomain.BaseDirectory}\bin\WebApi.XML";
}
}
}
3、集成XML注释
api 应用 ->右键->属性->生成->输出-配置XML
4、运行程序 地址栏请求:http://localhost:5746/swagger/ 逼格很高啊
到此第一种方法完成
开始改造第一种方法 删除SwaggerConfig ,修改Startup 代码如下:
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
ConfigureAuth(app);
HttpConfiguration config = new HttpConfiguration();
WebApiConfig.Register(config);
config.EnableSwagger(c =>
{
c.SingleApiVersion("v1", "WebAPI");
c.IncludeXmlComments(GetXmlCommentsPath());
c.ResolveConflictingActions(x => x.First());
}).EnableSwaggerUi();
app.UseWebApi(config);
}
private static string GetXmlCommentsPath()
{
return $@"{System.AppDomain.CurrentDomain.BaseDirectory}\bin\WebApi.XML";
}
解决方法如下:
you will need to import the Microsoft ASP.NET Web API 2 OWIN Self-Host Nuget package.
第一种方法改造完成,下面学习SwaggerUI 及Swagger Edit ,需要用到NodeJS ,下面是在网上找的配置方案,真的感谢
在团队开发中,一个好的 API 文档可以减少很多交流成本,也可以使一个新人快速上手业务。
总结一句话就是好用,逼格高。下面我将总结一下如何快速在本地搭建一个基于Node和Swagger UI的 API 的文档工具
git clone https://github.com/swagger-api/swagger-ui.git
mkdir node_app
➜ ~ ✗ >cd node_ap
➜ ~/node_app ✗ >npm init
// 下面的看你心情填写
name: (node_app) node_app
version: (1.0.0)
description:
entry point: (index.js)
test command:
git repository:
keywords:
author:
license: (ISC)
➜ ~/node_app git:(master) ✗ >npm install express --save
~/node_app git:(master) ✗ >vim index.js
var express = require('express');
var app = express();
app.get('/', function (req, res) {
res.send('Hello World!');
});
app.listen(3000, function () {
console.log('Example app listening on port 3000!');
});
➜ ~/node_app git:(master) ✗ >mkdir public
➜ ~/node_app git:(master) ✗ >cd public
修改路由
➜ ~/node_app/public git:(master) ✗ >vim ../index.js
//在文件第三行插入下面这句话
app.use('/static', express.static('public'));
把下载好的Swagger UI 文件中dist 目录下的文件全部复制到 public 文件夹下。
~/node_app git:(master) ✗ >node index.js
到此为止,你已经把官方的 demo 在本地配置好了。当然你也可以吧这个搭建在服务器上
url = "http://petstore.swagger.io/v2/swagger.json";
为url = "/static/test.json";
重启 node 服务,浏览器中打开http://localhost:3000/static/index.html
就是你自己写的 api 文档了
test.json 代码如下:做一下概要的说明
{
"swagger": "2.0",
"info": {
"title": "Uber API",
"description": "Move your app forward with the Uber API",
"version": "1.0.0"
},
"host": "115.29.161.201:8077/",
"schemes": [
"http"
],
"basePath": "/api",
"produces": [
"application/json"
],
"paths": {
"/products": {
"get": {
"summary": "Product Types",
"description": "The Products endpoint returns information about the *Uber* products\noffered at a given location. The response includes the display name\nand other details about each product, and lists the products in the\nproper display order.\n",
"parameters": [
{
"name": "latitude",
"in": "query",
"description": "Latitude component of location.",
"required": true,
"type": "number",
"format": "double"
},
{
"name": "longitude",
"in": "query",
"description": "Longitude component of location.",
"required": true,
"type": "number",
"format": "double"
}
],
"tags": [
"Products"
],
"responses": {
"200": {
"description": "An array of products",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/Product"
}
}
},
"default": {
"description": "Unexpected error",
"schema": {
"$ref": "#/definitions/Error"
}
}
}
}
},
"/estimates/price": {
"get": {
"summary": "Price Estimates",
"description": "The Price Estimates endpoint returns an estimated price range\nfor each product offered at a given location. The price estimate is\nprovided as a formatted string with the full price range and the localized\ncurrency symbol.
The response also includes low and high estimates,\nand the [ISO 4217](http://en.wikipedia.org/wiki/ISO_4217) currency code for\nsituations requiring currency conversion. When surge is active for a particular\nproduct, its surge_multiplier will be greater than 1, but the price estimate\nalready factors in this multiplier.\n",
"parameters": [
{
"name": "start_latitude",
"in": "query",
"description": "Latitude component of start location.",
"required": true,
"type": "number",
"format": "double"
},
{
"name": "start_longitude",
"in": "query",
"description": "Longitude component of start location.",
"required": true,
"type": "number",
"format": "double"
},
{
"name": "end_latitude",
"in": "query",
"description": "Latitude component of end location.",
"required": true,
"type": "number",
"format": "double"
},
{
"name": "end_longitude",
"in": "query",
"description": "Longitude component of end location.",
"required": true,
"type": "number",
"format": "double"
}
],
"tags": [
"Estimates"
],
"responses": {
"200": {
"description": "An array of price estimates by product",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/PriceEstimate"
}
}
},
"default": {
"description": "Unexpected error",
"schema": {
"$ref": "#/definitions/Error"
}
}
}
}
},
"/estimates/time": {
"get": {
"summary": "Time Estimates",
"description": "The Time Estimates endpoint returns ETAs for all products offered at a given location, with the responses expressed as integers in seconds. We recommend that this endpoint be called every minute to provide the most accurate, up-to-date ETAs.",
"parameters": [
{
"name": "start_latitude",
"in": "query",
"description": "Latitude component of start location.",
"required": true,
"type": "number",
"format": "double"
},
{
"name": "start_longitude",
"in": "query",
"description": "Longitude component of start location.",
"required": true,
"type": "number",
"format": "double"
},
{
"name": "customer_uuid",
"in": "query",
"type": "string",
"format": "uuid",
"description": "Unique customer identifier to be used for experience customization."
},
{
"name": "product_id",
"in": "query",
"type": "string",
"description": "Unique identifier representing a specific product for a given latitude & longitude."
}
],
"tags": [
"Estimates"
],
"responses": {
"200": {
"description": "An array of products",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/Product"
}
}
},
"default": {
"description": "Unexpected error",
"schema": {
"$ref": "#/definitions/Error"
}
}
}
}
},
"/me": {
"get": {
"summary": "User Profile",
"description": "The User Profile endpoint returns information about the Uber user that has authorized with the application.",
"tags": [
"User"
],
"responses": {
"200": {
"description": "Profile information for a user",
"schema": {
"$ref": "#/definitions/Profile"
}
},
"default": {
"description": "Unexpected error",
"schema": {
"$ref": "#/definitions/Error"
}
}
}
}
},
"/Values/Get/": {
"get": {
"summary": "Values Profile",
"description": "自己测试",
"parameters": [
{
"name": "id",
"in": "query",
"description": "Latitude component of start location.",
"required": true,
"type": "number",
"format": "int32"
}],
"tags": [
"Values"
],
"responses": {
"200": {
"description": "值得概要信息",
"schema": {
"$ref": "#/definitions/valuesget"
}
},
"default": {
"description": "意外错误",
"schema": {
"$ref": "#/definitions/Error"
}
}
}
}
},
"/history": {
"get": {
"summary": "User Activity",
"description": "The User Activity endpoint returns data about a user's lifetime activity with Uber. The response will include pickup locations and times, dropoff locations and times, the distance of past requests, and information about which products were requested.
The history array in the response will have a maximum length based on the limit parameter. The response value count may exceed limit, therefore subsequent API requests may be necessary.",
"parameters": [
{
"name": "offset",
"in": "query",
"type": "integer",
"format": "int32",
"description": "Offset the list of returned results by this amount. Default is zero."
},
{
"name": "limit",
"in": "query",
"type": "integer",
"format": "int32",
"description": "Number of items to retrieve. Default is 5, maximum is 100."
}
],
"tags": [
"User"
],
"responses": {
"200": {
"description": "History information for the given user",
"schema": {
"$ref": "#/definitions/Activities"
}
},
"default": {
"description": "Unexpected error",
"schema": {
"$ref": "#/definitions/Error"
}
}
}
}
}
},
"definitions": {
"Product": {
"type": "object",
"properties": {
"product_id": {
"type": "string",
"description": "Unique identifier representing a specific product for a given latitude & longitude. For example, uberX in San Francisco will have a different product_id than uberX in Los Angeles."
},
"description": {
"type": "string",
"description": "Description of product."
},
"display_name": {
"type": "string",
"description": "Display name of product."
},
"capacity": {
"type": "string",
"description": "Capacity of product. For example, 4 people."
},
"image": {
"type": "string",
"description": "Image URL representing the product."
}
}
},
"PriceEstimate": {
"type": "object",
"properties": {
"product_id": {
"type": "string",
"description": "Unique identifier representing a specific product for a given latitude & longitude. For example, uberX in San Francisco will have a different product_id than uberX in Los Angeles"
},
"currency_code": {
"type": "string",
"description": "[ISO 4217](http://en.wikipedia.org/wiki/ISO_4217) currency code."
},
"display_name": {
"type": "string",
"description": "Display name of product."
},
"estimate": {
"type": "string",
"description": "Formatted string of estimate in local currency of the start location. Estimate could be a range, a single number (flat rate) or \"Metered\" for TAXI."
},
"low_estimate": {
"type": "number",
"description": "Lower bound of the estimated price."
},
"high_estimate": {
"type": "number",
"description": "Upper bound of the estimated price."
},
"surge_multiplier": {
"type": "number",
"description": "Expected surge multiplier. Surge is active if surge_multiplier is greater than 1. Price estimate already factors in the surge multiplier."
}
}
},
"Profile": {
"type": "object",
"properties": {
"first_name": {
"type": "string",
"description": "First name of the Uber user."
},
"last_name": {
"type": "string",
"description": "Last name of the Uber user."
},
"email": {
"type": "string",
"description": "Email address of the Uber user"
},
"picture": {
"type": "string",
"description": "Image URL of the Uber user."
},
"promo_code": {
"type": "string",
"description": "Promo code of the Uber user."
}
}
},
"Activity": {
"type": "object",
"properties": {
"uuid": {
"type": "string",
"description": "Unique identifier for the activity"
}
}
},
"valuesget": {
"type": "object",
"properties": {
"Vanme": {
"type": "string",
"description": "名字"
}
}
},
"Activities": {
"type": "object",
"properties": {
"offset": {
"type": "integer",
"format": "int32",
"description": "Position in pagination."
},
"limit": {
"type": "integer",
"format": "int32",
"description": "Number of items to retrieve (100 max)."
},
"count": {
"type": "integer",
"format": "int32",
"description": "Total number of items available."
},
"history": {
"type": "array",
"items": {
"$ref": "#/definitions/Activity"
}
}
}
},
"Error": {
"type": "object",
"properties": {
"code": {
"type": "integer",
"format": "int32"
},
"message": {
"type": "string"
},
"fields": {
"type": "string"
}
}
}
}
}
host:配置程序发布地址
schemes :请求方式 http 或者https
tags:页签名称
其他的看个10分钟作用的基本就明白了
我请求web api 遇到了一个跨域的问题 ,永远显示 no content ,没有response
F12 看了一下
解决办法修改web.config
//public string Options()
//{
// return null; // HTTP 200 response with empty body
//}
我没有添加也成功了
发布到服务器上需要给api 发布程序和Swagger UI发布程序 配置防火墙入站规则,排除端口。 结束,下来继续研究,遇到问题也会更新。
继续补充Swagger授权
Authorization
我们写的api接口需要授权才可以访问,如果没有授权我们通过swagger 请求会提示如下:
没有授权是因为我在api接口里面添加了
[Authorize]
public class ValuesController : ApiController
{
// GET api/values
public IEnumerable Get()
{
return new string[] { "value1", "value2" };
}
// GET api/values/5
public string Get(int id)
{
return "value";
}
// POST api/values
public void Post([FromBody]string value)
{
}
// PUT api/values/5
public void Put(int id, [FromBody]string value)
{
}
// DELETE api/values/5
public void Delete(int id)
{
}
}
需要
[Authorize]
在swagger test.json 修改如下:
{
"swagger": "2.0",
"info": {
"title": "Uber API",
"description": "Move your app forward with the Uber API",
"version": "1.0.0"
},
"host": "localhost:7722",
"schemes": [
"http"
],
"basePath": "/api",
"produces": [
"application/json"
],
"paths": {
"/products": {
"get": {
"summary": "Product Types",
"description": "The Products endpoint returns information about the *Uber* products\noffered at a given location. The response includes the display name\nand other details about each product, and lists the products in the\nproper display order.\n",
"parameters": [
{
"name": "latitude",
"in": "query",
"description": "Latitude component of location.",
"required": true,
"type": "number",
"format": "double"
},
{
"name": "longitude",
"in": "query",
"description": "Longitude component of location.",
"required": true,
"type": "number",
"format": "double"
}
],
"tags": [
"Products"
],
"responses": {
"200": {
"description": "An array of products",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/Product"
}
}
},
"default": {
"description": "Unexpected error",
"schema": {
"$ref": "#/definitions/Error"
}
}
}
}
},
"/estimates/price": {
"get": {
"summary": "Price Estimates",
"description": "The Price Estimates endpoint returns an estimated price range\nfor each product offered at a given location. The price estimate is\nprovided as a formatted string with the full price range and the localized\ncurrency symbol.
The response also includes low and high estimates,\nand the [ISO 4217](http://en.wikipedia.org/wiki/ISO_4217) currency code for\nsituations requiring currency conversion. When surge is active for a particular\nproduct, its surge_multiplier will be greater than 1, but the price estimate\nalready factors in this multiplier.\n",
"parameters": [
{
"name": "start_latitude",
"in": "query",
"description": "Latitude component of start location.",
"required": true,
"type": "number",
"format": "double"
},
{
"name": "start_longitude",
"in": "query",
"description": "Longitude component of start location.",
"required": true,
"type": "number",
"format": "double"
},
{
"name": "end_latitude",
"in": "query",
"description": "Latitude component of end location.",
"required": true,
"type": "number",
"format": "double"
},
{
"name": "end_longitude",
"in": "query",
"description": "Longitude component of end location.",
"required": true,
"type": "number",
"format": "double"
}
],
"tags": [
"Estimates"
],
"responses": {
"200": {
"description": "An array of price estimates by product",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/PriceEstimate"
}
}
},
"default": {
"description": "Unexpected error",
"schema": {
"$ref": "#/definitions/Error"
}
}
}
}
},
"/estimates/time": {
"get": {
"summary": "Time Estimates",
"description": "The Time Estimates endpoint returns ETAs for all products offered at a given location, with the responses expressed as integers in seconds. We recommend that this endpoint be called every minute to provide the most accurate, up-to-date ETAs.",
"parameters": [
{
"name": "start_latitude",
"in": "query",
"description": "Latitude component of start location.",
"required": true,
"type": "number",
"format": "double"
},
{
"name": "start_longitude",
"in": "query",
"description": "Longitude component of start location.",
"required": true,
"type": "number",
"format": "double"
},
{
"name": "customer_uuid",
"in": "query",
"type": "string",
"format": "uuid",
"description": "Unique customer identifier to be used for experience customization."
},
{
"name": "product_id",
"in": "query",
"type": "string",
"description": "Unique identifier representing a specific product for a given latitude & longitude."
}
],
"tags": [
"Estimates"
],
"responses": {
"200": {
"description": "An array of products",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/Product"
}
}
},
"default": {
"description": "Unexpected error",
"schema": {
"$ref": "#/definitions/Error"
}
}
}
}
},
"/me": {
"get": {
"summary": "User Profile",
"description": "The User Profile endpoint returns information about the Uber user that has authorized with the application.",
"tags": [
"User"
],
"responses": {
"200": {
"description": "Profile information for a user",
"schema": {
"$ref": "#/definitions/Profile"
}
},
"default": {
"description": "Unexpected error",
"schema": {
"$ref": "#/definitions/Error"
}
}
}
}
},
"/Values": {
"get": {
"summary": "Values Profile",
"description": "自己测试",
"parameters": [
{
"name": "id",
"in": "query",
"description": "Latitude component of start location.",
"required": true,
"type": "number",
"format": "int32"
}],
"tags": [
"Values"
],
"responses": {
"200": {
"description": "值得概要信息",
"schema": {
"$ref": "#/definitions/valuesget"
}
},
"default": {
"description": "意外错误",
"schema": {
"$ref": "#/definitions/Error"
}
}
},
"security": [
{
"petstore_auth": [
"write:pets",
"read:pets"
]
}
]
}
},
"/history": {
"get": {
"summary": "User Activity",
"description": "The User Activity endpoint returns data about a user's lifetime activity with Uber. The response will include pickup locations and times, dropoff locations and times, the distance of past requests, and information about which products were requested.
The history array in the response will have a maximum length based on the limit parameter. The response value count may exceed limit, therefore subsequent API requests may be necessary.",
"parameters": [
{
"name": "offset",
"in": "query",
"type": "integer",
"format": "int32",
"description": "Offset the list of returned results by this amount. Default is zero."
},
{
"name": "limit",
"in": "query",
"type": "integer",
"format": "int32",
"description": "Number of items to retrieve. Default is 5, maximum is 100."
}
],
"tags": [
"User"
],
"responses": {
"200": {
"description": "History information for the given user",
"schema": {
"$ref": "#/definitions/Activities"
}
},
"default": {
"description": "Unexpected error",
"schema": {
"$ref": "#/definitions/Error"
}
}
}
}
}
},
"securityDefinitions": {
"petstore_auth": {
"type": "oauth2",
"authorizationUrl": "http://localhost:7722/Login/Index",
"flow": "implicit",
"scopes": {
"write:pets": "modify pets in your account",
"read:pets": "read your pets"
}
}
},
"definitions": {
"Product": {
"type": "object",
"properties": {
"product_id": {
"type": "string",
"description": "Unique identifier representing a specific product for a given latitude & longitude. For example, uberX in San Francisco will have a different product_id than uberX in Los Angeles."
},
"description": {
"type": "string",
"description": "Description of product."
},
"display_name": {
"type": "string",
"description": "Display name of product."
},
"capacity": {
"type": "string",
"description": "Capacity of product. For example, 4 people."
},
"image": {
"type": "string",
"description": "Image URL representing the product."
}
}
},
"PriceEstimate": {
"type": "object",
"properties": {
"product_id": {
"type": "string",
"description": "Unique identifier representing a specific product for a given latitude & longitude. For example, uberX in San Francisco will have a different product_id than uberX in Los Angeles"
},
"currency_code": {
"type": "string",
"description": "[ISO 4217](http://en.wikipedia.org/wiki/ISO_4217) currency code."
},
"display_name": {
"type": "string",
"description": "Display name of product."
},
"estimate": {
"type": "string",
"description": "Formatted string of estimate in local currency of the start location. Estimate could be a range, a single number (flat rate) or \"Metered\" for TAXI."
},
"low_estimate": {
"type": "number",
"description": "Lower bound of the estimated price."
},
"high_estimate": {
"type": "number",
"description": "Upper bound of the estimated price."
},
"surge_multiplier": {
"type": "number",
"description": "Expected surge multiplier. Surge is active if surge_multiplier is greater than 1. Price estimate already factors in the surge multiplier."
}
}
},
"Profile": {
"type": "object",
"properties": {
"first_name": {
"type": "string",
"description": "First name of the Uber user."
},
"last_name": {
"type": "string",
"description": "Last name of the Uber user."
},
"email": {
"type": "string",
"description": "Email address of the Uber user"
},
"picture": {
"type": "string",
"description": "Image URL of the Uber user."
},
"promo_code": {
"type": "string",
"description": "Promo code of the Uber user."
}
}
},
"Activity": {
"type": "object",
"properties": {
"uuid": {
"type": "string",
"description": "Unique identifier for the activity"
}
}
},
"valuesget": {
"type": "object",
"properties": {
"Vanme": {
"type": "string",
"description": "名字"
}
}
},
"Activities": {
"type": "object",
"properties": {
"offset": {
"type": "integer",
"format": "int32",
"description": "Position in pagination."
},
"limit": {
"type": "integer",
"format": "int32",
"description": "Number of items to retrieve (100 max)."
},
"count": {
"type": "integer",
"format": "int32",
"description": "Total number of items available."
},
"history": {
"type": "array",
"items": {
"$ref": "#/definitions/Activity"
}
}
}
},
"Error": {
"type": "object",
"properties": {
"code": {
"type": "integer",
"format": "int32"
},
"message": {
"type": "string"
},
"fields": {
"type": "string"
}
}
}
}
}
"securityDefinitions": {
"petstore_auth": {
"type": "oauth2",
"authorizationUrl": "http://localhost:7722/Login/Index",
"flow": "implicit",
"scopes": {
"write:pets": "modify pets in your account",
"read:pets": "read your pets"
}
}
},
authorizationUrl 地址是我们做的授权页面,自己开发一个授权页面概要代码如下:access_token 是swagger-oauth.js 里面接收的,值是从我们实际数据查询的
[HttpPost]
public ActionResult LoginOut(User info)
{
var url = string.Format(@"{0}?access_token=cyaosGJuKo54zlLvYqBslSJ_VhfsLDvdW9Eherl7ceA1L9JpJjcCG268OxrkwyMGSbqmwUyI9mWPXNQxWP-XX4XsRFpg6qCDQ3YgZf5Jtsy888cZvEbGzY6U30UsTE7BrjkWE4KNQPgwG2GiEhzNEab_nRizEyc9c-NDHPcoVnRPOXg1mjr-q47PkugzbfRCZynFXvg1DrvMAGNBIIBIiHbjSBPupsJ3VpyC4InClEkR19ndQgeoWedfZCZLLeDTKaEarGTmxCkEplZH5M27nHWMwyy8kQHxO2yjVoBkdUeSfLGjvpk7koTgsFzZmwG_wLEQg-aOMpGToyy03jBEsWQKxRXR3x6rvYsQaMYkGu5Zfax8qNyIADTlgDHuRV5DQhvoYhzBanZ5VcDJXNkFMnt7BUml9BfFE28kDtiZlrQjrjf4kdu8ZmCVL_gDrr8qcYZAicIQPxMjO4Agh0APh-V-lJowxH_3cd7JcZgOtw0wNYSLRpdjitFT3dhm3bju", info.redirect_uri );
return Redirect(url);
}
我们看一下swagger-oauth.js 最后面代码给头文件添加授权access_token
window.swaggerUi.api.clientAuthorizations.add(window.OAuthSchemeKey, new SwaggerClient.ApiKeyAuthorization('Authorization', 'Bearer ' + b, 'header'));
不清楚的地方可以认真看一下这个js文件 ,我们不需要修改什么,授权后继续请求,如下图:
蓝色按钮表示授权成功,header 信息也有我们的access_token
请求成功