Swagger for Asp.net 应用笔记

1602 查看

简介

一直听说Swagger是做Web API文档的好工具,正好手头一个项目使用WebApi,决定使用Swagger来文档化接口,作为和前端交流的基础。下面是使用Swashbuckle.net 给ASP.net web API添加文档的简要步骤。

使用中如果碰到任何问题欢迎评论,一起讨论解决

项目引入Swagger

Swashbuckle是Swagger在dotnet环境中的实现,在ASP.net项目中加入后即可支持Swagger/UI。5.X版本支持ASP.net, 6.X(beta)版本支持ASP.net Core. 目前项目使用ASP.net for IIS,所以使用了5.4的版本。 关于selfhost和Owin Swashbuckle 的readme有很清楚的描述,可以自行查看。

使用nuget加入Swashbuckle的引用。

Paste_Image.png

安装好以后,在App_Start目录下,会有一个SwaggerConfig.cs文件,SwaggerConfig类通过[assembly: PreApplicationStartMethod(typeof(SwaggerConfig), "Register")]启动时运行。

nuget添加完引用,无须任何配置,编译后,访问 http://yoururl/swagger 即可看到所有API的文档说明,当然说明都很简单,没有太大价值。

SwaggerConfig简单介绍

SwaggerConfig.cs文件会自动添加到项目的App_Start目录下,代码本身包含大量注释掉的代码,清除后,代码如下:

 public class SwaggerConfig
   {
       public static void Register()
       {
           var thisAssembly = typeof(SwaggerConfig).Assembly;
            GlobalConfiguration.Configuration
               .EnableSwagger(c =>//用于启用和设置Swagger的配置信息。
                   {
                       c.SingleApiVersion("v1", "Cxx.xxx.Web");       
                      //a#                
                   })
               .EnableSwaggerUi(c =>
                   {//用于启用UI界面上的东西。
                      //b#
                  });
       }
   }
支持XML注释

