ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Orchard Core OpenApi 模块实战指南:Swagger/ReDoc/Scalar 文档、Bearer/PKCE 认证与 NSwag 客户端生成

Orchard Core OpenApi 模块实战指南:Swagger/ReDoc/Scalar 文档、Bearer/PKCE 认证与 NSwag 客户端生成 CMS后端Web框架【免费下载链接】OrchardCoreOrchard Core is an open-source modular and multi-tenant application framework built with ASP.NET Core, and a content management system (CMS) built on top of that framework.项目地址https://gitcode.com/gh_mirrors/or/OrchardCore点击查看免费下载Orchard Core 的OrchardCore.OpenApi模块为站点自动暴露 OpenAPISwagger规范并提供 Swagger UI、ReDoc、Scalar 三种可视化文档界面同时通过 NSwag 自动生成 TypeScriptAxios与 C#HttpClient类型化客户端。本文围绕该模块的启用步骤、匿名/认证访问控制、基于 OAuth2 授权码 PKCE 的静默令牌获取机制、NSwag 客户端再生成流程以及 Bloom 前端的ApiService与通知系统展开并结合模块源码、配方文件与 NSwag 配置给出可落地的实操方案。模块概览与功能特性OrchardCore.OpenApi是一个「Api」分类下的模块其功能入口定义在 Manifest.cs包含一个主功能与三个独立的 UI 子功能功能 ID名称说明OrchardCore.OpenApiOpenApi基于Microsoft.AspNetCore.OpenApi的 OpenAPI 规范生成OrchardCore.OpenApi.SwaggerUIOpenApi Swagger UI交互式 API 浏览器位于~/swaggerOrchardCore.OpenApi.ReDocUIOpenApi ReDoc UI只读 API 文档位于~/redocOrchardCore.OpenApi.ScalarUIOpenApi Scalar UI现代化 API 参考页位于~/scalar/v1三个 UI 子功能都声明了对OrchardCore.OpenApi的依赖因此启用任意一个 UI 时主功能会自动被启用。从 Startup.cs 可以看到主Startup注册了AddOpenApi与AddSwaggerGenSwashbuckle并约定只包含带有 HTTP 动词的接口ShouldInclude/DocInclusionPredicate均判断HttpMethod ! nullSwaggerUIStartup、ReDocUIStartup、ScalarUIStartup三个类分别以[Feature(...)]特性挂接对应 UI 功能各 UI 的启用/禁用状态由Features 页面控制而不是在设置页中切换——这是理解后续配置章节的关键前提。快速开始Getting Started在管理后台按以下四步即可让文档界面跑起来在Configuration → Features中启用OrchardCore.OpenApi主功能并按需启用OpenApi Swagger UI、OpenApi ReDoc UI、OpenApi Scalar UI三个子功能使用拥有ViewOpenApiContent权限的账号登录Administrators 角色默认拥有该权限权限由模块中的Permissions类提供见 Startup.cs 中的AddPermissionProviderPermissions()若要让「Try it out」/「Send」按钮能够调用受保护的 API 接口执行OpenAPI Documentation — Bearer/PKCE配方Configuration → Recipes详见下文 API 认证访问对应文档地址/swagger、/redoc、/scalar/v1。文档端点的访问控制所有 OpenAPI 文档端点/swagger、/redoc、/scalar、/openapi都要求认证并具备ViewOpenApiContent权限。访问控制的中间件逻辑位于 Startup.cs未认证用户访问文档 UI 时被重定向到{pathBase}/admin登录页context.Response.Redirect已认证但无权限的用户收到403 ForbiddenJSON schema 端点如/swagger/v1/swagger.json默认同样需要ViewOpenApiContent权限未认证请求先尝试通过Apischeme 认证支持外部工具携带 Bearer 令牌认证失败时以ChallengeAsync(Api)返回带WWW-Authenticate头与 Problem Details 响应体的401 Unauthorized认证成功但无权限则ForbidAsync返回 403只有在 OpenApi 设置中开启Allow anonymous access to the API schema后JSON schema 端点才允许匿名访问。配置Configuration打开Configuration → Settings → OpenApi设置页由 OpenApiSettingsDisplayDriver.cs 与 OpenApiSettings.Edit.cshtml 呈现页面展示三个文档 UISwagger UI、ReDoc UI、Scalar UI的启用状态徽标——注意这些状态只是展示实际开关在 Features 页面禁用状态的 UI 访问时返回404 Not Found唯一的可配置项Allow anonymous access to the API schema。该设置的模型定义在 OpenApiSettings.cspublic class OpenApiSettings { /// summary /// Whether the OpenAPI JSON schema endpoints can be accessed without authentication. /// When cfalse/c (the default), the schema endpoints require the /// cViewOpenApiContent/c permission. /// /summary public bool AllowAnonymousSchemaAccess { get; set; } }默认关闭。关闭时 JSON schema 端点要求认证与ViewOpenApiContent权限未认证请求返回401 Unauthorized开启后/swagger/...json与/openapi...json端点可被匿名抓取外部工具如 NSwag CLI依赖这一行为拉取规范。设置项的开关判断逻辑在 Startup.cs 的中间件中实现settings.AllowAnonymousSchemaAccess为false时才执行认证与授权检查。配方OpenApiGeneration以及tools/OpenApiClientGenerator使用的OpenApiGenerationSetup配方会自动开启该设置。除此之外 OpenApi 模块没有其他需要手动配置的选项——文档 UI 的 API 认证在配方执行后自动生效见下一节。API 认证Bearer/PKCE 静默令牌流程Orchard Core 的 API 端点统一使用Api认证 scheme只接受 Bearer 令牌绝不使用会话 Cookie调用 API。因此 Swagger UI 与 Scalar 页面不提供传统的手动「Authorize」弹窗而是采用静默取令牌的方式模块向文档页面注入一个自举的 ES Module 脚本产物为 openapi-ui-auth.js源码在 openapi-ui-auth.ts脚本在隐藏的 iframe 中借助已登录的管理员 Cookie 会话针对同租户的 OpenID Connect 服务器执行 OAuth2授权码 PKCEpromptnone流程令牌在过期前以同样的方式静默续期并自动附加到每一次「Try it out」/「Send」请求上ReDoc 是纯只读文档没有请求交互面因此不需要令牌。如果静默请求因需要交互而失败——例如管理员尚无 OpenID 会话或客户端使用了非隐式同意类型explicit/systematic导致同意未授予——脚本会回退到可见授权流程一次性整页跳转到 OpenID 服务器建立会话/记录同意然后返回文档页此后静默登录与续期恢复正常。这保证了无论客户端配置的同意类型如何文档 UI 都能正常工作。相关常量定义在 OpenApiConstants.cs常量值用途DocumentationClientIdopenapi文档 UI 使用的 public OAuth2/PKCE 客户端 IDDocumentationScopesopenid email profile roles请求的作用域roles使令牌携带调用者角色供 API 权限校验使用SilentCallbackPath/OrchardCore.OpenApi/openapi-oidc-silent.html静默续期页面位于模块wwwroot必须与注册的 Redirect URI 完全一致AuthScriptPath/OrchardCore.OpenApi/Scripts/openapi-ui-auth/openapi-ui-auth.js注入到 Swagger/Scalar 页面的静默认证脚本用 OpenApiPkce 配方完成配置执行OpenAPI Documentation — Bearer/PKCE配方Configuration → Recipes其定义在 openapi-pkce.recipe.json。配方会启用OpenApi、OpenID Authorization Server、OpenID Token Validation、OpenID Management四个功能开启授权码流程并要求 PKCEAllowAuthorizationCodeFlow: true、RequireProofKeyForCodeExchange: true并针对Apischeme 启用本地同租户令牌校验OpenIdValidationSettings.Tenant: Default租户名需与实际运行租户一致否则校验 scheme 静默不注册所有 Bearer 调用都会 401注册一个public无密钥OpenID 应用客户端 ID 为openapi重定向 URI 指向模块的静默续期页/OrchardCore.OpenApi/openapi-oidc-silent.html并配置email、profile、roles三个 scope。执行前必须根据环境调整两处替换主机名将RedirectUris/PostLogoutRedirectUris中的https://localhost:5001换成租户真实 Origin重定向 URI 的路径必须完全匹配包含任何租户 URL 前缀赋予 API 权限角色确保使用「Try it out」的用户拥有授予相关 API 权限的角色——rolesscope 会把角色带进访问令牌而 API 的权限检查正是基于这些角色求值的。重要说明Client Credentials 与 Password 授权类型被有意排除——这两种流程都要求把客户端密钥内嵌在浏览器交付的代码中无法保密。对浏览器端客户端授权码 PKCEpublic client是 OAuth 2.0 安全最佳实践推荐的流程。脚本的安全设计注入脚本通过script标签自身的data-*属性配置由 Startup.BuildAuthHeadContent 生成没有内联脚本因此页面可在严格 Content Security Policy 下运行。设计上非常保守令牌不进 Web 存储访问令牌只保存在内存中每次页面加载执行一次廉价的同源静默登录仅同源Bearer 令牌只附加到路径包含/api/的同源请求上——即使规范中列了外部服务器、或用户在 Scalar 中编辑了目标 URL令牌也不会被带出schema、UI 资源与 OIDC 请求继续使用管理员 Cookie自愈如果服务器拒绝缓存的令牌例如站点被重新设置后旧令牌无法解密脚本会丢弃它、静默登录一次并重试该请求一次尊重手动令牌粘贴到 Swagger Authorize 对话框中的令牌会原样发送不做覆盖或重试。Swagger UI 还通过UseRequestInterceptor在请求拦截器中把/api/请求的credentials置为omit确保 API 调用走 Bearer 而不是管理员的 Cookie。OpenAPI 客户端生成NSwag模块使用 NSwag 配置 OrchardCore.OpenApi.nswag 从实时 OpenAPI 规范生成类型化客户端。自动化再生成推荐tools/OpenApiClientGenerator控制台项目可无头运行整条流水线——无需运行开发服务器或浏览器dotnet run --project tools/OpenApiClientGenerator -c Release yarn build # 依据重新生成的 TypeScript 客户端刷新打包的 JS其实现见 Program.cs在进程内于临时端口启动 CMS通过OrchardCore.AutoSetup以OpenApiGenerationSetup配方与OpenApiGeneration配方功能集一致配置 Default 租户抓取swagger.json再调用 NSwag CLI与手动路径相同的先决条件。生成是确定性的操作按路由路径 HTTP 动词排序因此没有 API 变更时重新生成会得到逐字节一致的客户端——这正是 Startup.cs 中OrderActionsBy(apiDesc ${apiDesc.RelativePath}_{apiDesc.HttpMethod})的目的模块化架构下功能启用顺序会影响 action 发现顺序显式排序可保证只有真正变化的端点才会在客户端中移位。手动流程在需要针对真实运行的开发服务器再生成、或想交互式检查swagger.json时仍然有用。先决条件NSwag CLI——安装全局工具dotnet tool install -g NSwag.ConsoleCore运行中的应用——站点必须正在运行NSwag 才能抓取 OpenAPI JSON匿名 schema 访问——JSON schema 端点默认需要认证而 NSwag CLI 是匿名抓取的。在租户上执行OpenApi Generation配方Configuration → Recipes该配方在 openapi-generation.recipe.json 中定义会启用完整 API 功能集OrchardCore.Contents、OrchardCore.Media、OrchardCore.Media.Tus、OrchardCore.Queries、OrchardCore.Tenants、OrchardCore.Search.Lucene、OrchardCore.Search.Elasticsearch、OrchardCore.OpenApi并开启Allow anonymous access to the API schema设置也可以在Configuration → Settings → OpenApi中手动开启。警告配方的 settings 步骤会替换租户已存储的 OpenApi 设置因此任何已配置的 OAuth2 认证信息client ID、端点 URL、scopes都会被重置。建议在一次性生成的临时租户上执行对已配置的租户请改为手动开启该设置。再生成步骤手动1. 启动应用cd src/OrchardCore.Cms.Web dotnet run等待控制台输出Application started.。2. 验证 OpenAPI 端点Swagger UIhttps://localhost:5001/swaggerOpenAPI JSONhttps://localhost:5001/swagger/v1/swagger.json3. 运行 NSwag在模块目录下执行cd src/OrchardCore.Modules/OrchardCore.OpenApi nswag run OrchardCore.OpenApi.nswag生成两个客户端客户端输出路径TypeScript (Axios).scripts/bloom/services/OpenApiClient.tsC# (HttpClient)Services/OpenApiClient.cs4. 构建前端资源# 在仓库根目录执行 yarn buildNSwag 配置要点从 OrchardCore.OpenApi.nswag 可以看出Source URLhttps://localhost:5001/swagger/v1/swagger.jsondocumentGenerator.fromDocument.urlC# JSON 库SystemTextJsonjsonLibrary: SystemTextJson版本 8.0——不使用 Newtonsoft.JsonTypeScript 模板Axiostemplate: Axios输出../../../.scripts/bloom/services/OpenApiClient.tsC# HTTP 层System.Net.Http.HttpClienthttpClientType输出Services/OpenApiClient.csC# 客户端命名空间为OrchardCore.OpenApi.Services并排除了JsonInheritanceConverter与QueriesItemsDto两个类型excludedTypeNames。ApiServiceapi-service.tsApiService类位于 api-service.ts是可复用的 HTTP 服务用认证处理包装 Axios并向后端生成的 NSwag 客户端提供配置好的 Axios 实例。它同时支持 Cookie 与 Bearer 令牌两种认证方式。认证类型类型行为cookie默认设置withCredentials: true并从页面附加防伪anti-forgery令牌bearer设置withCredentials: false并附加Authorization: Bearer token请求头基本用法import { ApiService, createApiService } from bloom/services/api-service; // Cookie 认证默认——用于已登录用户的后台页面。 const api new ApiService(); const response await api.get(/api/content/my-item-id); // Bearer 认证——用于机器对机器或外部消费者。 const api new ApiService({ authType: bearer, token: eyJ... }); await api.post(/api/content, { contentType: Article }); // 之后更新令牌例如刷新之后。 api.setToken(newToken...);与 NSwag 生成客户端配合ApiService通过getAxiosInstance()暴露底层的 Axios 实例可直接传给 NSwag 生成的Client构造器import { ApiService } from bloom/services/api-service; import { Client } from bloom/services/OpenApiClient; // Cookie 认证——后台页面。 const apiService new ApiService(); const client new Client(, apiService.getAxiosInstance()); await client.contentGET(my-content-item-id); // Bearer 认证——外部消费者。 const apiService new ApiService({ authType: bearer, token: accessToken }); const client new Client(, apiService.getAxiosInstance()); await client.contentGET(my-content-item-id);这样 NSwag 生成的客户端就自动获得了全部认证处理Cookie 防伪令牌或 Bearer 令牌无需额外配置。手动令牌管理const apiService new ApiService({ authType: bearer, token: eyJ... }); const client new Client(, apiService.getAxiosInstance()); // 之后更新令牌例如刷新之后。 apiService.setToken(newToken...);可用方法方法说明getT(url, config?)执行 GET 请求postT(url, data?, config?)执行 POST 请求putT(url, data?, config?)执行 PUT 请求patchT(url, data?, config?)执行 PATCH 请求deleteT(url, config?)执行 DELETE 请求setToken(token)更新后续请求使用的 Bearer 令牌getAxiosInstance()返回底层 Axios 实例供生成客户端使用添加新的 API 端点要让新端点被 OpenAPI 规范自动发现需遵循以下约定创建继承自ControllerBase的控制器添加[ApiController]特性添加[Route(api/...)]特性受保护端点添加[Authorize(AuthenticationSchemes Api)]使用 XML 文档注释///以获得更丰富的 Swagger 描述用[ProducesResponseType]装饰 action记录全部响应类型按上文再生成步骤更新客户端。示例控制器[ApiController] [Route(api/dashboard/myfeature)] [Authorize(AuthenticationSchemes Api)] public sealed class MyFeatureController : ControllerBase { /// summary /// Gets data from my feature. /// /summary [HttpGet] [ProducesResponseType(typeof(MyDataDto), StatusCodes.Status200OK)] public async TaskIActionResult GetDataAsync() { // Implementation } }提示模块的 OpenAPI 安全定义在 Startup.cs 中统一声明——HTTP bearer schemeSecuritySchemeType.HttpScheme bearerBearerFormat JWT并全局应用安全要求保证生成文档中的操作显示为「受保护」同时让 NSwag 生成的客户端具备令牌感知能力。前端通知系统Problem Details 集成Bloom 前端框架自带一个通知服务bloom/services/notifications/notifier开箱即用地理解 RFC 9457 Problem Details 响应形成从 API 到 UI 的无缝错误处理链路。工作方式API 端点返回ProblemDetails响应例如通过Problem()或TypedResults.Problem()NSwag 生成的客户端将非 2xx 响应作为错误抛出调用代码把错误传给notify()notifier 识别出ProblemDetails结构并转换为 UI 通知。架构┌─────────────────────┐ ProblemDetails JSON ┌─────────────────────────┐ │ API Controller │ ──────────────────────────► │ NSwag OpenApiClient.ts │ │ (C# / Server) │ │ (throws on non-2xx) │ └─────────────────────┘ └────────┬────────────┘ │ error ┌────────────▼────────────┐ │ Service Layer │ │ (e.g. FileDataService) │ └────────────┬────────────┘ │ catch ┌────────────▼────────────┐ │ notify(error) │ │ (notifier.ts) │ └────────────┬────────────┘ │ emit(notify) ┌────────────▼────────────┐ │ NotificationToast.vue │ │ (UI Toast Component) │ └─────────────────────────┘使用 notifier初始化在应用启动时注册一次通知总线import { registerNotificationBus } from bloom/services/notifications/notifier; registerNotificationBus();发送通知import { notify, NotificationMessage } from bloom/services/notifications/notifier; import { SeverityLevel } from bloom/services/notifications/interfaces; // 成功通知 notify(new NotificationMessage({ summary: Success, detail: File uploaded successfully., severity: SeverityLevel.Success, })); // 捕获 API 错误——ProblemDetails 自动处理 try { await fileDataService.deleteMedia(path); } catch (error) { notify(error); // title → summary, detail → detail, severity → Error }支持的消息类型notify()函数接受多种输入形态输入类型处理方式NotificationMessage直接透传。ValidationProblemDetails含errorstitle→ summary字段错误拼接后作为 detail。ProblemDetails含title/detailtitle→ summarydetail→ detail。Errorsummary 为Server Errormessage作为 detail。Falsy / 未知回退为通用错误消息。监听通知Vue 3script setup langts import { onMounted, onUnmounted, ref } from vue; import { registerNotificationBus } from bloom/services/notifications/notifier; import type { NotificationMessage } from bloom/services/notifications/notifier; const bus registerNotificationBus(); const messages refNotificationMessage[]([]); function onNotify(msg: NotificationMessage) { messages.value.push(msg); } onMounted(() bus.on(notify, onNotify)); onUnmounted(() bus.off(notify, onNotify)); /script严重级别SeverityLevel枚举bloom/services/notifications/interfaces定义四个级别级别典型用途Success确认已完成的操作文件移动、条目保存。Info信息性消息。Warn非阻塞警告。ErrorAPI 错误、校验失败、未预期异常。故障排查Troubleshooting「Try it out」请求返回401 Unauthorized执行 OpenApiPkce 配方最常见原因是静默认证客户端未配置或OpenID Token Validation功能未启用——没有它 API 无法校验 Bearer 令牌。配方同时完成两者见 API 认证重定向 URI 不匹配注册的重定向 URI 必须精确等于你的租户 Origin 加上/OrchardCore.OpenApi/openapi-oidc-silent.html包含任何租户 URL 前缀。不匹配会使静默登录失败——在浏览器控制台查找[openapi-ui]错误缺少角色令牌已获取但端点仍返回401/403——检查登录用户是否拥有被调用 API 权限所需的角色rolesscope 会将角色带进令牌重新设置后的陈旧令牌如果站点被重新设置而文档标签页保持打开第一次被拒绝的请求会触发一次自动静默重新登录并重试最坏情况下刷新页面即可。端点未出现在 Swagger 中确认控制器带有[ApiController]特性确认包含控制器的模块已启用检查路由是否与 MVC 路由冲突添加新控制器后重启应用。NSwag 生成失败确认应用正在运行且可访问检查 OpenAPI JSON URL 是否正确确认 NSwag CLI 安装正确dotnet tool list -g检查.nswag配置是否存在语法错误。TypeScript 编译错误运行yarn build重新构建所有资源检查组件中的类型不匹配确认 import 使用了指向OpenApiClient.ts的正确路径。相关资源模块源码与配方OrchardCore.OpenApi 模块设置模型OpenApiSettings.csNSwag 配置OrchardCore.OpenApi.nswag客户端生成工具tools/OpenApiClientGeneratorTypeScript 客户端与 ApiService.scripts/bloom/services赞分享CMS后端Web框架【免费下载链接】OrchardCoreOrchard Core is an open-source modular and multi-tenant application framework built with ASP.NET Core, and a content management system (CMS) built on top of that framework.项目地址https://gitcode.com/gh_mirrors/or/OrchardCore点击查看免费下载相关推荐黑苹果macOS版本选择指南用 OpCore-Simplify 选对系统并自动生成配置黑苹果macOS版本选择指南用 OpCore Simplify 选对系统并自动生成配置 黑苹果选哪个 macOS 版本取决于硬件而不是运气。OpCore S开发工具CLINSwag终极指南如何在ASP.NET Core中集成Swagger UI与ReDoc文档NSwag终极指南如何在ASP.NET Core中集成Swagger UI与ReDoc文档 NSwag是.NET生态系统中功能最完整的Swagger/Open开发工具代码生成API设计Mermaid Live Editor 使用指南免费把代码变成图表的完整教程Mermaid Live Editor 使用指南免费把代码变成图表的完整教程 如果你需要在文档、网页或演示里插入一张流程图、时序图或甘特图又不想打开复杂的绘前端开发者工具数据可视化上一篇Redisson集群部署终极指南数据分片与负载均衡最佳实践 下一篇Devise跳过会话存储skip_session_storage应用创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表