文档相关修复
This commit is contained in:
@@ -1,39 +1,105 @@
|
||||
using System;
|
||||
using System;
|
||||
using Swashbuckle.AspNetCore.Annotations;
|
||||
|
||||
namespace SimApi.Attributes;
|
||||
|
||||
/// <summary>
|
||||
/// 快捷自定义接口文档类
|
||||
/// 快捷自定义接口文档类(所有参数均可省略, 支持命名参数)。
|
||||
/// 对应仓颉版 SimApiDoc:
|
||||
/// tags → 接口标签(逗号分隔, 如 "认证,用户")
|
||||
/// name → API 名称(映射到 Summary, 作为文档接口标题)
|
||||
/// description → API 详细描述
|
||||
/// groupNames → 所属文档组(逗号分隔, 如 "api,admin"; "*" 表示所有文档; null/空 → 仅默认 "api" 文档)
|
||||
/// ignore → true 时不出现在任何文档中(路由不受影响)
|
||||
/// 写法示例:
|
||||
/// [SimApiDoc("认证", "登录")] 位置参数(保持旧版兼容)
|
||||
/// [SimApiDoc(tags: "认证", name: "登录", description: "...")]
|
||||
/// [SimApiDoc(groupNames: "api,admin")] 仅指定文档组
|
||||
/// [SimApiDoc(GroupNames = "*", Ignore = false)] 属性命名参数
|
||||
/// [SimApiDoc] 全部默认(可标在类上, 仅用 Ignore/GroupNames 等)
|
||||
/// 属性命名参数优先于构造命名参数。
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)]
|
||||
public class SimApiDocAttribute : SwaggerOperationAttribute
|
||||
{
|
||||
/// <summary>
|
||||
/// 定义接口说明
|
||||
/// 出现在所有文档组中的通配符
|
||||
/// </summary>
|
||||
/// <param name="tags">接口分组列表</param>
|
||||
public const string AllGroups = "*";
|
||||
|
||||
/// <summary>
|
||||
/// 接口所属的文档组(逗号分隔, 如 "api,admin"); "*" 表示所有文档; null/空 表示未分组(仅默认 "api" 文档)
|
||||
/// </summary>
|
||||
public string? GroupNames { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 为 true 时该接口不出现在任何文档中(不影响路由)
|
||||
/// </summary>
|
||||
public bool Ignore { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 定义接口说明(全部可选)
|
||||
/// </summary>
|
||||
/// <param name="tags">接口标签, 逗号分隔, 如 "认证,用户"</param>
|
||||
/// <param name="name">接口名称</param>
|
||||
/// <param name="description">接口描述</param>
|
||||
public SimApiDocAttribute(string[] tags, string name, string? description = null)
|
||||
/// <param name="groupNames">所属文档组, 逗号分隔; "*" 表示所有文档</param>
|
||||
/// <param name="ignore">true 时不出现在任何文档</param>
|
||||
public SimApiDocAttribute(string? tags = null, string? name = null, string? description = null,
|
||||
string? groupNames = null, bool ignore = false)
|
||||
{
|
||||
Tags = tags;
|
||||
Summary = name;
|
||||
if (description != null)
|
||||
{
|
||||
Description = description;
|
||||
}
|
||||
// Consumes = new[] {"application/json"};
|
||||
// Produces = new[] {"application/json"};
|
||||
Apply(tags, name, description, groupNames, ignore);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 定义接口说明
|
||||
/// 定义接口说明(标签以数组传入)
|
||||
/// </summary>
|
||||
/// <param name="tag">接口分组</param>
|
||||
/// <param name="tags">接口标签列表</param>
|
||||
/// <param name="name">接口名称</param>
|
||||
/// <param name="description">接口描述</param>
|
||||
public SimApiDocAttribute(string tag, string name, string? description = null) : this([tag], name, description)
|
||||
/// <param name="groupNames">所属文档组, 逗号分隔; "*" 表示所有文档</param>
|
||||
/// <param name="ignore">true 时不出现在任何文档</param>
|
||||
public SimApiDocAttribute(string[] tags, string? name = null, string? description = null,
|
||||
string? groupNames = null, bool ignore = false)
|
||||
{
|
||||
Apply(tags, name, description, groupNames, ignore);
|
||||
}
|
||||
|
||||
private void Apply(string? tags, string? name, string? description,
|
||||
string? groupNames, bool ignore)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(tags))
|
||||
{
|
||||
Tags = tags.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
|
||||
}
|
||||
|
||||
ApplyCore(name, description, groupNames, ignore);
|
||||
}
|
||||
|
||||
private void Apply(string[] tags, string? name, string? description,
|
||||
string? groupNames, bool ignore)
|
||||
{
|
||||
if (tags is { Length: > 0 })
|
||||
{
|
||||
Tags = tags;
|
||||
}
|
||||
|
||||
ApplyCore(name, description, groupNames, ignore);
|
||||
}
|
||||
|
||||
private void ApplyCore(string? name, string? description, string? groupNames, bool ignore)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(name))
|
||||
{
|
||||
Summary = name;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(description))
|
||||
{
|
||||
Description = description;
|
||||
}
|
||||
|
||||
GroupNames = groupNames;
|
||||
Ignore = ignore;
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@ namespace SimApi.Configurations;
|
||||
/// <summary>
|
||||
/// 文档组配置
|
||||
/// </summary>
|
||||
public class SimApiDocGroupOption(string id, string name, string description = "")
|
||||
public class SimApiDocGroupOption(string id, string name, string description = "", bool isDefault = false)
|
||||
{
|
||||
/// <summary>
|
||||
/// 文档标识
|
||||
@@ -22,6 +22,11 @@ public class SimApiDocGroupOption(string id, string name, string description = "
|
||||
/// 文档描述
|
||||
/// </summary>
|
||||
public string Description { get; set; } = description!;
|
||||
|
||||
/// <summary>
|
||||
/// 是否为默认文档组: 未标注分组的接口全部归入此文档组
|
||||
/// </summary>
|
||||
public bool IsDefault { get; set; } = isDefault;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -50,7 +55,7 @@ public class SimApiDocOptions
|
||||
/// </summary>
|
||||
public SimApiDocGroupOption[] ApiGroups { get; set; } =
|
||||
[
|
||||
new("api", "Api", "Api接口文档")
|
||||
new("api", "Api", "Api接口文档", isDefault: true)
|
||||
];
|
||||
|
||||
/// <summary>
|
||||
@@ -58,6 +63,12 @@ public class SimApiDocOptions
|
||||
/// </summary>
|
||||
public SimApiAuthOption ApiAuth { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// 接口文档页面访问前缀, 默认 "docs"。
|
||||
/// 访问 /docs 即打开文档页面, JSON 地址为 /docs/{文档组Id}.json
|
||||
/// </summary>
|
||||
public string UrlPrefix { get; set; } = "docs";
|
||||
|
||||
/// <summary>
|
||||
/// 文档页面标题
|
||||
/// </summary>
|
||||
|
||||
@@ -11,7 +11,7 @@ public class SimApiAuthController(SimApiAuth auth) : SimApiBaseController
|
||||
/// 退出登陆
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[HttpPost, SimApiDoc("认证", "退出登陆")]
|
||||
[HttpPost, SimApiDoc("认证", "退出登陆",groupNames: "*")]
|
||||
public void Logout()
|
||||
{
|
||||
if (Request.Headers.TryGetValue("Token", out var value))
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using SimApi.Configurations;
|
||||
|
||||
namespace SimApi.Controllers;
|
||||
|
||||
/// <summary>
|
||||
/// SimApi 内置端点路由表中的一项。
|
||||
/// </summary>
|
||||
public sealed record SimApiBuiltInRoute(
|
||||
string RouteName,
|
||||
string Controller,
|
||||
string Action,
|
||||
string[] HttpMethods,
|
||||
string Path);
|
||||
|
||||
/// <summary>
|
||||
/// SimApi 内置端点路由表 —— 路径的唯一配置处。
|
||||
/// UseSimApi 用它注册约定路由(动态路由),
|
||||
/// SimApiBuiltInRoutesDescriptionProvider 用它生成文档条目。
|
||||
/// 某项路径为 null / 空时该端点既不注册路由、也不出现在文档中。
|
||||
/// </summary>
|
||||
public static class SimApiBuiltInRoutes
|
||||
{
|
||||
public static IEnumerable<SimApiBuiltInRoute> Get(SimApiRouteOptions options)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(options);
|
||||
|
||||
if (!string.IsNullOrEmpty(options.UserInfoRoute))
|
||||
{
|
||||
yield return new SimApiBuiltInRoute(
|
||||
"UserInfo", "SimApiCommon", "UserInfo", ["POST"], options.UserInfoRoute);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(options.LogoutRoute))
|
||||
{
|
||||
yield return new SimApiBuiltInRoute(
|
||||
"Logout", "SimApiAuth", "Logout", ["POST"], options.LogoutRoute);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(options.WebConfigRoute))
|
||||
{
|
||||
// 控制器动作同时标注了 HttpGet/HttpPost
|
||||
yield return new SimApiBuiltInRoute(
|
||||
"WebConfig", "SimApiCommon", "WebConfig", ["GET", "POST"], options.WebConfigRoute);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc.ApiExplorer;
|
||||
using Microsoft.AspNetCore.Mvc.Controllers;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using SimApi.Configurations;
|
||||
|
||||
namespace SimApi.Controllers;
|
||||
|
||||
/// <summary>
|
||||
/// 将 SimApiRouteOptions 动态配置的内置约定路由(UserInfo / Logout / WebConfig)
|
||||
/// 翻译成 ApiDescription, 使它们能像属性路由接口一样出现在 Swagger 文档中:
|
||||
/// 请求/响应 schema 由 Swashbuckle 自动分析, 方法上已有的 [SimApiDoc] 注解自动生效。
|
||||
/// 路径为 null / 空 的端点在此一并跳过(与路由注册保持一致)。
|
||||
/// </summary>
|
||||
public class SimApiBuiltInRoutesDescriptionProvider(
|
||||
IServiceProvider services,
|
||||
SimApiOptions options) : IApiDescriptionProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// 默认 provider(DefaultApiDescriptionProvider)的 Order 为 -1000, 位于其后执行。
|
||||
/// </summary>
|
||||
public int Order => -900;
|
||||
|
||||
public void OnProvidersExecuting(ApiDescriptionProviderContext context)
|
||||
{
|
||||
}
|
||||
|
||||
public void OnProvidersExecuted(ApiDescriptionProviderContext context)
|
||||
{
|
||||
var modelMetadata = services.GetService<IModelMetadataProvider>();
|
||||
|
||||
foreach (var route in SimApiBuiltInRoutes.Get(options.SimApiRouteOptions))
|
||||
{
|
||||
var action = context.Actions.OfType<ControllerActionDescriptor>()
|
||||
.FirstOrDefault(a =>
|
||||
string.Equals(a.ControllerName, route.Controller, StringComparison.Ordinal) &&
|
||||
string.Equals(a.ActionName, route.Action, StringComparison.Ordinal));
|
||||
if (action is null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// 若该动作已被属性路由 / 其他 provider 收录, 跳过避免重复
|
||||
if (context.Results.Any(d => ReferenceEquals(d.ActionDescriptor, action)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (var httpMethod in route.HttpMethods)
|
||||
{
|
||||
context.Results.Add(CreateDescription(action, route, httpMethod, modelMetadata));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static ApiDescription CreateDescription(
|
||||
ControllerActionDescriptor action,
|
||||
SimApiBuiltInRoute route,
|
||||
string httpMethod,
|
||||
IModelMetadataProvider? modelMetadata)
|
||||
{
|
||||
var apiDescription = new ApiDescription
|
||||
{
|
||||
ActionDescriptor = action,
|
||||
HttpMethod = httpMethod,
|
||||
RelativePath = route.Path.TrimStart('/')
|
||||
};
|
||||
|
||||
// 输入: 按真实方法参数翻译(内置端点均为零参, 此处保证将来加参也可分析)
|
||||
foreach (var parameter in action.Parameters)
|
||||
{
|
||||
var parameterDescription = new ApiParameterDescription
|
||||
{
|
||||
Name = parameter.Name,
|
||||
Type = parameter.ParameterType,
|
||||
ParameterDescriptor = parameter,
|
||||
Source = parameter.BindingInfo?.BindingSource ?? BindingSource.Body,
|
||||
IsRequired = true
|
||||
};
|
||||
if (modelMetadata is not null)
|
||||
{
|
||||
parameterDescription.ModelMetadata = modelMetadata.GetMetadataForType(parameter.ParameterType);
|
||||
}
|
||||
|
||||
apiDescription.ParameterDescriptions.Add(parameterDescription);
|
||||
}
|
||||
|
||||
// 输出: 方法返回类型 => 200 + json
|
||||
var returnType = Unwrap(action.MethodInfo.ReturnType);
|
||||
if (returnType != typeof(void))
|
||||
{
|
||||
var responseType = new ApiResponseType
|
||||
{
|
||||
StatusCode = 200,
|
||||
Type = returnType,
|
||||
IsDefaultResponse = true
|
||||
};
|
||||
if (modelMetadata is not null)
|
||||
{
|
||||
responseType.ModelMetadata = modelMetadata.GetMetadataForType(returnType);
|
||||
}
|
||||
|
||||
responseType.ApiResponseFormats.Add(new ApiResponseFormat { MediaType = "application/json" });
|
||||
apiDescription.SupportedResponseTypes.Add(responseType);
|
||||
}
|
||||
|
||||
return apiDescription;
|
||||
}
|
||||
|
||||
private static Type Unwrap(Type type) =>
|
||||
type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Task<>)
|
||||
? type.GetGenericArguments()[0]
|
||||
: type;
|
||||
}
|
||||
@@ -18,7 +18,7 @@ public class SimApiCommonController(SimApiOptions simApiOptions) : SimApiBaseCon
|
||||
/// <param name="code">错误代码</param>
|
||||
/// <returns></returns>
|
||||
[HttpGet("exception/{code:int}")]
|
||||
[ApiExplorerSettings(IgnoreApi = true)]
|
||||
[SimApiDoc(ignore: true)]
|
||||
public void ExceptionHandler(int code)
|
||||
{
|
||||
Error(code);
|
||||
@@ -29,6 +29,7 @@ public class SimApiCommonController(SimApiOptions simApiOptions) : SimApiBaseCon
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[HttpPost, HttpGet]
|
||||
[HttpPost, SimApiDoc("公共", "获取自定义配置",groupNames: "*")]
|
||||
public Dictionary<string, object> WebConfig()
|
||||
{
|
||||
var resp = simApiOptions.WebConfig!.ToDictionary();
|
||||
@@ -49,6 +50,6 @@ public class SimApiCommonController(SimApiOptions simApiOptions) : SimApiBaseCon
|
||||
/// 获取已登录用户信息
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[HttpPost, SimApiAuth, SimApiDoc("认证", "获取已登录用户信息")]
|
||||
[HttpPost, SimApiAuth, SimApiDoc("认证", "获取已登录用户信息",groupNames: "*")]
|
||||
public SimApiLoginItem UserInfo() => LoginInfo;
|
||||
}
|
||||
@@ -28,7 +28,7 @@ public static class SimApiUtil
|
||||
{
|
||||
// ReferenceHandler = ReferenceHandler.Preserve,
|
||||
Encoder = JavaScriptEncoder.Create(UnicodeRanges.All),
|
||||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
||||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
|
||||
// DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
|
||||
};
|
||||
|
||||
|
||||
@@ -60,7 +60,24 @@ public class SimApiRequestLogMiddleware(
|
||||
}
|
||||
else
|
||||
{
|
||||
var reqLogs = SimApiUtil.FromJson<Dictionary<string, object>>(requestBodyText);
|
||||
// GET/DELETE 等无 body 的请求, 或表单/文件上传等非 JSON body:
|
||||
// 不进行长度裁剪, 原样记录, 避免空 JSON 解析抛异常
|
||||
Dictionary<string, object>? reqLogs = null;
|
||||
try
|
||||
{
|
||||
reqLogs = SimApiUtil.FromJson<Dictionary<string, object>>(requestBodyText);
|
||||
}
|
||||
catch (JsonException)
|
||||
{
|
||||
reqLogs = null;
|
||||
}
|
||||
|
||||
if (reqLogs == null)
|
||||
{
|
||||
logMessage.AppendLine(requestBodyText);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var reqLog in reqLogs)
|
||||
{
|
||||
if (reqLog.Value is not JsonElement { ValueKind: JsonValueKind.String } je) continue;
|
||||
@@ -73,6 +90,7 @@ public class SimApiRequestLogMiddleware(
|
||||
|
||||
logMessage.AppendLine(SimApiUtil.Json(reqLogs));
|
||||
}
|
||||
}
|
||||
|
||||
var originalBodyStream = context.Response.Body;
|
||||
using var responseBody = new MemoryStream();
|
||||
|
||||
+55
-33
@@ -9,15 +9,18 @@ using Hangfire.Redis.StackExchange;
|
||||
using SimApi.Helpers;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
using Microsoft.OpenApi;
|
||||
using SimApi.Middlewares;
|
||||
using Microsoft.AspNetCore.HttpOverrides;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.ApiExplorer;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using SimApi.Attributes;
|
||||
using SimApi.AuthSDK;
|
||||
using SimApi.Configurations;
|
||||
using SimApi.Controllers;
|
||||
using SimApi.Interfaces;
|
||||
using SimApi.Logger;
|
||||
using SimApi.SwaggerFilters;
|
||||
@@ -229,15 +232,42 @@ public static class SimApiExtensions
|
||||
|
||||
x.DocInclusionPredicate((docName, apiDesc) =>
|
||||
{
|
||||
// 获取接口标记的 GroupName(未标记则为 null)
|
||||
var actionGroupName = apiDesc.ActionDescriptor.EndpointMetadata
|
||||
var metadata = apiDesc.ActionDescriptor.EndpointMetadata;
|
||||
|
||||
// 类/方法上的 [SimApiDoc] 标注统一控制文档归属
|
||||
var docAttrs = metadata.OfType<SimApiDocAttribute>().ToArray();
|
||||
|
||||
// Ignore: 不出现在任何文档(路由不受影响)
|
||||
if (docAttrs.Any(a => a.Ignore))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// GroupNames: 逗号分隔的文档组列表; "*" 表示所有文档;
|
||||
// 标注在类上则整类生效, 方法级标注可覆盖
|
||||
var groupNames = docAttrs
|
||||
.Select(a => a.GroupNames)
|
||||
.FirstOrDefault(g => !string.IsNullOrWhiteSpace(g));
|
||||
if (groupNames != null)
|
||||
{
|
||||
var groups = groupNames.Split(',',
|
||||
StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
|
||||
return groups.Contains(SimApiDocAttribute.AllGroups) || groups.Contains(docName);
|
||||
}
|
||||
|
||||
// 兼容: ApiExplorerSettings.GroupName 单组标注
|
||||
var actionGroupName = metadata
|
||||
.OfType<ApiExplorerSettingsAttribute>()
|
||||
.FirstOrDefault()?.GroupName;
|
||||
|
||||
// 情况1:接口未标记任何 GroupName(actionGroupName 为 null)
|
||||
// 未标注任何分组 → 放入配置为 IsDefault 的文档组;
|
||||
// 未配置 IsDefault 时依次回退 id 为 "api" 的组 / 第一个组
|
||||
if (actionGroupName == null)
|
||||
{
|
||||
return docName == "api"; // 未分组接口只属于默认分组v1
|
||||
var defaultGroup = docOptions.ApiGroups.FirstOrDefault(g => g.IsDefault)
|
||||
?? docOptions.ApiGroups.FirstOrDefault(g => g.Id == "api")
|
||||
?? docOptions.ApiGroups.FirstOrDefault();
|
||||
return defaultGroup != null && docName == defaultGroup.Id;
|
||||
}
|
||||
|
||||
return docName == actionGroupName;
|
||||
@@ -308,6 +338,13 @@ public static class SimApiExtensions
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// 让 SimApiRouteOptions 配置的内置约定路由(UserInfo/Logout/WebConfig)
|
||||
// 也能出现在文档中: 通过 IApiDescriptionProvider 手工产出 ApiDescription,
|
||||
// 请求/响应 schema 由 Swashbuckle 自动分析, [SimApiDoc] 注解自动生效。
|
||||
builder.TryAddEnumerable(
|
||||
ServiceDescriptor.Transient<IApiDescriptionProvider,
|
||||
SimApiBuiltInRoutesDescriptionProvider>());
|
||||
}
|
||||
|
||||
// 使用Header转发,应对代理后获取真实ip
|
||||
@@ -316,7 +353,7 @@ public static class SimApiExtensions
|
||||
builder.Configure<ForwardedHeadersOptions>(fwOptions =>
|
||||
{
|
||||
fwOptions.ForwardedHeaders = ForwardedHeaders.All;
|
||||
fwOptions.KnownNetworks.Clear();
|
||||
fwOptions.KnownIPNetworks.Clear();
|
||||
fwOptions.KnownProxies.Clear();
|
||||
});
|
||||
}
|
||||
@@ -462,36 +499,19 @@ public static class SimApiExtensions
|
||||
builder.UseMiddleware<SimApiAuthMiddleware>();
|
||||
}
|
||||
|
||||
if (options.SimApiRouteOptions.UserInfoRoute != null)
|
||||
// 注册内置Route(UserInfo/Logout/WebConfig)。
|
||||
// 路径统一由 SimApiBuiltInRoutes 路由表提供: 配置为 null/空 的端点不注册。
|
||||
foreach (var route in SimApiBuiltInRoutes.Get(options.SimApiRouteOptions))
|
||||
{
|
||||
logger.LogInformation("注册内置Route: UserInfo => {}", options.SimApiRouteOptions.UserInfoRoute);
|
||||
builder.MapControllerRoute(name: "UserInfo", pattern: options.SimApiRouteOptions.UserInfoRoute,
|
||||
logger.LogInformation("注册内置Route: {RouteName} => {RoutePath} [{HttpMethods}]", route.RouteName,
|
||||
route.Path, string.Join(", ", route.HttpMethods));
|
||||
builder.MapControllerRoute(
|
||||
name: route.RouteName,
|
||||
pattern: route.Path,
|
||||
defaults: new
|
||||
{
|
||||
controller = "SimApiCommon",
|
||||
action = "UserInfo"
|
||||
});
|
||||
}
|
||||
|
||||
if (options.SimApiRouteOptions.LogoutRoute != null)
|
||||
{
|
||||
logger.LogInformation("注册内置Route: Logout => {}", options.SimApiRouteOptions.LogoutRoute);
|
||||
builder.MapControllerRoute(name: "Logout", pattern: options.SimApiRouteOptions.LogoutRoute,
|
||||
defaults: new
|
||||
{
|
||||
controller = "SimApiAuth",
|
||||
action = "Logout"
|
||||
});
|
||||
}
|
||||
|
||||
if (options.SimApiRouteOptions.WebConfigRoute != null)
|
||||
{
|
||||
logger.LogInformation("注册内置Route: Logout => {}", options.SimApiRouteOptions.WebConfigRoute);
|
||||
builder.MapControllerRoute(name: "WebConfig", pattern: options.SimApiRouteOptions.WebConfigRoute,
|
||||
defaults: new
|
||||
{
|
||||
controller = "SimApiCommon",
|
||||
action = "WebConfig"
|
||||
controller = route.Controller,
|
||||
action = route.Action
|
||||
});
|
||||
}
|
||||
|
||||
@@ -499,12 +519,14 @@ public static class SimApiExtensions
|
||||
{
|
||||
logger.LogInformation("开始配置SimApiDoc...");
|
||||
var docOptions = options.SimApiDocOptions;
|
||||
builder.UseSwagger(x => x.RouteTemplate = "/swagger/{documentName}.json")
|
||||
builder.UseSwagger(x => x.RouteTemplate = $"/{docOptions.UrlPrefix}/{{documentName}}.json")
|
||||
.UseSwaggerUI(x =>
|
||||
{
|
||||
x.RoutePrefix = docOptions.UrlPrefix;
|
||||
x.DocumentTitle = docOptions.DocumentTitle;
|
||||
foreach (var group in docOptions.ApiGroups)
|
||||
{
|
||||
// 相对端点: 页面位于 /{UrlPrefix}/, 自动解析为 /{UrlPrefix}/{group.Id}.json
|
||||
x.SwaggerEndpoint($"{group.Id}.json", name: group.Name);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user