获取所有继承自 ControllerBase 的类,获取其公共的实例的方法(不包含父类),认作接口,
接口注释的xml文件,可以在项目-右键属性-生成-XML文档文件勾选,再次生成。
using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net.Http; using System.Reflection; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Xml; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Hosting; /* // Install-Package System.Text.Encoding.CodePages Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); // BackgroundService services.AddHostedService<GenerateApiDocumentService>(); */ namespace App.HostServices { /// <summary> /// 生成接口文档 /// </summary> public class GenerateApiDocumentService : BackgroundService { /// <summary>生成的Csv路径</summary> public static string CSVPath = "d:/1.csv"; /// <summary>当前提供的接口生成的注释路径</summary> public static string AnnotationXmlPath = "d:/API.xml"; /// <summary> /// /// </summary> /// <param name="stoppingToken"></param> /// <returns></returns> protected override async Task ExecuteAsync(CancellationToken stoppingToken) { var doc = new XmlDocument(); doc.Load(AnnotationXmlPath); var xpath = "/doc/members/member"; var nodes = doc.SelectNodes(xpath).Cast<XmlNode>().ToList(); var ass = Assembly.GetEntryAssembly(); var types = ass.GetTypes(); var cbt = typeof(ControllerBase); var ls = new List<MM>(); foreach (var type in types) { if (!cbt.IsAssignableFrom(type)) continue; // 过滤一些 if (type.FullName.StartsWith("Microsoft.AspNetCore.Mvc")) continue; if (type.Name.EqualIgnoreCase("BaseController")) continue; var publicMethods = type.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance); if (!publicMethods.Any()) continue; foreach (var method in publicMethods) { var attrs = method.GetCustomAttributes().ToList(); var httpMethod = HttpMethod.Get; if (attrs.Any(d => d.GetType() == typeof(HttpPostAttribute))) { httpMethod = HttpMethod.Post; } var methodParas = new List<String>(); // 参数 var paras = method.GetParameters(); foreach (var para in paras) { methodParas.Add($"{para.ParameterType.Name} {para.Name}"); } var summary = getSummary(nodes, type.FullName, method.Name, paras.Length > 0); var mm = new MM() { ControllerName = type.Name, ActionName = method.Name, HttpMethod = httpMethod, Summary = summary, Attrs = attrs.Select(d => d.GetType().Name).ToList(), Paras = methodParas, }; ls.Add(mm); } } ToFile(CSVPath, ls); await Task.CompletedTask; } static void ToFile(String path, List<MM> ls) { File.Delete(path); var defEncoding = Encoding.GetEncoding("gb2312"); using (var fs = File.OpenWrite(path)) { { var headerStr = "ControllerName,ActionName,Summary,Paras,HttpMethod,Attrs "; var data = defEncoding.GetBytes(headerStr); fs.Write(data); } foreach (var item in ls) { { var rowStr = $"{item.ControllerName},{item.ActionName}," + $"{item.Summary}," + $"{item.Paras.Join(" ")}," + $"{item.HttpMethod},{item.Attrs.Join("/")} "; var data = defEncoding.GetBytes(rowStr); fs.Write(data); } } } } // 获取方法注释 static String getSummary(IList<XmlNode> nodes, String typeFullName, String methodName, bool hasParas) { /* <member name="M:System.on(System.String)"> <summary>创建新的User</summary> <param name="token"></param> <returns></returns> </member> */ // M:API.Common.TokenSession.CreateSession( var name = $"M:{typeFullName}.{methodName}{(hasParas ? "(" : "")}"; foreach (var node in nodes) { var attrs = node.Attributes; foreach (XmlAttribute attr in attrs) { if (attr.Name.EqualIgnoreCase("name")) { var attrVal = attr.Value; if (attrVal.StartsWithIgnoreCase(name)) { // 读取 summary var summary = node.ChildNodes.Cast<XmlNode>().FirstOrDefault(d => d.Name.EqualIgnoreCase("summary")); return summary?.InnerText; } } } } return null; } } /// <summary></summary> public class MM { public String ControllerName { get; set; } public String ActionName { get; set; } public HttpMethod HttpMethod { get; set; } /// <summary>注释</summary> public String Summary { get; set; } public IList<String> Attrs { get; set; } public IList<String> Paras { get; set; } } }