最近做的项目使用mvc+webapi,采取前后端分离的方式,后台提供API接口给前端开发人员。
这个过程中遇到一个问题后台开发人员怎么提供接口说明文档给前端开发人员。
为了解决这个问题,项目中引用swagger(我比较喜欢戏称为“丝袜哥”)
这篇文章介绍的Swagger的使用比较详细 https://www.cnblogs.com/yanweidie/p/5709113.html
实现效果
1.列出所有API控制器和控制器描述
2.列出action和描述
3.直观的接口测试
1.WebApi项目中引入Swagger nuget添加下图两个包
2.配置Swagger和配置项目输出XML文档,用于显示注释
3.汉化问题Swagger汉化
SwaggerConfig.cs修改源码如下
1 using System.Web.Http; 2 using WebActivatorEx; 3 using FamilyDoctorWebApi; 4 using Swashbuckle.Application; 5 using System; 6 using System.Collections.Concurrent; 7 using Swashbuckle.Swagger; 8 using System.Collections.Generic; 9 using System.Xml; 10 using System.IO; 11 12 [assembly: PreApplicationStartMethod(typeof(SwaggerConfig), "Register")] 13 namespace FamilyDoctorWebApi 14 { 15 /// <summary> 16 /// 17 /// </summary> 18 public class SwaggerConfig 19 { 20 public static void Register() 21 { 22 var thisAssembly = typeof(SwaggerConfig).Assembly; 23 24 GlobalConfiguration.Configuration 25 .EnableSwagger(c => 26 { 27 c.SingleApiVersion("v1", "家庭医生WebApi接口文档"); 28 c.IncludeXmlComments(GetXmlCommentsPath(thisAssembly.GetName().Name)); 29 c.UseFullTypeNameInSchemaIds(); 30 c.CustomProvider((defaultProvider) => new CachingSwaggerProvider(defaultProvider)); 31 }) 32 .EnableSwaggerUi(c => 33 { 34 //路径规则,项目命名空间.文件夹名称.js文件名称 35 c.InjectJavaScript(thisAssembly, "FamilyDoctorWebApi.Scripts.swagger.js"); 36 }); 37 } 38 /// <summary> 39 /// 40 /// </summary> 41 /// <param name="name"></param> 42 /// <returns></returns> 43 protected static string GetXmlCommentsPath(string name) 44 { 45 return string.Format(@"{0}\bin\{1}.XML", AppDomain.CurrentDomain.BaseDirectory, name); 46 } 47 } 48 49 public class CachingSwaggerProvider : Swashbuckle.Swagger.ISwaggerProvider 50 { 51 private static ConcurrentDictionary<string, SwaggerDocument> _cache = 52 new ConcurrentDictionary<string, SwaggerDocument>(); 53 54 private readonly ISwaggerProvider _swaggerProvider; 55 56 public CachingSwaggerProvider(ISwaggerProvider swaggerProvider) 57 { 58 _swaggerProvider = swaggerProvider; 59 } 60 61 public SwaggerDocument GetSwagger(string rootUrl, string apiVersion) 62 { 63 var cacheKey = string.Format("{0}_{1}", rootUrl, apiVersion); 64 SwaggerDocument srcDoc = null; 65 //只读取一次 66 if (!_cache.TryGetValue(cacheKey, out srcDoc)) 67 { 68 srcDoc = _swaggerProvider.GetSwagger(rootUrl, apiVersion); 69 70 srcDoc.vendorExtensions = new Dictionary<string, object> { { "ControllerDesc", GetControllerDesc() } }; 71 _cache.TryAdd(cacheKey, srcDoc); 72 } 73 return srcDoc; 74 } 75 76 /// <summary> 77 /// 从API文档中读取控制器描述 78 /// </summary> 79 /// <returns>所有控制器描述</returns> 80 public static ConcurrentDictionary<string, string> GetControllerDesc() 81 { 82 string xmlpath = string.Format("{0}/bin/{1}.XML", System.AppDomain.CurrentDomain.BaseDirectory, typeof(SwaggerConfig).Assembly.GetName().Name); 83 ConcurrentDictionary<string, string> controllerDescDict = new ConcurrentDictionary<string, string>(); 84 if (File.Exists(xmlpath)) 85 { 86 XmlDocument xmldoc = new XmlDocument(); 87 xmldoc.Load(xmlpath); 88 string type = string.Empty, path = string.Empty, controllerName = string.Empty; 89 90 string[] arrPath; 91 int length = -1, cCount = "Controller".Length; 92 XmlNode summaryNode = null; 93 foreach (XmlNode node in xmldoc.SelectNodes("//member")) 94 { 95 type = node.Attributes["name"].Value; 96 if (type.StartsWith("T:")) 97 { 98 //控制器 99 arrPath = type.Split('.'); 100 length = arrPath.Length; 101 controllerName = arrPath[length - 1]; 102 if (controllerName.EndsWith("Controller")) 103 { 104 //获取控制器注释 105 summaryNode = node.SelectSingleNode("summary"); 106 string key = controllerName.Remove(controllerName.Length - cCount, cCount); 107 if (summaryNode != null && !string.IsNullOrEmpty(summaryNode.InnerText) && !controllerDescDict.ContainsKey(key)) 108 { 109 controllerDescDict.TryAdd(key, summaryNode.InnerText.Trim()); 110 } 111 } 112 } 113 } 114 } 115 return controllerDescDict; 116 } 117 } 118 }