tnblog
首页

使用swagger创建webapi文档描述---详解

91人阅读 2019/4/23 15:24 评论:2 手机浏览 收藏
分类: C#

在前后端分离开发中,作为后端如何给前端提供有效直观的接口文档呢?

没错,就是使用swagger 俗称“丝袜哥”


教程:

一、首先我们建立一个webapi项目,然后引用需要包。swagger需引用下面这2个包

卸载重复包(提示:这里直接卸载是卸载不掉的,这个包是依赖于Swagger.Net.UI这个包的,所以我们要勾选中强制删除,然后再删):

swaggUI这个文件夹也没用,删掉


二、写一个测试用的接口:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;

namespace webApiTest.Areas.Test.Controllers
{
  ///<summary>
  ///测试接口
  ///</summary
  public class TestController : ApiController
  {
    ///<summary>
    ///测试接口
    ///</summary>
    ///<param name="name">姓名</param>
    ///<returns>返回问候语</returns>
     [Route("v1.0/SendVer"), HttpGet]
    public string SayHello(string name)
    {
      return "Hello," + name;
    }

  }
}


默认的路由地址是:http://xxx/Swagger 访问一下已可以到接口描述:

三、现在已经有了接口描述,为了更加直观可视,我们还需重写下路由和进行汉化处理


1、首先加载汉化包:可以到我的百度网盘去下:

            链接:https://pan.baidu.com/s/1nCBGIQ1_OWi6Ly8Ta84IzQ 

            提取码:epxz

      下载完成后放到项目Scripts文件夹中去:

        

      有了汉化文件,我们就来加载到项目中去,重写 swaggerConfig.cs文件,打开后发现一堆英文注释,看不球懂,自己翻译了整理下(

       只翻译了部分我用到的,看实际情况食用):

using System.Web.Http;
using WebActivatorEx;
using webApiTest;
using Swashbuckle.Application;
using System;
using Swashbuckle.Swagger;
using System.Xml;
using System.Collections.Concurrent;
using System.IO;
using System.Collections.Generic;

[assembly: PreApplicationStartMethod(typeof(SwaggerConfig), "Register")]

