AspNetCore 生成接口文档
获取所有继承自 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\n";
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("/")}\n";
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; }
}
}

浙公网安备 33010602011771号