mirror of
https://github.com/nsnail/IGeekFan.AspNetCore.Knife4jUI.git
synced 2025-12-26 07:35:48 +08:00
Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
089c238126 | ||
|
|
99fd092b24 | ||
|
|
1e9fb7d195 | ||
|
|
66cd518872 | ||
|
|
b7ae3cf42e | ||
|
|
311a886830 | ||
|
|
3c584c3a95 | ||
|
|
8a4066a40e | ||
|
|
35fa68cffc | ||
|
|
8ea53827f5 | ||
|
|
1d63f2c585 |
@@ -22,6 +22,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SwaggerUI_IndexStream_Knife
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IGeekFan.AspNetCore.Knife4jUI", "src\IGeekFan.AspNetCore.Knife4jUI\IGeekFan.AspNetCore.Knife4jUI.csproj", "{6C784918-BE29-4FEF-8AC3-9D34A38DE822}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "WebSites", "WebSites", "{86851B6C-3504-4879-8464-1DB422D46BA0}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OAuth2Integration", "test\WebSites\OAuth2Integration\OAuth2Integration.csproj", "{9E8D8F42-33F0-4F2D-9B56-1AB1B33DE1FA}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NSwag.Swagger.Knife4jUI", "test\WebSites\NSwag.Swagger.Knife4jUI\NSwag.Swagger.Knife4jUI.csproj", "{42B4C1C3-AE38-47C7-AAAA-FE0FDA7DADEB}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@@ -44,6 +50,14 @@ Global
|
||||
{6C784918-BE29-4FEF-8AC3-9D34A38DE822}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{6C784918-BE29-4FEF-8AC3-9D34A38DE822}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{6C784918-BE29-4FEF-8AC3-9D34A38DE822}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{9E8D8F42-33F0-4F2D-9B56-1AB1B33DE1FA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{9E8D8F42-33F0-4F2D-9B56-1AB1B33DE1FA}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{9E8D8F42-33F0-4F2D-9B56-1AB1B33DE1FA}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{9E8D8F42-33F0-4F2D-9B56-1AB1B33DE1FA}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{42B4C1C3-AE38-47C7-AAAA-FE0FDA7DADEB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{42B4C1C3-AE38-47C7-AAAA-FE0FDA7DADEB}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{42B4C1C3-AE38-47C7-AAAA-FE0FDA7DADEB}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{42B4C1C3-AE38-47C7-AAAA-FE0FDA7DADEB}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
@@ -53,6 +67,9 @@ Global
|
||||
{86A24FA0-E85D-4BDD-97D7-B990C50A40A9} = {75C51574-4CBD-403B-8182-8BF2A6DCFD43}
|
||||
{1D6FD5CA-5D58-4895-8545-A93099CE1AD4} = {C146A419-15E0-4475-9623-706C5E2DCE0B}
|
||||
{6C784918-BE29-4FEF-8AC3-9D34A38DE822} = {929BB2D7-C678-4BE8-8AA9-F271A2AE4545}
|
||||
{86851B6C-3504-4879-8464-1DB422D46BA0} = {75C51574-4CBD-403B-8182-8BF2A6DCFD43}
|
||||
{9E8D8F42-33F0-4F2D-9B56-1AB1B33DE1FA} = {86851B6C-3504-4879-8464-1DB422D46BA0}
|
||||
{42B4C1C3-AE38-47C7-AAAA-FE0FDA7DADEB} = {86851B6C-3504-4879-8464-1DB422D46BA0}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {9D77CCB4-F597-421B-9EF9-52D4B0AC382D}
|
||||
|
||||
43
README.md
43
README.md
@@ -20,11 +20,24 @@
|
||||
|
||||
### 🚀安装包
|
||||
|
||||
以下为使用Swashbuckle.AspNetCore.Swagger底层组件
|
||||
|
||||
1.Install the standard Nuget package into your ASP.NET Core application.
|
||||
|
||||
```
|
||||
Package Manager : Install-Package IGeekFan.AspNetCore.Knife4jUI
|
||||
CLI : dotnet add package IGeekFan.AspNetCore.Knife4jUI
|
||||
Package Manager :
|
||||
|
||||
Install-Package Swashbuckle.AspNetCore.Swagger
|
||||
Install-Package Swashbuckle.AspNetCore.SwaggerGen
|
||||
Install-Package IGeekFan.AspNetCore.Knife4jUI
|
||||
|
||||
OR
|
||||
|
||||
CLI :
|
||||
|
||||
dotnet add package Swashbuckle.AspNetCore.Swagger
|
||||
dotnet add package Swashbuckle.AspNetCore.SwaggerGen
|
||||
dotnet add package IGeekFan.AspNetCore.Knife4jUI
|
||||
```
|
||||
|
||||
2.In the ConfigureServices method of Startup.cs, register the Swagger generator, defining one or more Swagger documents.
|
||||
@@ -92,6 +105,32 @@ c.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, "SwaggerDemo.xml"),t
|
||||
|
||||
|
||||
|
||||
### NSwag.AspNetCore
|
||||
(请参考目录test/WebSites/NSwag.Swagger.Knife4jUI)
|
||||
|
||||
```
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
// 其它Service
|
||||
services.AddOpenApiDocument();
|
||||
}
|
||||
```
|
||||
|
||||
```
|
||||
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
|
||||
{
|
||||
// 其它 Use
|
||||
app.UseOpenApi();
|
||||
app.UseKnife4UI(c =>
|
||||
{
|
||||
c.RoutePrefix = "";
|
||||
c.SwaggerEndpoint("/swagger/v1/swagger.json");
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
即可使用 Knife4jUI
|
||||
|
||||
### 🔎 效果图
|
||||
运行项目,打开 https://localhost:5001/index.html#/home
|
||||
|
||||
|
||||
@@ -8,9 +8,9 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.10.9" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore.Swagger" Version="5.5.1" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" Version="5.5.1" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUi" Version="5.5.1" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore.Swagger" Version="6.1.4" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" Version="6.1.4" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUi" Version="6.1.4" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
|
||||
@@ -1,39 +1,48 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>netstandard2.0;netcoreapp3.0</TargetFrameworks>
|
||||
<TargetFrameworks>netstandard2.0;netcoreapp3.1;net5.0</TargetFrameworks>
|
||||
<Description>Middleware to expose an embedded version of the knife4j-vue-v3 from an ASP.NET Core application</Description>
|
||||
<NoWarn>$(NoWarn);1591</NoWarn>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
<PackageId></PackageId>
|
||||
<PackageId>IGeekFan.AspNetCore.Knife4jUI</PackageId>
|
||||
<PackageTags>swagger;documentation;discovery;help;webapi;aspnet;aspnetcore</PackageTags>
|
||||
<PackageProjectUrl>https://github.com/luoyunchong/IGeekFan.AspNetCore.Knife4jUI</PackageProjectUrl>
|
||||
<RepositoryType>git</RepositoryType>
|
||||
<RepositoryUrl>https://github.com/luoyunchong/IGeekFan.AspNetCore.Knife4jUI.git</RepositoryUrl>
|
||||
<RootNamespace>IGeekFan.AspNetCore.Knife4jUI</RootNamespace>
|
||||
<Version>0.0.3</Version>
|
||||
<Version>0.0.10</Version>
|
||||
<Company />
|
||||
<Authors>igeekfan;xiaoym;</Authors>
|
||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||
<Copyright>Apache License 2.0</Copyright>
|
||||
<PackageLicenseExpression></PackageLicenseExpression>
|
||||
<AssemblyVersion>0.0.3.0</AssemblyVersion>
|
||||
<AssemblyVersion>0.0.10.0</AssemblyVersion>
|
||||
<FileVersion>0.0.10.0</FileVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' ">
|
||||
<!-- Using SourceLink -->
|
||||
<PropertyGroup>
|
||||
<IncludeSymbols>true</IncludeSymbols>
|
||||
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
|
||||
<PublishRepositoryUrl>true</PublishRepositoryUrl>
|
||||
<EmbedUntrackedSources>true</EmbedUntrackedSources>
|
||||
<AllowedOutputExtensionsInPackageBuildOutputFolder>$(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb</AllowedOutputExtensionsInPackageBuildOutputFolder>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0' ">
|
||||
<PackageReference Include="Microsoft.AspNetCore.Routing" Version="2.1.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.StaticFiles" Version="2.1.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="2.1.0" />
|
||||
<PackageReference Include="System.Text.Json" Version="4.6.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition=" '$(TargetFramework)' == 'netcoreapp3.0' ">
|
||||
<ItemGroup Condition="'$(TargetFramework)' != 'netstandard2.0' ">
|
||||
<FrameworkReference Include="Microsoft.AspNetCore.App" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Swashbuckle.AspNetCore.Swagger" Version="5.5.1" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" Version="5.5.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="knife4j/**/*" />
|
||||
|
||||
@@ -136,6 +136,8 @@ namespace IGeekFan.AspNetCore.Knife4jUI
|
||||
public string Url { get; set; }
|
||||
|
||||
public string Name { get; set; }
|
||||
|
||||
public string SwaggerVersion { get; set; } = "3.0";
|
||||
}
|
||||
|
||||
public enum ModelRendering
|
||||
|
||||
@@ -14,8 +14,8 @@ using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using System.Collections.Generic;
|
||||
#if NETCOREAPP3_0
|
||||
using IHostingEnvironment = Microsoft.AspNetCore.Hosting.IWebHostEnvironment;
|
||||
#if NETSTANDARD2_0
|
||||
using IWebHostEnvironment = Microsoft.AspNetCore.Hosting.IHostingEnvironment;
|
||||
#endif
|
||||
namespace IGeekFan.AspNetCore.Knife4jUI
|
||||
{
|
||||
@@ -29,7 +29,7 @@ namespace IGeekFan.AspNetCore.Knife4jUI
|
||||
|
||||
public Knife4jUIMiddleware(
|
||||
RequestDelegate next,
|
||||
IHostingEnvironment hostingEnv,
|
||||
IWebHostEnvironment hostingEnv,
|
||||
ILoggerFactory loggerFactory,
|
||||
Knife4UIOptions options)
|
||||
{
|
||||
@@ -45,6 +45,7 @@ namespace IGeekFan.AspNetCore.Knife4jUI
|
||||
|
||||
public async Task Invoke(HttpContext httpContext)
|
||||
{
|
||||
|
||||
var httpMethod = httpContext.Request.Method;
|
||||
var path = httpContext.Request.Path.Value;
|
||||
|
||||
@@ -66,7 +67,7 @@ namespace IGeekFan.AspNetCore.Knife4jUI
|
||||
return;
|
||||
}
|
||||
|
||||
if (httpMethod == "GET" && Regex.IsMatch(path, $"^/v3/api-docs/swagger-config$"))
|
||||
if (httpMethod == "GET" && Regex.IsMatch(path, $"/swagger-resources$"))
|
||||
{
|
||||
await RespondWithConfig(httpContext.Response);
|
||||
return;
|
||||
@@ -74,15 +75,15 @@ namespace IGeekFan.AspNetCore.Knife4jUI
|
||||
|
||||
await _staticFileMiddleware.Invoke(httpContext);
|
||||
}
|
||||
|
||||
|
||||
private async Task RespondWithConfig(HttpResponse response)
|
||||
{
|
||||
await response.WriteAsync(JsonSerializer.Serialize(_options.ConfigObject, _jsonSerializerOptions));
|
||||
await response.WriteAsync(JsonSerializer.Serialize(_options.ConfigObject.Urls, _jsonSerializerOptions));
|
||||
}
|
||||
|
||||
private StaticFileMiddleware CreateStaticFileMiddleware(
|
||||
RequestDelegate next,
|
||||
IHostingEnvironment hostingEnv,
|
||||
IWebHostEnvironment hostingEnv,
|
||||
ILoggerFactory loggerFactory,
|
||||
Knife4UIOptions options)
|
||||
{
|
||||
|
||||
@@ -6,20 +6,37 @@
|
||||
<meta name=viewport content="width=device-width,initial-scale=1">
|
||||
<link rel=icon href=favicon.ico>
|
||||
<title>%(DocumentTitle)</title>
|
||||
<link href=knife4j/css/app.b932f742.css rel=preload>
|
||||
<link href=knife4j/js/app.920753bc.js rel=preload as=script>
|
||||
<link href=knife4j/js/chunk-vendors.e86fea24.js rel=preload as=script>
|
||||
<link href=knife4j/css/app.b932f742.css rel=stylesheet>
|
||||
<link href=knife4j/css/chunk-51277dbe.57225f85.css rel=prefetch>
|
||||
<link href=knife4j/js/chunk-069eb437.0b47243d.js rel=prefetch>
|
||||
<link href=knife4j/js/chunk-0fd67716.d57e2c41.js rel=prefetch>
|
||||
<link href=knife4j/js/chunk-2d0af44e.c299c1d4.js rel=prefetch>
|
||||
<link href=knife4j/js/chunk-2d0bd799.cc91c520.js rel=prefetch>
|
||||
<link href=knife4j/js/chunk-2d0d0b98.cb1dea78.js rel=prefetch>
|
||||
<link href=knife4j/js/chunk-2d0da532.dd3c929c.js rel=prefetch>
|
||||
<link href=knife4j/js/chunk-2d22269d.bd9173e1.js rel=prefetch>
|
||||
<link href=knife4j/js/chunk-3b888a65.8737ce4f.js rel=prefetch>
|
||||
<link href=knife4j/js/chunk-3ec4aaa8.a79d19f8.js rel=prefetch>
|
||||
<link href=knife4j/js/chunk-51277dbe.4335d8bb.js rel=prefetch>
|
||||
<link href=knife4j/js/chunk-589faee0.b24e5f3d.js rel=prefetch>
|
||||
<link href=knife4j/js/chunk-735c675c.76ef1019.js rel=prefetch>
|
||||
<link href=knife4j/js/chunk-adb9e944.b888f4bd.js rel=prefetch>
|
||||
<link href=knife4j/css/app.284871fa.css rel=preload as=style>
|
||||
<link href=knife4j/css/chunk-vendors.3f2387de.css rel=preload as=style>
|
||||
<link href=knife4j/js/app.703cb2b2.js rel=preload as=script>
|
||||
<link href=knife4j/js/chunk-vendors.90e8ba20.js rel=preload as=script>
|
||||
<link href=knife4j/css/chunk-vendors.3f2387de.css rel=stylesheet>
|
||||
<link href=knife4j/css/app.284871fa.css rel=stylesheet>
|
||||
%(HeadContent)
|
||||
</head>
|
||||
<body>
|
||||
<noscript><strong>We're sorry but knife4j-vue doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id=app></div>
|
||||
<script src=knife4j/js/chunk-vendors.e86fea24.js></script>
|
||||
<script src=knife4j/js/app.920753bc.js></script>
|
||||
<noscript><strong>We're sorry but knife4j-vue doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript>
|
||||
<div id=app></div>
|
||||
<script src=knife4j/js/chunk-vendors.90e8ba20.js></script>
|
||||
<script src=knife4j/js/app.703cb2b2.js></script>
|
||||
<script>
|
||||
window.onload = function () {
|
||||
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
||||
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
@@ -0,0 +1 @@
|
||||
.api-tab[data-v-7da2228c]{margin-top:15px}.api-tab .ant-tag[data-v-7da2228c]{height:32px;line-height:32px}.api-basic[data-v-7da2228c]{padding:11px}.api-basic-title[data-v-7da2228c]{font-size:14px;font-weight:700}.api-basic-body[data-v-7da2228c]{font-size:14px;font-family:-webkit-body}.api-description[data-v-7da2228c]{border-left:4px solid #ddd;line-height:30px}.api-body-desc[data-v-7da2228c]{padding:10px;min-height:35px;-webkit-box-sizing:border-box;box-sizing:border-box;border:1px solid #e8e8e8}.ant-card-body[data-v-7da2228c]{padding:5px}.api-title[data-v-7da2228c]{margin-top:10px;margin-bottom:5px;font-size:16px;font-weight:600;height:30px;line-height:30px;border-left:4px solid #00ab6d;text-indent:8px}.content-line[data-v-7da2228c]{height:25px;line-height:25px}.content-line-count[data-v-7da2228c]{height:35px;line-height:35px}.divider[data-v-7da2228c]{margin:4px 0}
|
||||
File diff suppressed because one or more lines are too long
Binary file not shown.
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB |
|
Before Width: | Height: | Size: 306 KiB After Width: | Height: | Size: 306 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
File diff suppressed because one or more lines are too long
BIN
src/IGeekFan.AspNetCore.Knife4jUI/knife4j/js/app.703cb2b2.js.gz
Normal file
BIN
src/IGeekFan.AspNetCore.Knife4jUI/knife4j/js/app.703cb2b2.js.gz
Normal file
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
@@ -0,0 +1 @@
|
||||
(window.webpackJsonp=window.webpackJsonp||[]).push([["chunk-2d0af44e"],{"0e36":function(e,t,i){"use strict";i.r(t);var n={name:"EditorShow",components:{editor:i("7c9e")},props:{value:{type:String,required:!0,default:""},xmlMode:{type:Boolean,default:!1,required:!1}},data:function(){return{lang:"json",editor:null,editorHeight:200}},methods:{change:function(e){this.$emit("change",e)},resetEditorHeight:function(){var e=this;setTimeout((function(){var t=e.editor.session.getLength();1==t&&(t=10);var i=16*t;e.editorHeight=i}),300)},editorInit:function(e){var t=this;this.editor=e,i("2099"),i("818b"),i("0696"),this.xmlMode&&(this.lang="xml"),i("1d29"),this.resetEditorHeight(),this.editor.renderer.on("afterRender",(function(){t.$emit("showDescription","123")}))}}},o=i("2877"),r=Object(o.a)(n,(function(){var e=this,t=e.$createElement,i=e._self._c||t;return i("div",[i("editor",{attrs:{value:e.value,lang:e.lang,theme:"eclipse",width:"100%",height:e.editorHeight},on:{init:e.editorInit,input:e.change}})],1)}),[],!1,null,null,null);t.default=r.exports}}]);
|
||||
@@ -0,0 +1 @@
|
||||
(window.webpackJsonp=window.webpackJsonp||[]).push([["chunk-2d0bd799"],{"2bc6":function(t,e,a){"use strict";a.r(e);var n={name:"DataType",props:{text:{type:String,default:"string",required:!0},record:{type:Object,required:!0}},data:function(){return{validators:[]}},created:function(){this.intiValidator()},methods:{intiValidator:function(){var t=this.record;if(null!=t.validateInstance)for(var e in this.getJsonKeyLength(t.validateInstance),t.validateInstance){var a=e+":"+t.validateInstance[e];this.validators.push({key:e,val:a})}},getJsonKeyLength:function(t){var e=0;if(null!=t)for(var a in t)t.hasOwnProperty(a)&&e++;return e}}},r=a("2877"),i=Object(r.a)(n,(function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("div",[t.record.validateStatus?a("span",{staticClass:"knife4j-request-validate-jsr"},[a("a-tooltip",{attrs:{placement:"right"}},[a("template",{slot:"title"},t._l(t.validators,(function(e){return a("div",{key:e.key},[t._v(t._s(e.val))])})),0),t._v(" "+t._s(t.text)+" ")],2)],1):a("span",[t._v(t._s(null==t.text||""==t.text?"string":t.text))])])}),[],!1,null,null,null);e.default=i.exports}}]);
|
||||
@@ -0,0 +1 @@
|
||||
(window.webpackJsonp=window.webpackJsonp||[]).push([["chunk-2d0d0b98"],{"68cc":function(n,e,t){"use strict";t.r(e);var s=t("0e54"),r=t.n(s);r.a.setOptions({gfm:!0,tables:!0,breaks:!1,pedantic:!1,sanitize:!1,smartLists:!0,smartypants:!1});var a={name:"Markdown",props:{source:{type:String}},computed:{markdownSource:function(){return r()(this.source)}}},o=t("2877"),c=Object(o.a)(a,(function(){var n=this,e=n.$createElement;return(n._self._c||e)("div",{staticClass:"knife4j-markdown",domProps:{innerHTML:n._s(n.markdownSource)}})}),[],!1,null,null,null);e.default=c.exports}}]);
|
||||
@@ -0,0 +1 @@
|
||||
(window.webpackJsonp=window.webpackJsonp||[]).push([["chunk-2d0da532"],{"6ab3":function(e,n,t){"use strict";t.r(n);var u=(t("5609"),t("b1c7"),{name:"OAuth2",components:{},data:function(){return{api:null,swaggerInstance:null,debugSupport:!1}},computed:{swagger:function(){return this.$store.state.globals.swagger}},mounted:function(){},beforeCreate:function(){},created:function(){},methods:{}}),a=t("2877"),o=Object(a.a)(u,(function(){var e=this,n=e.$createElement;return(e._self._c||n)("a-row",[e._v(" 我是OAuth2回调页面 ")])}),[],!1,null,"5ccd4e78",null);n.default=o.exports}}]);
|
||||
@@ -0,0 +1 @@
|
||||
(window.webpackJsonp=window.webpackJsonp||[]).push([["chunk-2d22269d"],{cf04:function(t,e,i){"use strict";i.r(e);var n={name:"EditorShow",components:{editor:i("7c9e")},props:{value:{type:String,required:!0,default:""}},data:function(){return{lang:"javascript",editor:null,editorHeight:200}},methods:{resetEditorHeight:function(){var t=this;setTimeout((function(){var e=t.editor.session.getLength();1==e&&(e=10);var i=16*e;t.editorHeight=i}),300)},change:function(t){this.$emit("change",t)},editorInit:function(t){var e=this;this.editor=t,i("2099"),i("bb36"),i("1d29"),this.resetEditorHeight(),this.editor.renderer.on("afterRender",(function(){e.$emit("showDescription","123")}))}}},r=i("2877"),o=Object(r.a)(n,(function(){var t=this,e=t.$createElement,i=t._self._c||e;return i("div",[i("editor",{attrs:{value:t.value,lang:t.lang,theme:"eclipse",width:"100%",height:t.editorHeight},on:{init:t.editorInit,input:t.change}})],1)}),[],!1,null,null,null);e.default=o.exports}}]);
|
||||
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
@@ -0,0 +1,6 @@
|
||||
/*!
|
||||
* clipboard.js v2.0.6
|
||||
* https://clipboardjs.com/
|
||||
*
|
||||
* Licensed MIT © Zeno Rocha
|
||||
*/
|
||||
Binary file not shown.
File diff suppressed because one or more lines are too long
@@ -0,0 +1,6 @@
|
||||
/*!
|
||||
* clipboard.js v2.0.6
|
||||
* https://clipboardjs.com/
|
||||
*
|
||||
* Licensed MIT © Zeno Rocha
|
||||
*/
|
||||
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
@@ -0,0 +1,6 @@
|
||||
/*!
|
||||
* clipboard.js v2.0.6
|
||||
* https://clipboardjs.com/
|
||||
*
|
||||
* Licensed MIT © Zeno Rocha
|
||||
*/
|
||||
Binary file not shown.
File diff suppressed because one or more lines are too long
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
object-assign
|
||||
(c) Sindre Sorhus
|
||||
@license MIT
|
||||
*/
|
||||
|
||||
/*!
|
||||
localForage -- Offline Storage, Improved
|
||||
Version 1.9.0
|
||||
https://localforage.github.io/localForage
|
||||
(c) 2013-2017 Mozilla, Apache License 2.0
|
||||
*/
|
||||
|
||||
/*!
|
||||
* vue-router v3.4.3
|
||||
* (c) 2020 Evan You
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
/*!
|
||||
Copyright (c) 2017 Jed Watson.
|
||||
Licensed under the MIT License (MIT), see
|
||||
http://jedwatson.github.io/classnames
|
||||
*/
|
||||
|
||||
/*!
|
||||
* The buffer module from node.js, for the browser.
|
||||
*
|
||||
* @author Feross Aboukhadijeh <http://feross.org>
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
/*!
|
||||
* Vue.js v2.6.12
|
||||
* (c) 2014-2020 Evan You
|
||||
* Released under the MIT License.
|
||||
*/
|
||||
|
||||
/*!
|
||||
* vue-i18n v8.21.0
|
||||
* (c) 2020 kazuya kawaguchi
|
||||
* Released under the MIT License.
|
||||
*/
|
||||
|
||||
/*!
|
||||
* vuex v3.5.1
|
||||
* (c) 2020 Evan You
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
/*! http://mths.be/fromcodepoint v0.1.0 by @mathias */
|
||||
|
||||
/*! safe-buffer. MIT License. Feross Aboukhadijeh <https://feross.org/opensource> */
|
||||
|
||||
/**
|
||||
* [js-md5]{@link https://github.com/emn178/js-md5}
|
||||
*
|
||||
* @namespace md5
|
||||
* @version 0.7.3
|
||||
* @author Chen, Yi-Cyuan [emn178@gmail.com]
|
||||
* @copyright Chen, Yi-Cyuan 2014-2017
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
//! authors : Tim Wood, Iskren Chernev, Moment.js contributors
|
||||
|
||||
//! license : MIT
|
||||
|
||||
//! moment.js
|
||||
|
||||
//! moment.js language configuration
|
||||
|
||||
//! moment.js locale configuration
|
||||
|
||||
//! momentjs.com
|
||||
|
||||
//! version : 2.27.0
|
||||
Binary file not shown.
File diff suppressed because one or more lines are too long
@@ -1,91 +0,0 @@
|
||||
/*
|
||||
object-assign
|
||||
(c) Sindre Sorhus
|
||||
@license MIT
|
||||
*/
|
||||
|
||||
/*!
|
||||
localForage -- Offline Storage, Improved
|
||||
Version 1.7.3
|
||||
https://localforage.github.io/localForage
|
||||
(c) 2013-2017 Mozilla, Apache License 2.0
|
||||
*/
|
||||
|
||||
/*!
|
||||
* vue-router v3.1.6
|
||||
* (c) 2020 Evan You
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
/*!
|
||||
Copyright (c) 2017 Jed Watson.
|
||||
Licensed under the MIT License (MIT), see
|
||||
http://jedwatson.github.io/classnames
|
||||
*/
|
||||
|
||||
/*!
|
||||
* The buffer module from node.js, for the browser.
|
||||
*
|
||||
* @author Feross Aboukhadijeh <http://feross.org>
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
/*!
|
||||
* Vue.js v2.6.11
|
||||
* (c) 2014-2019 Evan You
|
||||
* Released under the MIT License.
|
||||
*/
|
||||
|
||||
/*!
|
||||
* clipboard.js v2.0.6
|
||||
* https://clipboardjs.com/
|
||||
*
|
||||
* Licensed MIT © Zeno Rocha
|
||||
*/
|
||||
|
||||
/*!
|
||||
* html2canvas 1.0.0-rc.5 <https://html2canvas.hertzen.com>
|
||||
* Copyright (c) 2019 Niklas von Hertzen <https://hertzen.com>
|
||||
* Released under MIT License
|
||||
*/
|
||||
|
||||
/*!
|
||||
* vue-i18n v8.17.4
|
||||
* (c) 2020 kazuya kawaguchi
|
||||
* Released under the MIT License.
|
||||
*/
|
||||
|
||||
/*! *****************************************************************************
|
||||
Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||
this file except in compliance with the License. You may obtain a copy of the
|
||||
License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
|
||||
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
|
||||
MERCHANTABLITY OR NON-INFRINGEMENT.
|
||||
|
||||
See the Apache Version 2.0 License for specific language governing permissions
|
||||
and limitations under the License.
|
||||
***************************************************************************** */
|
||||
|
||||
/*! http://mths.be/fromcodepoint v0.1.0 by @mathias */
|
||||
|
||||
/*! https://mths.be/punycode v1.4.1 by @mathias */
|
||||
|
||||
/**
|
||||
* [js-md5]{@link https://github.com/emn178/js-md5}
|
||||
*
|
||||
* @namespace md5
|
||||
* @version 0.7.3
|
||||
* @author Chen, Yi-Cyuan [emn178@gmail.com]
|
||||
* @copyright Chen, Yi-Cyuan 2014-2017
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
/**
|
||||
* vuex v3.3.0
|
||||
* (c) 2020 Evan You
|
||||
* @license MIT
|
||||
*/
|
||||
Binary file not shown.
@@ -1,13 +1,14 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp3.0</TargetFramework>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
<NoWarn>$(NoWarn);1591</NoWarn>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Swashbuckle.AspNetCore.Annotations" Version="5.5.1" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore.Annotations" Version="6.1.4" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUi" Version="6.1.4" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -26,6 +26,13 @@ namespace Basic.Controllers
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
[HttpPost("form-with-user")]
|
||||
public IActionResult PostFormUser([FromForm] FormUser FormUser)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
|
||||
[HttpGet("{name}")]
|
||||
[Produces("application/octet-stream", Type = typeof(FileResult))]
|
||||
public FileResult GetFile(string name)
|
||||
@@ -47,4 +54,11 @@ namespace Basic.Controllers
|
||||
|
||||
public IFormFile File { get; set; }
|
||||
}
|
||||
|
||||
public class FormUser
|
||||
{
|
||||
public string Name { get; set; }
|
||||
|
||||
public string User { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -99,21 +99,26 @@ namespace Basic
|
||||
|
||||
app.UseSwagger(c =>
|
||||
{
|
||||
|
||||
});
|
||||
|
||||
app.UseSwaggerUI(c =>
|
||||
{
|
||||
c.RoutePrefix = "swagger"; // serve the UI at root
|
||||
c.SwaggerEndpoint("/v1/swagger.json", "V1 Docs");
|
||||
c.SwaggerEndpoint("/gp/swagger.json", "<22><>¼ģ<C2BC><C4A3>");
|
||||
});
|
||||
app.UseKnife4UI(c =>
|
||||
{
|
||||
//c.RoutePrefix = ""; // serve the UI at root
|
||||
c.SwaggerEndpoint("/v1/api-docs", "V1 Docs");
|
||||
c.SwaggerEndpoint("/gp/api-docs", "<22><>¼ģ<C2BC><C4A3>");
|
||||
c.RoutePrefix = ""; // serve the UI at root
|
||||
c.SwaggerEndpoint("/v1/swagger.json", "V1 Docs");
|
||||
c.SwaggerEndpoint("/gp/swagger.json", "<22><>¼ģ<C2BC><C4A3>");
|
||||
});
|
||||
|
||||
|
||||
app.UseEndpoints(endpoints =>
|
||||
{
|
||||
endpoints.MapControllers();
|
||||
endpoints.MapSwagger("{documentName}/api-docs");
|
||||
endpoints.MapSwagger("{documentName}/swagger.json");
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
using System;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Knife4jUIDemo.Controllers
|
||||
{
|
||||
@@ -11,7 +10,7 @@ namespace Knife4jUIDemo.Controllers
|
||||
/// 中文这是一个Get请求这是一个Get请求
|
||||
/// </summary>
|
||||
[ApiController]
|
||||
[Route("api/WeatherForecast")]
|
||||
[Route("api/WeatherForecast/[action]")]
|
||||
public class WeatherForecastController : ControllerBase
|
||||
{
|
||||
private static readonly string[] Summaries = new[]
|
||||
@@ -26,6 +25,38 @@ namespace Knife4jUIDemo.Controllers
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 得到一个ErrorCode
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[HttpGet]
|
||||
public ErrorCode GetErrorCode()
|
||||
{
|
||||
return ErrorCode.Success;
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public ErrorCode GetErrorCode2(ErrorCode errorCode)
|
||||
{
|
||||
return errorCode;
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public IActionResult GetErrorCode4(ErrorCode errorCode)
|
||||
{
|
||||
return new JsonResult(new PostErrorCodeDto() { Message="a",ErrorCode=errorCode});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 发送一个Post
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[HttpPost]
|
||||
public PostErrorCodeDto PostErrorCode([FromBody] PostErrorCodeDto PostErrorCodeDto)
|
||||
{
|
||||
return PostErrorCodeDto;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 这是一个Get请求
|
||||
/// </summary>
|
||||
@@ -43,4 +74,19 @@ namespace Knife4jUIDemo.Controllers
|
||||
.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 请求实体
|
||||
/// </summary>
|
||||
public class PostErrorCodeDto
|
||||
{
|
||||
/// <summary>
|
||||
/// 异常信息
|
||||
/// </summary>
|
||||
public string Message { get; set; }
|
||||
/// <summary>
|
||||
/// 状态码
|
||||
/// </summary>
|
||||
public ErrorCode ErrorCode { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
81
test/Knife4jUIDemo/ErrorCode.cs
Normal file
81
test/Knife4jUIDemo/ErrorCode.cs
Normal file
@@ -0,0 +1,81 @@
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace Knife4jUIDemo
|
||||
{
|
||||
/// <summary>
|
||||
/// 注释ErrorCode
|
||||
/// </summary>
|
||||
public enum ErrorCode
|
||||
{
|
||||
/// <summary>
|
||||
/// 操作成功
|
||||
/// </summary>
|
||||
Success = 0,
|
||||
/// <summary>
|
||||
/// 未知错误
|
||||
/// </summary>
|
||||
UnknownError = 1007,
|
||||
/// <summary>
|
||||
/// 服务器未知错误
|
||||
/// </summary>
|
||||
ServerUnknownError = 999,
|
||||
|
||||
/// <summary>
|
||||
/// 失败
|
||||
/// </summary>
|
||||
Error = 1000,
|
||||
|
||||
/// <summary>
|
||||
/// 认证失败
|
||||
/// </summary>
|
||||
AuthenticationFailed = 10000,
|
||||
/// <summary>
|
||||
/// 无权限
|
||||
/// </summary>
|
||||
NoPermission = 10001,
|
||||
/// <summary>
|
||||
/// 失败
|
||||
/// </summary>
|
||||
Fail = 9999,
|
||||
/// <summary>
|
||||
/// refreshToken异常
|
||||
/// </summary>
|
||||
RefreshTokenError = 10100,
|
||||
/// <summary>
|
||||
/// 资源不存在
|
||||
/// </summary>
|
||||
NotFound = 10020,
|
||||
/// <summary>
|
||||
/// 参数错误
|
||||
/// </summary>
|
||||
[Description("参数错误")]
|
||||
ParameterError = 10030,
|
||||
/// <summary>
|
||||
/// 令牌失效
|
||||
/// </summary>
|
||||
[Description("令牌失效")]
|
||||
TokenInvalidation = 10040,
|
||||
/// <summary>
|
||||
/// 令牌过期
|
||||
/// </summary>
|
||||
TokenExpired = 10050,
|
||||
/// <summary>
|
||||
/// 字段重复
|
||||
/// </summary>
|
||||
RepeatField = 10060,
|
||||
/// <summary>
|
||||
/// 禁止操作
|
||||
/// </summary>
|
||||
Inoperable = 10070,
|
||||
//10080 请求方法不允许
|
||||
|
||||
//10110 文件体积过大
|
||||
|
||||
//10120 文件数量过多
|
||||
|
||||
//10130 文件扩展名不符合规范
|
||||
|
||||
//10140 请求过于频繁,请稍后重试
|
||||
ManyRequests = 10140
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="5.5.1" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.1.4" />
|
||||
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.10.9" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -9,11 +9,113 @@
|
||||
中文这是一个Get请求这是一个Get请求
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:Knife4jUIDemo.Controllers.WeatherForecastController.GetErrorCode">
|
||||
<summary>
|
||||
得到一个ErrorCode
|
||||
</summary>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:Knife4jUIDemo.Controllers.WeatherForecastController.PostErrorCode(Knife4jUIDemo.Controllers.PostErrorCodeDto)">
|
||||
<summary>
|
||||
发送一个Post
|
||||
</summary>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:Knife4jUIDemo.Controllers.WeatherForecastController.Get">
|
||||
<summary>
|
||||
这是一个Get请求
|
||||
</summary>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="T:Knife4jUIDemo.Controllers.PostErrorCodeDto">
|
||||
<summary>
|
||||
请求实体
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:Knife4jUIDemo.Controllers.PostErrorCodeDto.Message">
|
||||
<summary>
|
||||
异常信息
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:Knife4jUIDemo.Controllers.PostErrorCodeDto.ErrorCode">
|
||||
<summary>
|
||||
状态码
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:Knife4jUIDemo.ErrorCode">
|
||||
<summary>
|
||||
注释ErrorCode
|
||||
</summary>
|
||||
</member>
|
||||
<member name="F:Knife4jUIDemo.ErrorCode.Success">
|
||||
<summary>
|
||||
操作成功
|
||||
</summary>
|
||||
</member>
|
||||
<member name="F:Knife4jUIDemo.ErrorCode.UnknownError">
|
||||
<summary>
|
||||
未知错误
|
||||
</summary>
|
||||
</member>
|
||||
<member name="F:Knife4jUIDemo.ErrorCode.ServerUnknownError">
|
||||
<summary>
|
||||
服务器未知错误
|
||||
</summary>
|
||||
</member>
|
||||
<member name="F:Knife4jUIDemo.ErrorCode.Error">
|
||||
<summary>
|
||||
失败
|
||||
</summary>
|
||||
</member>
|
||||
<member name="F:Knife4jUIDemo.ErrorCode.AuthenticationFailed">
|
||||
<summary>
|
||||
认证失败
|
||||
</summary>
|
||||
</member>
|
||||
<member name="F:Knife4jUIDemo.ErrorCode.NoPermission">
|
||||
<summary>
|
||||
无权限
|
||||
</summary>
|
||||
</member>
|
||||
<member name="F:Knife4jUIDemo.ErrorCode.Fail">
|
||||
<summary>
|
||||
失败
|
||||
</summary>
|
||||
</member>
|
||||
<member name="F:Knife4jUIDemo.ErrorCode.RefreshTokenError">
|
||||
<summary>
|
||||
refreshToken异常
|
||||
</summary>
|
||||
</member>
|
||||
<member name="F:Knife4jUIDemo.ErrorCode.NotFound">
|
||||
<summary>
|
||||
资源不存在
|
||||
</summary>
|
||||
</member>
|
||||
<member name="F:Knife4jUIDemo.ErrorCode.ParameterError">
|
||||
<summary>
|
||||
参数错误
|
||||
</summary>
|
||||
</member>
|
||||
<member name="F:Knife4jUIDemo.ErrorCode.TokenInvalidation">
|
||||
<summary>
|
||||
令牌失效
|
||||
</summary>
|
||||
</member>
|
||||
<member name="F:Knife4jUIDemo.ErrorCode.TokenExpired">
|
||||
<summary>
|
||||
令牌过期
|
||||
</summary>
|
||||
</member>
|
||||
<member name="F:Knife4jUIDemo.ErrorCode.RepeatField">
|
||||
<summary>
|
||||
字段重复
|
||||
</summary>
|
||||
</member>
|
||||
<member name="F:Knife4jUIDemo.ErrorCode.Inoperable">
|
||||
<summary>
|
||||
禁止操作
|
||||
</summary>
|
||||
</member>
|
||||
</members>
|
||||
</doc>
|
||||
|
||||
@@ -60,19 +60,20 @@ namespace Knife4jUIDemo
|
||||
|
||||
app.UseSwaggerUI(c =>
|
||||
{
|
||||
c.SwaggerEndpoint("/v1/api-docs", "LinCms");
|
||||
c.SwaggerEndpoint("v1/swagger.json", "My API V1");
|
||||
//c.SwaggerEndpoint("/v1/api-docs", "LinCms");
|
||||
});
|
||||
|
||||
app.UseKnife4UI(c =>
|
||||
{
|
||||
c.RoutePrefix = ""; // serve the UI at root
|
||||
c.SwaggerEndpoint("/v1/api-docs", "V1 Docs");
|
||||
c.SwaggerEndpoint("/v1/swagger.json", "V1 Docs");
|
||||
});
|
||||
|
||||
app.UseEndpoints(endpoints =>
|
||||
{
|
||||
endpoints.MapControllers();
|
||||
endpoints.MapSwagger("{documentName}/api-docs");
|
||||
endpoints.MapSwagger("{documentName}/swagger.json");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace NSwag.Swagger.Knife4jUI.Controllers
|
||||
{
|
||||
[ApiController]
|
||||
[Route("[controller]")]
|
||||
public class WeatherForecastController : ControllerBase
|
||||
{
|
||||
private static readonly string[] Summaries = new[]
|
||||
{
|
||||
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
|
||||
};
|
||||
|
||||
private readonly ILogger<WeatherForecastController> _logger;
|
||||
|
||||
public WeatherForecastController(ILogger<WeatherForecastController> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public IEnumerable<WeatherForecast> Get()
|
||||
{
|
||||
var rng = new Random();
|
||||
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
|
||||
{
|
||||
Date = DateTime.Now.AddDays(index),
|
||||
TemperatureC = rng.Next(-20, 55),
|
||||
Summary = Summaries[rng.Next(Summaries.Length)]
|
||||
})
|
||||
.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="NSwag.AspNetCore" Version="13.8.2" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\src\IGeekFan.AspNetCore.Knife4jUI\IGeekFan.AspNetCore.Knife4jUI.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
26
test/WebSites/NSwag.Swagger.Knife4jUI/Program.cs
Normal file
26
test/WebSites/NSwag.Swagger.Knife4jUI/Program.cs
Normal file
@@ -0,0 +1,26 @@
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace NSwag.Swagger.Knife4jUI
|
||||
{
|
||||
public class Program
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
CreateHostBuilder(args).Build().Run();
|
||||
}
|
||||
|
||||
public static IHostBuilder CreateHostBuilder(string[] args) =>
|
||||
Host.CreateDefaultBuilder(args)
|
||||
.ConfigureWebHostDefaults(webBuilder =>
|
||||
{
|
||||
webBuilder.UseStartup<Startup>();
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"$schema": "http://json.schemastore.org/launchsettings.json",
|
||||
"iisSettings": {
|
||||
"windowsAuthentication": false,
|
||||
"anonymousAuthentication": true,
|
||||
"iisExpress": {
|
||||
"applicationUrl": "http://localhost:2990",
|
||||
"sslPort": 44362
|
||||
}
|
||||
},
|
||||
"profiles": {
|
||||
"IIS Express": {
|
||||
"commandName": "IISExpress",
|
||||
"launchBrowser": true,
|
||||
"launchUrl": "swagger",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
},
|
||||
"NSwag.Swagger.Knife4jUI": {
|
||||
"commandName": "Project",
|
||||
"dotnetRunMessages": "true",
|
||||
"launchBrowser": true,
|
||||
"launchUrl": "swagger",
|
||||
"applicationUrl": "https://localhost:5001;http://localhost:5000",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
70
test/WebSites/NSwag.Swagger.Knife4jUI/Startup.cs
Normal file
70
test/WebSites/NSwag.Swagger.Knife4jUI/Startup.cs
Normal file
@@ -0,0 +1,70 @@
|
||||
using IGeekFan.AspNetCore.Knife4jUI;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.HttpsPolicy;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
// using Microsoft.OpenApi.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace NSwag.Swagger.Knife4jUI
|
||||
{
|
||||
public class Startup
|
||||
{
|
||||
public Startup(IConfiguration configuration)
|
||||
{
|
||||
Configuration = configuration;
|
||||
}
|
||||
|
||||
public IConfiguration Configuration { get; }
|
||||
|
||||
// This method gets called by the runtime. Use this method to add services to the container.
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
|
||||
services.AddControllers();
|
||||
services.AddOpenApiDocument();
|
||||
}
|
||||
|
||||
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
||||
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
|
||||
{
|
||||
if (env.IsDevelopment())
|
||||
{
|
||||
app.UseDeveloperExceptionPage();
|
||||
}
|
||||
|
||||
app.UseHttpsRedirection();
|
||||
|
||||
app.UseRouting();
|
||||
|
||||
app.UseAuthorization();
|
||||
|
||||
app.UseOpenApi(settings =>
|
||||
{
|
||||
settings.PostProcess = (document, request) =>
|
||||
{
|
||||
document.Info.Title = Configuration["Project:Name"];
|
||||
};
|
||||
});
|
||||
app.UseKnife4UI(c =>
|
||||
{
|
||||
c.RoutePrefix = ""; // serve the UI at root
|
||||
// c.RoutePrefix = "/docs"; // serve the UI at root
|
||||
c.SwaggerEndpoint("/swagger/v1/swagger.json", "V1 Docs");
|
||||
});
|
||||
// app.UseSwaggerUi3();
|
||||
|
||||
app.UseEndpoints(endpoints =>
|
||||
{
|
||||
endpoints.MapControllers();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
15
test/WebSites/NSwag.Swagger.Knife4jUI/WeatherForecast.cs
Normal file
15
test/WebSites/NSwag.Swagger.Knife4jUI/WeatherForecast.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
using System;
|
||||
|
||||
namespace NSwag.Swagger.Knife4jUI
|
||||
{
|
||||
public class WeatherForecast
|
||||
{
|
||||
public DateTime Date { get; set; }
|
||||
|
||||
public int TemperatureC { get; set; }
|
||||
|
||||
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
|
||||
|
||||
public string Summary { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft": "Warning",
|
||||
"Microsoft.Hosting.Lifetime": "Information"
|
||||
}
|
||||
}
|
||||
}
|
||||
10
test/WebSites/NSwag.Swagger.Knife4jUI/appsettings.json
Normal file
10
test/WebSites/NSwag.Swagger.Knife4jUI/appsettings.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft": "Warning",
|
||||
"Microsoft.Hosting.Lifetime": "Information"
|
||||
}
|
||||
},
|
||||
"AllowedHosts": "*"
|
||||
}
|
||||
57
test/WebSites/OAuth2Integration/AuthServer/Config.cs
Normal file
57
test/WebSites/OAuth2Integration/AuthServer/Config.cs
Normal file
@@ -0,0 +1,57 @@
|
||||
using System.Collections.Generic;
|
||||
using IdentityServer4.Models;
|
||||
using IdentityServer4.Test;
|
||||
|
||||
namespace OAuth2Integration.AuthServer
|
||||
{
|
||||
public static class Config
|
||||
{
|
||||
internal static IEnumerable<Client> Clients()
|
||||
{
|
||||
yield return new Client
|
||||
{
|
||||
ClientId = "test-id",
|
||||
ClientName = "Test client (Code with PKCE)",
|
||||
|
||||
RedirectUris = new[] {
|
||||
"http://localhost:55202/resource-server/swagger/oauth2-redirect.html", // IIS Express
|
||||
"http://localhost:5000/resource-server/swagger/oauth2-redirect.html", // Kestrel
|
||||
},
|
||||
|
||||
ClientSecrets = { new Secret("test-secret".Sha256()) },
|
||||
RequireConsent = true,
|
||||
|
||||
AllowedGrantTypes = GrantTypes.Code,
|
||||
RequirePkce = true,
|
||||
AllowedScopes = new[] { "readAccess", "writeAccess" },
|
||||
};
|
||||
}
|
||||
|
||||
internal static IEnumerable<ApiResource> ApiResources()
|
||||
{
|
||||
yield return new ApiResource
|
||||
{
|
||||
Name = "api",
|
||||
DisplayName = "API",
|
||||
Scopes = new[]
|
||||
{
|
||||
new Scope("readAccess", "Access read operations"),
|
||||
new Scope("writeAccess", "Access write operations")
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
internal static List<TestUser> TestUsers()
|
||||
{
|
||||
return new List<TestUser>
|
||||
{
|
||||
new TestUser
|
||||
{
|
||||
SubjectId = "joebloggs",
|
||||
Username = "joebloggs",
|
||||
Password = "pass123"
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Authentication.Cookies;
|
||||
using IdentityServer4;
|
||||
using IdentityServer4.Test;
|
||||
using NCaptcha.Abstractions;
|
||||
using NCaptcha.AspNetCore.Extensions;
|
||||
|
||||
namespace OAuth2Integration.AuthServer.Controllers
|
||||
{
|
||||
[ApiExplorerSettings(IgnoreApi = true)]
|
||||
[Route("account")]
|
||||
public class AccountController : Controller
|
||||
{
|
||||
private readonly TestUserStore _userStore;
|
||||
|
||||
public AccountController()
|
||||
{
|
||||
_userStore = new TestUserStore(Config.TestUsers());
|
||||
}
|
||||
|
||||
[HttpGet("login")]
|
||||
public IActionResult Login(string returnUrl)
|
||||
{
|
||||
var viewModel = new LoginViewModel { Username = "joebloggs", Password = "pass123", ReturnUrl = returnUrl };
|
||||
|
||||
return View("/AuthServer/Views/Login.cshtml", viewModel);
|
||||
}
|
||||
|
||||
|
||||
[HttpPost("login")]
|
||||
public async Task<IActionResult> Login([FromForm] LoginViewModel viewModel)
|
||||
{
|
||||
if (!_userStore.ValidateCredentials(viewModel.Username, viewModel.Password))
|
||||
{
|
||||
ModelState.AddModelError("", "Invalid username or password");
|
||||
viewModel.Password = string.Empty;
|
||||
return View("/AuthServer/Views/Login.cshtml", viewModel);
|
||||
}
|
||||
|
||||
// Use an IdentityServer-compatible ClaimsPrincipal
|
||||
var identityServerUser = new IdentityServerUser(viewModel.Username);
|
||||
identityServerUser.DisplayName = viewModel.Username;
|
||||
await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, identityServerUser.CreatePrincipal());
|
||||
|
||||
return Redirect(viewModel.ReturnUrl);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class LoginViewModel
|
||||
{
|
||||
public string ReturnUrl { get; set; }
|
||||
public string Username { get; set; }
|
||||
public string Password { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
using System.Threading.Tasks;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using IdentityServer4.Stores;
|
||||
using IdentityServer4.Services;
|
||||
using IdentityServer4.Models;
|
||||
|
||||
namespace OAuth2Integration.AuthServer.Controllers
|
||||
{
|
||||
[ApiExplorerSettings(IgnoreApi = true)]
|
||||
public class ConsentController : Controller
|
||||
{
|
||||
private readonly IIdentityServerInteractionService _interaction;
|
||||
private readonly IClientStore _clientStore;
|
||||
private readonly IResourceStore _resourceStore;
|
||||
|
||||
public ConsentController(
|
||||
IIdentityServerInteractionService interaction,
|
||||
IClientStore clientStore,
|
||||
IResourceStore resourceStore)
|
||||
{
|
||||
_interaction = interaction;
|
||||
_clientStore = clientStore;
|
||||
_resourceStore = resourceStore;
|
||||
}
|
||||
|
||||
[HttpGet("consent")]
|
||||
public async Task<IActionResult> Consent(string returnUrl)
|
||||
{
|
||||
var request = await _interaction.GetAuthorizationContextAsync(returnUrl);
|
||||
var client = await _clientStore.FindEnabledClientByIdAsync(request.ClientId);
|
||||
var resource = await _resourceStore.FindApiResourceAsync("api");
|
||||
|
||||
var viewModel = new ConsentViewModel
|
||||
{
|
||||
ReturnUrl = returnUrl,
|
||||
ClientName = client.ClientName,
|
||||
ScopesRequested = resource.Scopes.Where(s => request.ScopesRequested.Contains(s.Name))
|
||||
};
|
||||
|
||||
return View("/AuthServer/Views/Consent.cshtml", viewModel);
|
||||
}
|
||||
|
||||
[HttpPost("consent")]
|
||||
public async Task<IActionResult> Consent([FromForm]ConsentViewModel viewModel)
|
||||
{
|
||||
var request = await _interaction.GetAuthorizationContextAsync(viewModel.ReturnUrl);
|
||||
|
||||
// Communicate outcome of consent back to identityserver
|
||||
var consentResponse = new ConsentResponse
|
||||
{
|
||||
ScopesConsented = viewModel.ScopesConsented
|
||||
};
|
||||
await _interaction.GrantConsentAsync(request, consentResponse);
|
||||
|
||||
return Redirect(viewModel.ReturnUrl);
|
||||
}
|
||||
}
|
||||
|
||||
public class ConsentViewModel
|
||||
{
|
||||
public string ReturnUrl { get; set; }
|
||||
public string ClientName { get; set; }
|
||||
public IEnumerable<Scope> ScopesRequested { get; set; }
|
||||
public string[] ScopesConsented { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
@model OAuth2Integration.AuthServer.Controllers.ConsentViewModel
|
||||
|
||||
<style>
|
||||
</style>
|
||||
|
||||
<h1>Consent</h1>
|
||||
<p>
|
||||
@Model.ClientName is requesting your permission to ...
|
||||
</p>
|
||||
<form asp-controller="Consent" asp-action="Consent">
|
||||
<input type="hidden" asp-for="ReturnUrl" />
|
||||
<fieldset>
|
||||
<ul>
|
||||
@foreach (var scope in Model.ScopesRequested)
|
||||
{
|
||||
<li>
|
||||
<label>
|
||||
<input name="ScopesConsented" type="checkbox" value="@scope.Name" checked />@scope.DisplayName
|
||||
</label>
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
</fieldset>
|
||||
<button type="submit">Grant Access</button>
|
||||
</form>
|
||||
@@ -0,0 +1,23 @@
|
||||
@model OAuth2Integration.AuthServer.Controllers.LoginViewModel
|
||||
|
||||
<style>
|
||||
input {
|
||||
display: block;
|
||||
}
|
||||
</style>
|
||||
|
||||
<h1>Login</h1>
|
||||
<form asp-controller="Account" asp-action="Login">
|
||||
<input type="hidden" asp-for="ReturnUrl" />
|
||||
<fieldset>
|
||||
<label>
|
||||
Username:
|
||||
<input type="text" asp-for="Username" />
|
||||
</label>
|
||||
<label>
|
||||
Password:
|
||||
<input type="text" asp-for="Password" />
|
||||
</label>
|
||||
</fieldset>
|
||||
<button type="submit">Login</button>
|
||||
</form>
|
||||
@@ -0,0 +1 @@
|
||||
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
|
||||
19
test/WebSites/OAuth2Integration/OAuth2Integration.csproj
Normal file
19
test/WebSites/OAuth2Integration/OAuth2Integration.csproj
Normal file
@@ -0,0 +1,19 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="IdentityServer4" Version="3.0.2" />
|
||||
<PackageReference Include="IdentityServer4.AccessTokenValidation" Version="3.0.1" />
|
||||
<PackageReference Include="NCaptcha.AspNetCore.SessionImages" Version="0.1.0" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore.Swagger" Version="6.1.4" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" Version="6.1.4" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUi" Version="6.1.4" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\src\IGeekFan.AspNetCore.Knife4jUI\IGeekFan.AspNetCore.Knife4jUI.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
26
test/WebSites/OAuth2Integration/Program.cs
Normal file
26
test/WebSites/OAuth2Integration/Program.cs
Normal file
@@ -0,0 +1,26 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace OAuth2Integration
|
||||
{
|
||||
public class Program
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
CreateHostBuilder(args).Build().Run();
|
||||
}
|
||||
|
||||
public static IHostBuilder CreateHostBuilder(string[] args) =>
|
||||
Host.CreateDefaultBuilder(args)
|
||||
.ConfigureWebHostDefaults(webBuilder =>
|
||||
{
|
||||
webBuilder.UseStartup<Startup>();
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"$schema": "http://json.schemastore.org/launchsettings.json",
|
||||
"iisSettings": {
|
||||
"windowsAuthentication": false,
|
||||
"anonymousAuthentication": true,
|
||||
"iisExpress": {
|
||||
"applicationUrl": "http://localhost:55202",
|
||||
"sslPort": 0
|
||||
}
|
||||
},
|
||||
"profiles": {
|
||||
"IIS Express": {
|
||||
"commandName": "IISExpress",
|
||||
"launchBrowser": true,
|
||||
"launchUrl": "resource-server/swagger",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
},
|
||||
"OAuth2Integration": {
|
||||
"commandName": "Project",
|
||||
"launchBrowser": true,
|
||||
"launchUrl": "resource-server/swagger",
|
||||
"applicationUrl": "http://localhost:5000",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using NCaptcha.Abstractions;
|
||||
using NCaptcha.AspNetCore.Extensions;
|
||||
|
||||
namespace OAuth2Integration.ResourceServer.Controllers
|
||||
{
|
||||
[Route("captcha")]
|
||||
public class CaptchaController : Controller
|
||||
{
|
||||
private readonly ICaptchaGenerator _captchaGenerator;
|
||||
|
||||
public CaptchaController(ICaptchaGenerator captchaGenerator)
|
||||
{
|
||||
_captchaGenerator = captchaGenerator;
|
||||
}
|
||||
|
||||
[Produces("image/gif", Type = typeof(FileContentResult))]
|
||||
[HttpGet]
|
||||
public async Task<IActionResult> OnGetCaptchaAsync() => await _captchaGenerator.GetCaptchaFileResultAsync();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
|
||||
namespace OAuth2Integration.ResourceServer.Controllers
|
||||
{
|
||||
[Route("products")]
|
||||
[Authorize(AuthenticationSchemes = "Bearer")]
|
||||
public class ProductsController : Controller
|
||||
{
|
||||
[HttpGet]
|
||||
[Authorize("readAccess")]
|
||||
public IEnumerable<Product> GetProducts()
|
||||
{
|
||||
yield return new Product
|
||||
{
|
||||
Id = 1,
|
||||
SerialNo = "ABC123",
|
||||
};
|
||||
}
|
||||
|
||||
[HttpGet("{id}")]
|
||||
[Authorize("readAccess")]
|
||||
public Product GetProduct(int id)
|
||||
{
|
||||
return new Product
|
||||
{
|
||||
Id = 1,
|
||||
SerialNo = "ABC123",
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[Authorize("writeAccess")]
|
||||
public void CreateProduct([FromBody]Product product)
|
||||
{
|
||||
}
|
||||
|
||||
[HttpDelete("{id}")]
|
||||
[Authorize("writeAccess")]
|
||||
public void DeleteProduct(int id)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public class Product
|
||||
{
|
||||
public int Id { get; internal set; }
|
||||
public string SerialNo { get; set; }
|
||||
public ProductStatus Status { get; set; }
|
||||
}
|
||||
|
||||
public enum ProductStatus
|
||||
{
|
||||
InStock, ComingSoon
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.OpenApi.Models;
|
||||
using Swashbuckle.AspNetCore.SwaggerGen;
|
||||
|
||||
namespace OAuth2Integration.ResourceServer.Swagger
|
||||
{
|
||||
public class SecurityRequirementsOperationFilter : IOperationFilter
|
||||
{
|
||||
public void Apply(OpenApiOperation operation, OperationFilterContext context)
|
||||
{
|
||||
// Policy names map to scopes
|
||||
var requiredScopes = context.MethodInfo
|
||||
.GetCustomAttributes(true)
|
||||
.OfType<AuthorizeAttribute>()
|
||||
.Select(attr => attr.Policy)
|
||||
.Distinct();
|
||||
|
||||
if (requiredScopes.Any())
|
||||
{
|
||||
operation.Responses.Add("401", new OpenApiResponse { Description = "Unauthorized" });
|
||||
operation.Responses.Add("403", new OpenApiResponse { Description = "Forbidden" });
|
||||
|
||||
var oAuthScheme = new OpenApiSecurityScheme
|
||||
{
|
||||
Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "oauth2" }
|
||||
};
|
||||
|
||||
operation.Security = new List<OpenApiSecurityRequirement>
|
||||
{
|
||||
new OpenApiSecurityRequirement
|
||||
{
|
||||
[ oAuthScheme ] = requiredScopes.ToList()
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
172
test/WebSites/OAuth2Integration/Startup.cs
Normal file
172
test/WebSites/OAuth2Integration/Startup.cs
Normal file
@@ -0,0 +1,172 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Authentication.Cookies;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.OpenApi.Models;
|
||||
using NCaptcha.AspNetCore.SessionImages;
|
||||
using IGeekFan.AspNetCore.Knife4jUI;
|
||||
using Microsoft.AspNetCore.Mvc.Controllers;
|
||||
|
||||
namespace OAuth2Integration
|
||||
{
|
||||
public class Startup
|
||||
{
|
||||
public Startup(IConfiguration configuration)
|
||||
{
|
||||
Configuration = configuration;
|
||||
}
|
||||
|
||||
public IConfiguration Configuration { get; }
|
||||
|
||||
// This method gets called by the runtime. Use this method to add services to the container.
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
// Register IdentityServer services to power OAuth2.0 flows
|
||||
services.AddIdentityServer()
|
||||
.AddDeveloperSigningCredential()
|
||||
.AddInMemoryClients(AuthServer.Config.Clients())
|
||||
.AddInMemoryApiResources(AuthServer.Config.ApiResources())
|
||||
.AddTestUsers(AuthServer.Config.TestUsers());
|
||||
|
||||
// The auth setup is a little nuanced because this app provides the auth-server & the resource-server
|
||||
// Use the "Cookies" scheme by default & explicitly require "Bearer" in the resource-server controllers
|
||||
// See https://docs.microsoft.com/en-us/aspnet/core/security/authorization/limitingidentitybyscheme?tabs=aspnetcore2x
|
||||
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
|
||||
.AddCookie()
|
||||
.AddIdentityServerAuthentication(c =>
|
||||
{
|
||||
c.Authority = "http://localhost:55202/auth-server/";
|
||||
c.RequireHttpsMetadata = false;
|
||||
c.ApiName = "api";
|
||||
});
|
||||
|
||||
// Configure named auth policies that map directly to OAuth2.0 scopes
|
||||
services.AddAuthorization(c =>
|
||||
{
|
||||
c.AddPolicy("readAccess", p => p.RequireClaim("scope", "readAccess"));
|
||||
c.AddPolicy("writeAccess", p => p.RequireClaim("scope", "writeAccess"));
|
||||
});
|
||||
|
||||
services.AddControllersWithViews();
|
||||
|
||||
services.AddSession();
|
||||
services.AddSessionBasedImageCaptcha();
|
||||
|
||||
services.AddSwaggerGen(c =>
|
||||
{
|
||||
c.SwaggerDoc("v1", new OpenApiInfo { Version = "v1", Title = "Test API V1" });
|
||||
|
||||
// Define the OAuth2.0 scheme that's in use (i.e. Implicit Flow)
|
||||
c.AddSecurityDefinition("oauth2", new OpenApiSecurityScheme
|
||||
{
|
||||
Type = SecuritySchemeType.OAuth2,
|
||||
Flows = new OpenApiOAuthFlows
|
||||
{
|
||||
AuthorizationCode = new OpenApiOAuthFlow
|
||||
{
|
||||
AuthorizationUrl = new Uri("/auth-server/connect/authorize", UriKind.Relative),
|
||||
TokenUrl = new Uri("/auth-server/connect/token", UriKind.Relative),
|
||||
Scopes = new Dictionary<string, string>
|
||||
{
|
||||
{ "readAccess", "Access read operations" },
|
||||
{ "writeAccess", "Access write operations" }
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
c.CustomOperationIds(apiDesc =>
|
||||
{
|
||||
var controllerAction = apiDesc.ActionDescriptor as ControllerActionDescriptor;
|
||||
return controllerAction.ControllerName + "-" + controllerAction.ActionName;
|
||||
});
|
||||
|
||||
c.AddSecurityRequirement(new OpenApiSecurityRequirement
|
||||
{
|
||||
{
|
||||
new OpenApiSecurityScheme
|
||||
{
|
||||
Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "oauth2" }
|
||||
},
|
||||
new[] { "readAccess", "writeAccess" }
|
||||
}
|
||||
});
|
||||
|
||||
// Assign scope requirements to operations based on AuthorizeAttribute
|
||||
//c.OperationFilter<SecurityRequirementsOperationFilter>();
|
||||
});
|
||||
}
|
||||
|
||||
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
||||
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
|
||||
{
|
||||
if (env.IsDevelopment())
|
||||
{
|
||||
app.UseDeveloperExceptionPage();
|
||||
}
|
||||
|
||||
app.UseSession();
|
||||
|
||||
app.Map("/auth-server", authServer =>
|
||||
{
|
||||
authServer.UseRouting();
|
||||
|
||||
authServer.UseAuthentication();
|
||||
|
||||
authServer.UseIdentityServer();
|
||||
|
||||
authServer.UseEndpoints(endpoints =>
|
||||
{
|
||||
endpoints.MapControllers();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
app.Map("/resource-server", resourceServer =>
|
||||
{
|
||||
resourceServer.UseRouting();
|
||||
|
||||
resourceServer.UseAuthentication();
|
||||
|
||||
resourceServer.UseAuthorization();
|
||||
|
||||
resourceServer.UseEndpoints(endpoints =>
|
||||
{
|
||||
endpoints.MapControllers();
|
||||
});
|
||||
|
||||
resourceServer.UseSwagger();
|
||||
|
||||
app.UseKnife4UI(c =>
|
||||
{
|
||||
c.RoutePrefix = ""; //http://localhost:5000/index.html#/home
|
||||
c.SwaggerEndpoint("/resource-server/swagger/v1/swagger.json", "My API V1");
|
||||
c.EnableDeepLinking();
|
||||
c.OAuthClientId("test-id");
|
||||
c.OAuthClientSecret("test-secret");
|
||||
c.OAuthAppName("test-app");
|
||||
c.OAuthScopeSeparator(" ");
|
||||
c.OAuthUsePkce();
|
||||
});
|
||||
|
||||
resourceServer.UseSwaggerUI(c =>
|
||||
{
|
||||
//http://localhost:5000/resource-server/swagger/index.html
|
||||
c.SwaggerEndpoint("/resource-server/swagger/v1/swagger.json", "My API V1");
|
||||
c.EnableDeepLinking();
|
||||
|
||||
// Additional OAuth settings (See https://github.com/swagger-api/swagger-ui/blob/v3.10.0/docs/usage/oauth2.md)
|
||||
c.OAuthClientId("test-id");
|
||||
c.OAuthClientSecret("test-secret");
|
||||
c.OAuthAppName("test-app");
|
||||
c.OAuthScopeSeparator(" ");
|
||||
c.OAuthUsePkce();
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Debug",
|
||||
"System": "Information",
|
||||
"Microsoft": "Information"
|
||||
}
|
||||
}
|
||||
}
|
||||
10
test/WebSites/OAuth2Integration/appsettings.json
Normal file
10
test/WebSites/OAuth2Integration/appsettings.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft": "Warning",
|
||||
"Microsoft.Hosting.Lifetime": "Information"
|
||||
}
|
||||
},
|
||||
"AllowedHosts": "*"
|
||||
}
|
||||
1
test/WebSites/OAuth2Integration/tempkey.rsa
Normal file
1
test/WebSites/OAuth2Integration/tempkey.rsa
Normal file
@@ -0,0 +1 @@
|
||||
{"KeyId":"08433185bdf583112d2df84b15adc5fe","Parameters":{"D":"G5yB2Ha3ktEmpXH+1qIuJ/dJ8TJKFAj2j1qMLl/Zu0jI1GZJlCyeWHEM0B0IFlbkUq2W82ym9dJUaSGXC3FxSZ/JHwNzgonpv9ELQZDnAwvO0kpnJTw+SQWwDxUxItGNT8giqqm5LcSYWhv+QexepyEOwKr6Efh6hHrTsjxk/2t3xPhZJbutifcBrAaHb0wjV/mw442wXol1cjWmG5qVOj54n16kU2Pxr1i9oukZBJMYLltg8NB99KgvZSVvOoctXiPszEHel4kNdSv0R3yt2lBad+OEZykCgzgbZYmA2aIvUmB8r+yxFUiXEDuM9Wjp3gxBLdAWAdutPhtcXqPXGw==","DP":"fVcFyftVuEEFERT6UzfnIhDW/2A1ckzwutVQAYOjRwC+FyRIGyN6kzc1SSIhpuMECcibvPBp0wbyx0JsUbcUVlo6uKXaYx5h6Qcv4fIrqfFOUfVNuQ2W8mltwpDdFuquwOAEz08zMtkVavZxfghmJgYNeqqQvvuvetjAuL6Tdws=","DQ":"whD6E1yg5fe3FDcq+cVQnj3xeSWB7pYURNIYAI2Oo1y2fCfH7/TDaQbGpIr0nO/RiH2SxH1vJ5O5foKG0JES3wCe7qflJOviOzb3SNlXm3gCdvNZyAruA2k9wyro5hauDMU51saed6xAy3dL+Q5fKE+0fmKOBxVZr2RPMnglTc0=","Exponent":"AQAB","InverseQ":"tQt+mEXZQq9YpAU6Q7/8gpPBy+F3B52KmP3e5bcnvilSDIXOUjye8NMTd8WeX+NmOje+g+5aYW5kVyiXhwoVM1Bz3Iwyq51Kix3JMmaiFBPaC/r9jF/2JveiT3iKlzgi0RJcOM4rnA1pHakr3rz6nVoxpBGe8OMUTrg4cVkM4HU=","Modulus":"y6F+tcOmGz6DAYfooFDMtkeG5/7TqOpW3U3eSu4KmuoCMdc8hiJwspcwu6Yu6OGVbYKW1nZTEP2SizpTixy6W/TYFMIQZC08iBxR8mDlp2HXeGiOTMFJQFdgT+Qe0xgZ9yPdOSbBmih6dATK61k4QvDbrhAf8j7mv7JW4s5T6WCjmx0UOyo9medzP/YhCL6lnaxP3hWA61ncVK38BGHFzo/sZSyA79EXo/091CKm1V0VpZMhpZIt6wyXPg8bLyJh1edXFCqEYDXvVcfuFFhmILfB/g5CUz/YCJu2Z1VDG4OOXPtg6xJMAbcl/67yH0wu/HSNoUcLN1cJkYXAUsg1yQ==","P":"6Tl+cOLjTwUiQbTiVtC4v2uO4FcjsuKbWlqKiqJ7veMJQHdP/AOPNtLq9eKrpKfc6f0eRQHYLf//o1ntweFFBhZ3lgCAdD3gQw02meMCxSPt+BMgylFkNZXW0TfISx/a3q4XGBSLTmu0d5uN3FNxRDNmAKeL8xgvb/ikKQSHnGM=","Q":"34Qr0kjiZkXvxHY6pxfBmzwYitC/bBwxwOrLq8S30FN5G2QQ/KWc7EJnOJz3C+yX9Vep2Fslu5xYAoBjbEofVMRlvsu2T4c+d/KfUQLaualXNlQiR4TKqW13Dau+ZEDEh71NFdJwqhKzJb8ojiRMcMHD1M5AmDApDUfqAOoybuM="}}
|
||||
Reference in New Issue
Block a user