使用Swagger的目的就是希望把代码中方法和类型的注释自动导出来。在Swashbuckle中,很简单。

  • 相关工程需要生成XML文档


    Paste_Image.png
  • 在Swagger.Config的Register方法的EnableSwagger匿名函数中加上对应的XML文件(可以添加在//a#代码后面。)

var baseDirectory = AppDomain.CurrentDomain.BaseDirectory +"\bin\"; var commentsFileName = Assembly.GetExecutingAssembly().GetName().Name + ".XML"; var commentsFile = Path.Combine(baseDirectory, commentsFileName); c.IncludeXmlComments(commentsFile);

上述代码把Web工程的XML注释加入到Swagger中。一般我们会把viewmodel或者其他类型定义在不同的工程中,通过下面的代码可以继续加入其它xml注释文件。(对应功能需要启用XML文档文件生成)
c.IncludeXmlComments(Path.Combine(baseDirectory, "CrXX.XX.xml"));

实现默认调用参数

Swagger本身支持直接调用,但是调用参数需要手动编写,一个比较简单的办法是自定义一个attribute,给viewmodel的参数加上默认参数。

  • 首先增加一个Swagger属性如下图:

     public class SwaggerDefaultValue : Attribute
     {
          public string Name { get; set; }
          public string Value { get; set; }
          public SwaggerDefaultValue(string value)
          {
              this.Value = value;
          }
    
          public SwaggerDefaultValue(string name, string value) : this(value)
          {
              this.Name = name;
          }
    }
  • 增加一个AddDefaultValues类,如下面代码

        public class AddDefaultValues : IOperationFilter
        {
          public void Apply(Operation operation, SchemaRegistry schemaRegistry, ApiDescription     apiDescription)
          {
    
              IDictionary<string, object> parameterValuePairs =
              GetParameterValuePairs(apiDescription.ActionDescriptor);
              if (operation?.parameters != null)
              {
    
                  foreach (var param in operation.parameters)
                  {
                      if (param.schema != null && param.schema.@ref != null)
                      {
                          string schemaName = param.schema.@ref.Split('/').LastOrDefault();
                          if (schemaRegistry.Definitions.ContainsKey(schemaName))
                              foreach (var props in schemaRegistry.Definitions[schemaName].properties)
                              {
                                  if (parameterValuePairs.ContainsKey(props.Key.ToLower()))//默认会转换为camelcase,所以强行转为小写
                                      props.Value.@default = parameterValuePairs[props.Key.ToLower()];//默认会转换为camelcase,所以强行转为小写
                              }
                      }
                      var parameterValuePair = parameterValuePairs.FirstOrDefault(p =>   p.Key.IndexOf(param.name, StringComparison.InvariantCultureIgnoreCase) >= 0);
                      param.@default = parameterValuePair.Value;
                  }
              }
          }
    
        private IDictionary<string, object> GetParameterValuePairs(HttpActionDescriptor actionDescriptor)
        {
          IDictionary<string, object> parameterValuePairs = new Dictionary<string, object>();
    
          foreach (SwaggerDefaultValue defaultValue in actionDescriptor.GetCustomAttributes<SwaggerDefaultValue>())
          {
              parameterValuePairs.Add(defaultValue.Name, defaultValue.Value);
          }
    
          foreach (var parameter in actionDescriptor.GetParameters())
          {
              if (!parameter.ParameterType.IsPrimitive)
              {
                  InitialType(parameterValuePairs, parameter.ParameterType);
              }
          }
    
          return parameterValuePairs;
        }
    
        void InitialType(IDictionary<string, object> parameterValuePairs, Type t)
        {
          foreach (PropertyInfo property in t.GetProperties())
          {
              var defaultValue = GetDefaultValue(property);
    
              if (defaultValue != null)
              {
    
                  parameterValuePairs.Add(property.Name.ToLower(), defaultValue);//默认会转换为camelcase,所以强行转为小写
              }
          }
        }
    
        private static object GetDefaultValue(PropertyInfo property)
        {
          var customAttribute = property.GetCustomAttributes<SwaggerDefaultValue>().FirstOrDefault();
    
          if (customAttribute != null)
          {
              return customAttribute.Value;
          }
    
          return null;
        }  
    }
  • 在Swagger.Config的Register方法的EnableSwagger匿名函数中对应的过滤器(可以添加在//a#代码后面。)
    c.OperationFilter<AddDefaultValues>();

  • 在具体的调用参数上加上默认参数,在swagger的界面上就会有默认参数啦。

    public class CheckMobile
    {
         [SwaggerDefaultValue("13482894351")]
         [Required]
         public string Mobile { get; set; }   
    }
    实现Oauth2 API的调用

    我们的WebAPI基于OAuth2的验证,所以Swagger上并不能直接调用。如果希望Swagger能直接调用OAuth2保护的API。

  • 增加一个js到Web工程中, zhe

    Paste_Image.png

*js的内容如下

$('#input_apiKey').change(function () {
    var key = $('#input_apiKey')[0].value;
    var credentials = key.split(':'); //username:password expected
    $.ajax({
        url: "/Token",
        type: "post",
        contenttype: 'x-www-form-urlencoded',
        data: "grant_type=password&username=" + credentials[0] + "&password=" + credentials[1],
        success: function (response) {

            var bearerToken = 'Bearer ' + response.access_token;
            swaggerUi.api.clientAuthorizations.add('key', new SwaggerClient.ApiKeyAuthorization('Authorization', bearerToken, 'header'));

        },
        error: function (xhr, ajaxoptions, thrownerror) {
            alert("输入的用户名密码不对,不能登陆!");
        }
    });
});

*在Swagger.config 的Register方法的EanbleSwaggerUI匿名函数中加上对应的JS,可以加在//b#后面
c.InjectJavaScript(typeof(SwaggerConfig).Assembly, "CrXXX.XX.SwaggerLogin.js");

CrXXX.XX 需要改成web工程的名字

在Swagger界面上的API_Key输入框输入oauth2的用户名密码即可登录.

Swagger API文档的效果图

Paste_Image.png