namespace webApiTest
{
  public class SwaggerConfig
  {
    public static void Register()
    {
      var thisAssembly = typeof(SwaggerConfig).Assembly;

      GlobalConfiguration.Configuration
        .EnableSwagger(c =>
        {
          //默认情况下,服务根url推断从请求用于访问文档。然而,可能存在的情况下(如代理和负载均衡环境),这确实notresolve正确。
          //你可以解决这个通过提供您自己的代码来确定根URL。
          //c.RootUrl(req => GetRootUrlFromAppConfig());

          //如果Swagger2.0提供的文档不明确,然后计划用于访问文档作为默认值。
          //如果您的API支持多个计划和你想要明确的,您可以使用“方案”选项如下所示。
          //c.Schemes(new[] { "http", "https" });

          //使用“SingleApiVersion”来描述一个版本的API。Swagger2.0包含一个“信息”对象持有额外的元数据API。
          //版本和标题是必需的,但是您还可以提供额外的字段SingleApiVersion链接方法。
          c.SingleApiVersion("v1", "");

          //如果希望输出Swagger文档正确缩进,请启用“PrettyPrint”选项。
          //c.PrettyPrint();

          //如果您的API有多个版本,使用“MultipleApiVersions”而不是“SingleApiVersion”。
          //在这种情况下,您必须提供一个告诉Swagger的行为应该被包括在给定API版本的文档。
          //像“SingleApiVersion”,每次调用“版本”返回一个“信息”构建器可以提供额外的元数据API版本
          //c.MultipleApiVersions(
          //  (apiDesc, targetApiVersion) => ResolveVersionSupportByRouteConstraint(apiDesc, targetApiVersion),
          //  (vc) =>
          //  {
          //    vc.Version("v2", "Swashbuckle Dummy API V2");
          //    vc.Version("v1", "Swashbuckle Dummy API V1");
          //  });

          //您可以使用“BasicAuth”、“ApiKey”或“OAuth2”选项来描述API的安全方案。
          //详细信息,请参阅https://github.com/swagger-api/swagger-spec/blob/master/versions/2.0.md。
          //注意:这些只定义相对应的方案,需要加上一个“安全”属性的文档或操作水平,表明该方案所需的操作。
          //要做到这一点,你需要实现一个自定义IDocumentFilter和/或IOperationFilter根据您特定的授权设置这些属性实现
          //c.BasicAuth("basic")
          // .Description("Basic HTTP Authentication");
          //
          //注意:您还必须在SwaggerUI部分下面配置“EnableApiKeySupport”
          //c.ApiKey("apiKey")
          // .Description("API Key Authentication")
          // .Name("apiKey")
          // .In("header");
          //
          //c.OAuth2("oauth2")
          // .Description("OAuth2 Implicit Grant")
          // .Flow("implicit")
          // .AuthorizationUrl("http://petstore.swagger.wordnik.com/api/oauth/dialog")
          //  .TokenUrl("https://tempuri.org/token")
          // .Scopes(scopes =>
          //  {
          //     scopes.Add("read", "Read access to protected resources");
          //   scopes.Add("write", "Write access to protected resources");
          // });
                    //这个标志设置为省略过时的属性描述的任何行动
          //c.IgnoreObsoleteActions();

          //每个操作被分配一个或多个标签,然后用消费者出于各种原因。例如,swagger-ui组操作根据每个操作的第一个标记。
          //默认情况下,这将是控制器的名字,但您可以使用“GroupActionsBy”选项与任何值覆盖。
          //c.GroupActionsBy(apiDesc => apiDesc.HttpMethod.ToString());

          //你也可以指定一个自定义的排序顺序组(定义为“GroupActionsBy”)规定的顺序列出操作。
          //例如,如果默认分组(控制器名称)和您指定一个下行字母排序,然后从aProductsController行动将从CustomersController上市之前。
          //这是通常用于定制swagger -ui分组的顺序。
          //c.OrderActionGroupsBy(new DescendingAlphabeticComparer());

          //如果您用它来注释控制器和API类型
          //Xml注释(http://msdn.microsoft.com/en-us/library/b2s063f7(v=vs.110).aspx),可以合并
          //将这些注释添加到生成的文档和UI中。您可以通过提供到one或更多的Xml注释文件。
          //c.IncludeXmlComments(GetXmlCommentsPath());

          //Swashbuckle尽力为各种类型生成与Swagger兼容的JSON模式在您的API中公开。然而,有时可能需要对输出进行更多的控制。
          //这是通过“MapType”和“SchemaFilter”选项支持的:使用“MapType”选项覆盖特定类型的模式生成。
          //需要注意的是,对于任何适用的操作,生成的模式都将放在“内联”位置。
          //虽然Swagger 2.0支持“所有”模式类型的内联定义,但是Swagger -ui工具不支持。
          //它期望“复杂”模式被单独定义和引用。出于这个原因,你只应该当得到的模式是基本类型或数组类型时,
          //使用“MapType”选项。如果你需要改变复杂模式,使用模式过滤器。
          //c.MapType<ProductType>(() => new Schema { type = "integer", format = "int32" });

          //这个是webApiTest接口这个项目的XML文档
          c.IncludeXmlComments(string.Format("{0}/bin/webApiTest.XML", AppDomain.CurrentDomain.BaseDirectory));
          //这个是实体类的XML文档,因为在实际中我们需要调用实体类库,我这里只是个简单的例子,所以没有使用到这个
          //c.IncludeXmlComments(string.Format("{0}/bin/Model.XML", AppDomain.CurrentDomain.BaseDirectory));

          c.CustomProvider((defaultProvider) => new CachingSwaggerProvider(defaultProvider));
        })
        //重新定义路径及汉化文件
        .EnableSwaggerUi("apis/{*assetPath}", c =>
        {
            //这里代表你汉化文件的存放路径,这里有一个坑,当解决方案有'.'这种字符的时候会给识别为路径,且没办法转义,
          //这个坑的我好惨,比如解决方案改为webapi.test 就要出事了
          //路径规则,项目命名空间.文件夹名称.js文件名称
          c.InjectStylesheet(thisAssembly, "webApiTest.Scripts.custom.css");
          c.InjectJavaScript(thisAssembly, "webApiTest.Scripts.swagger_lang.js");
          //c.CustomAsset("index", thisAssembly, "NineTeam.API.Swagger.index.html");
        });
      }

      ///<summary>
      ///Swagger汉化
      ///</summary>
      public class CachingSwaggerProvider : ISwaggerProvider
      {
          private static ConcurrentDictionary<string, SwaggerDocument> _cache =
        new ConcurrentDictionary<string, SwaggerDocument>();

        private readonly ISwaggerProvider _swaggerProvider;

        public CachingSwaggerProvider(ISwaggerProvider swaggerProvider)
        {
          _swaggerProvider = swaggerProvider;
        }

        public SwaggerDocument GetSwagger(string rootUrl, string apiVersion)
        {
          var cacheKey = string.Format("{0}_{1}", rootUrl, apiVersion);
          SwaggerDocument srcDoc = null;
          //只读取一次
          if(!_cache.TryGetValue(cacheKey, out srcDoc))
          {
            srcDoc = _swaggerProvider.GetSwagger(rootUrl, apiVersion);

            srcDoc.vendorExtensions = new Dictionary<string, object> { { "ControllerDesc", GetControllerDesc() } };
              _cache.TryAdd(cacheKey, srcDoc);
          }
          return srcDoc;
        }

        ///<summary>
        ///从API文档中读取控制器描述
        ///</summary>
        ///<returns>所有控制器描述</returns>
        public static ConcurrentDictionary<string, string> GetControllerDesc()
        {
          string xmlpath = string.Format(@"{0}\bin\webApiTest.XML", System.AppDomain.CurrentDomain.BaseDirectory);
          ConcurrentDictionary<string, string> controllerDescDict = new ConcurrentDictionary<string, string>();
          if (File.Exists(xmlpath))
          {
            XmlDocument xmldoc = new XmlDocument();
            xmldoc.Load(xmlpath);
            string type = string.Empty, path = string.Empty, controllerName = string.Empty;

            string[] arrPath;
            int length = -1, cCount = "Controller".Length;
            XmlNode summaryNode = null;
            foreach (XmlNode node in xmldoc.SelectNodes("//member"))
            {
            type = node.Attributes["name"].Value;
            if (type.StartsWith("T:"))
            {
              //控制器
              arrPath = type.Split('.');
              length = arrPath.Length;
              controllerName = arrPath[length - 1];
              if (controllerName.EndsWith("Controller"))
              {
                //获取控制器注释
                summaryNode = node.SelectSingleNode("summary");
                string key = controllerName.Remove(controllerName.Length - cCount, cCount);
                if (summaryNode != null && !string.IsNullOrEmpty(summaryNode.InnerText) && !controllerDescDict.ContainsKey(key))
                {
                    controllerDescDict.TryAdd(key, summaryNode.InnerText.Trim());
                  }
              }
            }
          }
        }
        return controllerDescDict;
      }
    }
  }
}

 2、认真看了代码的童鞋可能已经发现,swagger的原理实际上就是读取你在编写代码时编写的注释,所以你的注释得详细,那么如何生成XML文件?

       右键项目属性》生成》:

重新生成下项目,可以看到bin文件夹下已生成xml文件,打开看一下是什么东西:

可以到在末尾有我们刚才接口的注释。嗯~感觉是搭建的差不多了,跑一下:

   汉化怎么没成???但是文档的注释是加载进来了。重新检查下路径发现也是对的?

                怎么办呢,别急,只需要改下这两个文件的生产操作:

右键这两个文件》属性》生成操作》改为嵌入式


OK!再试一下:


嗯~这就美观多了。


总结:实践是检验真理的唯一标准!





评价
Decorating heart
博主搜索
文章
6
评论
8
分类
16
关注
16
文章类别
最新文章
最新评价
{{item.ArticleTitle}}
{{item.BlogName}} : {{item.Content}}