diff --git a/Configurations/SimApiDocOptions.cs b/Configurations/SimApiDocOptions.cs
new file mode 100644
index 0000000..00a1e1e
--- /dev/null
+++ b/Configurations/SimApiDocOptions.cs
@@ -0,0 +1,73 @@
+using System;
+using System.Collections.Generic;
+using Swashbuckle.AspNetCore.SwaggerUI;
+
+namespace SimApi.Configs
+{
+ ///
+ /// 文档组配置
+ ///
+ public class SimApiDocGroupOption
+ {
+ ///
+ /// 文档标识
+ ///
+ public string Id { get; set; }
+
+ ///
+ /// 文档名称
+ ///
+ public string Name { get; set; }
+
+ ///
+ /// 文档描述
+ ///
+ public string Description { get; set; }
+ }
+
+ ///
+ /// 授权配置, Type支持 "SimApiAuth","ClientCredentials","Implicit","AuthorizationCode"
+ ///
+ public class SimApiAuthOption
+ {
+ public string[] Type { get; set; } = new[] {"SimApiAuth"};
+ public string Description { get; set; } = "认证服务器颁发的AccessToken";
+ public string AuthorizationUrl { get; set; }
+ public string TokenUrl { get; set; }
+ public Dictionary Scopes { get; set; }
+ }
+
+ ///
+ /// 文档配置
+ ///
+ public class SimApiDocOptions
+ {
+ ///
+ /// 文档组配置
+ ///
+ public SimApiDocGroupOption[] ApiGroups { get; set; } = new[]
+ {
+ new SimApiDocGroupOption
+ {
+ Id = "api",
+ Name = "Api",
+ Description = "Api接口文档"
+ }
+ };
+
+ ///
+ /// 授权配置
+ ///
+ public SimApiAuthOption ApiAuth { get; set; } = new SimApiAuthOption();
+
+ ///
+ /// 文档页面标题
+ ///
+ public string DocumentTitle { get; set; } = "API接口文档";
+
+ ///
+ /// 接口支持的调用方式
+ ///
+ public SubmitMethod[] SupportedMethod { get; set; } = new[] {SubmitMethod.Post};
+ }
+}
\ No newline at end of file
diff --git a/Configurations/SimApiOptions.cs b/Configurations/SimApiOptions.cs
new file mode 100644
index 0000000..67b2a69
--- /dev/null
+++ b/Configurations/SimApiOptions.cs
@@ -0,0 +1,27 @@
+using System;
+
+namespace SimApi.Configs
+{
+ public class SimApiOptions
+ {
+ public bool EnableSimApiAuth { get; set; } = false;
+ public bool EnableSimApiDoc { get; set; } = true;
+ public bool EnableSimApiException { get; set; } = true;
+ public bool EnableSimApiStorage { get; set; } = false;
+ public bool EnableForwardHeaders { get; set; } = true;
+ public bool EnableLowerUrl { get; set; } = true;
+ public SimApiDocOptions SimApiDocOptions { get; set; } = new SimApiDocOptions();
+ public SimApiStorageOptions SimApiStorageOptions { get; set; } = new SimApiStorageOptions();
+
+
+ public void ConfigureSimApiDoc(Action options = null)
+ {
+ options?.Invoke(SimApiDocOptions);
+ }
+
+ public void ConfigureSimApiStorage(Action options = null)
+ {
+ options?.Invoke(SimApiStorageOptions);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Configurations/SimApiStorageOptions.cs b/Configurations/SimApiStorageOptions.cs
new file mode 100644
index 0000000..fdd3110
--- /dev/null
+++ b/Configurations/SimApiStorageOptions.cs
@@ -0,0 +1,11 @@
+namespace SimApi.Configs
+{
+ public class SimApiStorageOptions
+ {
+ public string Endpoint { get; set; }
+ public string ServeUrl { get; set; }
+ public string Bucket { get; set; }
+ public string AccessKey { get; set; }
+ public string SecretKey { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/Helpers/SimApiStorage.cs b/Helpers/SimApiStorage.cs
new file mode 100644
index 0000000..c63b091
--- /dev/null
+++ b/Helpers/SimApiStorage.cs
@@ -0,0 +1,81 @@
+using System;
+using System.IO;
+using Microsoft.AspNetCore.Http;
+using Minio;
+using Minio.Exceptions;
+using SimApi.Configs;
+
+namespace SimApi.Helpers
+{
+ public class SimApiStorage
+ {
+ private MinioClient Mc { get; }
+ private string ServeUrl { get; }
+ private string Bucket { get; }
+ private IHttpContextAccessor HttpContextAccessor { get; }
+
+ public SimApiStorage(SimApiOptions apiOptions, IHttpContextAccessor httpContextAccessor)
+ {
+ var options = apiOptions.SimApiStorageOptions;
+ HttpContextAccessor = httpContextAccessor;
+ var useSsl = false;
+ var endpoint = string.Empty;
+ if (options.Endpoint.StartsWith("http://"))
+ {
+ endpoint = options.Endpoint.Replace("http://", string.Empty);
+ }
+ else if (options.Endpoint.StartsWith("https://"))
+ {
+ endpoint = options.Endpoint.Replace("https://", string.Empty);
+ useSsl = true;
+ }
+ else
+ {
+ throw new Exception("SimApiStorage: Error Endpoint");
+ }
+
+ ServeUrl = options.ServeUrl;
+ Bucket = options.Bucket;
+ if (useSsl)
+ {
+ Mc = new MinioClient(endpoint, options.AccessKey, options.SecretKey).WithSSL();
+ }
+ else
+ {
+ Mc = new MinioClient(endpoint, options.AccessKey, options.SecretKey);
+ }
+
+ bool found = Mc.BucketExistsAsync(Bucket).Result;
+ if (!found)
+ {
+ Mc.MakeBucketAsync(Bucket).Wait();
+ }
+ }
+
+ public string UploadFile(string path, Stream stream)
+ {
+ try
+ {
+ Mc.PutObjectAsync(Bucket, path, stream, stream.Length).Wait();
+ return null;
+ }
+ catch (MinioException e)
+ {
+ Console.WriteLine("Error occurred: " + e);
+ return e.Message;
+ }
+ }
+
+ public string FullUrl(string path)
+ {
+ var httpRequest = HttpContextAccessor.HttpContext.Request;
+ var url = $"{httpRequest.Scheme}://{httpRequest.Host}";
+ if (string.IsNullOrEmpty(path))
+ {
+ return path;
+ }
+
+ return path.StartsWith("~") ? url + path.Substring(1, path.Length - 1) : $"{ServeUrl}{path}";
+ }
+ }
+}
\ No newline at end of file
diff --git a/Helpers/SimApiUpload.cs b/Helpers/SimApiUpload.cs
deleted file mode 100644
index 92f12eb..0000000
--- a/Helpers/SimApiUpload.cs
+++ /dev/null
@@ -1,103 +0,0 @@
-using System;
-using System.IO;
-using Microsoft.AspNetCore.Hosting;
-
-namespace SimApi.Helpers
-{
- public class SimApiUpload
- {
- private string FilePathFolder = "/uploads/";
- public string FilePath
- {
- set
- {
- if (!value.StartsWith("/"))
- {
- FilePathFolder = $"/{value}";
- }
- if (!FilePathFolder.EndsWith("/"))
- {
- FilePathFolder += "/";
- }
- }
- get
- {
- return FilePathFolder;
- }
- }
-
- private IWebHostEnvironment Env { get; }
-
- public SimApiUpload(IWebHostEnvironment env)
- {
- Env = env;
- }
-
- public class SimApiUploadInfo
- {
- public string Path { get; set; }
- public int Size { get; set; }
- }
-
- ///
- /// 保存base64文件,如果有标头按照标头自动识别
- ///
- ///
- /// 扩展名
- /// 存放路径
- /// 文件名
- ///
- public SimApiUploadInfo SaveFile(string base64, string ext = null, string path = null, string fileName = null)
- {
- byte[] bt;
- var filePath = FilePath + path;
- if (fileName == null)
- {
- fileName = Guid.NewGuid().ToString();
- }
- var baseArr = base64.Split(";");
- if (baseArr.Length == 2)
- {
- var info = baseArr[0].Split(":")[1].Split("/");
- if (path == null)
- {
- filePath += $"{info[0]}/";
- }
- if (ext == null)
- {
- ext = info[1];
- }
- var data = baseArr[1].Split(",");
- bt = Convert.FromBase64String(data[1]);
- }
- else
- {
- bt = Convert.FromBase64String(base64);
- }
- var fn = fileName + (string.IsNullOrEmpty(ext) ? string.Empty : $".{ext}");
- return WriteFile(filePath, fn, bt);
- }
-
- ///
- /// 把数据写入文件
- ///
- ///
- ///
- ///
- ///
- public SimApiUploadInfo WriteFile(string filePath, string fileName, byte[] data)
- {
- var realPath = Directory.GetCurrentDirectory() + "/wwwroot" + filePath;
- if (!Directory.Exists(realPath))
- {
- Directory.CreateDirectory(realPath);
- }
- File.WriteAllBytes(realPath + fileName, data);
- return new SimApiUploadInfo
- {
- Path = filePath + fileName,
- Size = data.Length
- };
- }
- }
-}
diff --git a/README.md b/README.md
index 4e58da0..655ac47 100644
--- a/README.md
+++ b/README.md
@@ -23,43 +23,33 @@ namespace Controllers
}
```
-2. 基于Swagger的API文档服务
+统一配置,现在只需要进行AddSimApi的配置, Configure中直接 UseSimApi即可。
```C#
-#Startup.cs
-
- services.AddSimApiDoc("文档名称", "文档描述");
-
- app.UseSimApiDoc("名称",SubmitMethod[])
-
-#控制器中可以直接使用特性
-
- [SimApiDoc("分组","名称")]
+services.AddSimApi(options =>
+{
+ options.ConfigureSimApiDoc(options =>
+ {
+ options.ApiGroups = new[]
+ {
+ new SimApiDocGroupOption
+ {Id = "admin", Name = "后台管理接口", Description = "本接口调用需要Scope:sac.api.admin"},
+ new SimApiDocGroupOption
+ {Id = "user-v1", Name = "用户中心接口", Description = "本接口调用需要Scope:sac.api.user"}
+ };
+ options.ApiAuth = new SimApiAuthOption
+ {
+ Type = new[] {"ClientCredentials", "Implicit", "AuthorizationCode"},
+ Scopes = new Dictionary
+ {
+ {"sac.api.user", "用户信息接口权限"},
+ {"sac.api.admin", "后台管理API"}
+ },
+ AuthorizationUrl = "/connect/authorize",
+ TokenUrl = "/connect/token"
+ };
+ });
+ options.EnableSimApiStorage = true;
+ options.SimApiStorageOptions = Configuration.GetSection("S3").Get();
+});
```
-
-3. 简单的基于Redis的登录TOKEN服务
-
-```C#
-#Startup.cs
-service.AddSimApiAuth();
-```
-添加时候, 可以从DI中获取 Auth 类,调用 Auth.Set(int,string) 将用户ID/类型和生成的Token绑定,本方法返回缓存中的Key名称
-
-```C#
-#Startup.cs
-app.UseSimApiAuth();
-```
-调用本中间件,然后再需要登录认证的地方,使用 [YYAuth] 特性,即可完成检测登录相关的操作,
-如果需要获取用户的ID, 只需要 直接使用 LoginId 属性即可获取
-
-4. 统一返回
-所有Response 均需要继承 SimApiBaseResponse类
-
-
-5. 异常处理
-本异常中间件封装了API错误的异常处理,控制器可以使用 Error(int,string) 直接返回API错误
-
-```C#
-#Startup.cs
-app.UseSimApiException();
-```
\ No newline at end of file
diff --git a/SimApi.csproj b/SimApi.csproj
index 2a4120d..45cb0c1 100644
--- a/SimApi.csproj
+++ b/SimApi.csproj
@@ -25,10 +25,12 @@
+
-
-
+
+
+
diff --git a/SimApiExtensions.cs b/SimApiExtensions.cs
index a3552b6..c67e3d8 100644
--- a/SimApiExtensions.cs
+++ b/SimApiExtensions.cs
@@ -3,188 +3,190 @@ using SimApi.Helpers;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.OpenApi.Models;
-using Swashbuckle.AspNetCore.SwaggerUI;
using SimApi.Middlewares;
using Microsoft.AspNetCore.HttpOverrides;
+using SimApi.Configs;
namespace SimApi
{
///
/// 加入系统的扩展信息
///
- public static class Extensions
+ public static class SimApiExtensions
{
- public static string DocumentTitle = "";
- public static string DocumentDescription = "";
-
//**********快捷添加**************
-
- ///
- /// 添加整个SimAPI,同时增加CORS规则
- ///
- ///
- ///
- ///
- ///
- public static IServiceCollection AddSimApi(this IServiceCollection builder, string title,
- string description = null)
+ public static IServiceCollection AddSimApi(this IServiceCollection builder,
+ Action options = null)
{
- return builder.AddSimApiAuth().AddSimApiDoc(title, description).AddCors().AddSimApiUpload().Configure(options =>
+ var simApiOptions = new SimApiOptions();
+ options?.Invoke(simApiOptions);
+
+ // 是否使用SIMAUTH
+ if (simApiOptions.EnableSimApiAuth)
{
- options.ForwardedHeaders = ForwardedHeaders.XForwardedFor |
- ForwardedHeaders.XForwardedProto;
- // Only loopback proxies are allowed by default.
- // Clear that restriction because forwarders are enabled by explicit
- // configuration.
- options.KnownNetworks.Clear();
- options.KnownProxies.Clear();
- }); ;
+ builder.AddScoped();
+ }
+
+ // 使用SimApiDoc
+ if (simApiOptions.EnableSimApiDoc)
+ {
+ var docOptions = simApiOptions.SimApiDocOptions;
+ builder.AddSwaggerGen(x =>
+ {
+ foreach (var group in docOptions.ApiGroups)
+ {
+ x.SwaggerDoc(group.Id, new OpenApiInfo {Title = group.Name, Description = group.Description});
+ }
+
+ x.EnableAnnotations();
+ var haveOauth = false;
+ var haveSimApiAuth = false;
+ var oauthFlows = new OpenApiOAuthFlows();
+ foreach (var auth in docOptions.ApiAuth.Type)
+ {
+ switch (auth)
+ {
+ case "SimApiAuth":
+ x.AddSecurityRequirement(new OpenApiSecurityRequirement
+ {
+ {
+ new OpenApiSecurityScheme
+ {
+ Reference = new OpenApiReference
+ {Type = ReferenceType.SecurityScheme, Id = "HeaderToken"}
+ },
+ new[] {"readAccess", "writeAccess"}
+ }
+ });
+ haveSimApiAuth = true;
+ break;
+ case "ClientCredentials":
+ oauthFlows.ClientCredentials = new OpenApiOAuthFlow
+ {
+ TokenUrl = new Uri(docOptions.ApiAuth.TokenUrl, UriKind.RelativeOrAbsolute),
+ Scopes = docOptions.ApiAuth.Scopes
+ };
+ haveOauth = true;
+ break;
+ case "Implicit":
+ oauthFlows.Implicit = new OpenApiOAuthFlow
+ {
+ AuthorizationUrl = new Uri(docOptions.ApiAuth.AuthorizationUrl,
+ UriKind.RelativeOrAbsolute),
+ Scopes = docOptions.ApiAuth.Scopes
+ };
+ haveOauth = true;
+ break;
+ case "AuthorizationCode":
+ oauthFlows.AuthorizationCode = new OpenApiOAuthFlow
+ {
+ TokenUrl = new Uri(docOptions.ApiAuth.TokenUrl, UriKind.RelativeOrAbsolute),
+ AuthorizationUrl = new Uri(docOptions.ApiAuth.AuthorizationUrl,
+ UriKind.RelativeOrAbsolute),
+ Scopes = docOptions.ApiAuth.Scopes
+ };
+ haveOauth = true;
+ break;
+ }
+ }
+
+ if (haveOauth)
+ {
+ x.AddSecurityDefinition("oauth2", new OpenApiSecurityScheme
+ {
+ Type = SecuritySchemeType.OAuth2,
+ Flows = oauthFlows,
+ Description = docOptions.ApiAuth.Description,
+ In = ParameterLocation.Header
+ });
+ x.AddSecurityRequirement(new OpenApiSecurityRequirement
+ {
+ {
+ new OpenApiSecurityScheme
+ {
+ Reference = new OpenApiReference
+ {Type = ReferenceType.SecurityScheme, Id = "oauth2"}
+ },
+ new[] {"SimApiAuth"}
+ }
+ });
+ }
+
+ if (haveSimApiAuth)
+ {
+ x.AddSecurityDefinition("HeaderToken",
+ new OpenApiSecurityScheme {Name = "Token", In = ParameterLocation.Header});
+ }
+ });
+ }
+
+ // 使用Header转发,应对代理后获取真实ip
+ if (simApiOptions.EnableForwardHeaders)
+ {
+ builder.Configure(options =>
+ {
+ options.ForwardedHeaders = ForwardedHeaders.XForwardedFor |
+ ForwardedHeaders.XForwardedProto;
+ options.KnownNetworks.Clear();
+ options.KnownProxies.Clear();
+ });
+ }
+
+ if (simApiOptions.EnableLowerUrl)
+ {
+ builder.AddRouting(options => options.LowercaseUrls = true);
+ }
+
+ if (simApiOptions.EnableSimApiStorage)
+ {
+ builder.AddSingleton();
+ }
+
+ builder.AddSingleton(simApiOptions);
+ return builder;
}
///
/// 使用所有SimApi自定义中间件
///
///
- ///
- ///
- ///
///
- public static IApplicationBuilder UseSimApi(this IApplicationBuilder builder, params SubmitMethod[] submitMethods)
+ public static IApplicationBuilder UseSimApi(this IApplicationBuilder builder)
{
- return builder.UseSimApiException().UseSimApiDoc(submitMethods).UseMiddleware().UseCors(x => x.AllowAnyHeader().AllowAnyMethod().AllowAnyOrigin()).UseSimApiUpload().UseForwardedHeaders();
- }
-
-
-
- //*********组件快捷方式*************
-
- ///
- /// 添加自定义授权认证中间价
- ///
- ///
- ///
- public static IServiceCollection AddSimApiAuth(this IServiceCollection builder)
- {
- return builder.AddScoped();
- }
-
- ///
- /// 添加Base64上传组件
- ///
- ///
- ///
- public static IServiceCollection AddSimApiUpload(this IServiceCollection builder)
- {
- return builder.AddSingleton();
- }
-
- ///
- /// 添加Api文档服务
- ///
- ///
- /// 文档标题
- /// 文档描述
- ///
- public static IServiceCollection AddSimApiDoc(this IServiceCollection builder, string title,
- string description = null)
- {
- DocumentTitle = title;
- DocumentDescription = description;
- return builder.AddSwaggerGen(x =>
+ var options = builder.ApplicationServices.GetService();
+ if (options.EnableSimApiAuth)
{
- x.SwaggerDoc("api", new OpenApiInfo { Title = DocumentTitle, Description = DocumentDescription });
- x.EnableAnnotations();
- x.AddSecurityRequirement(new OpenApiSecurityRequirement
+ builder.UseMiddleware();
+ }
+
+ if (options.EnableSimApiDoc)
+ {
+ var docOptions = options.SimApiDocOptions;
+ return builder.UseSwagger(x => x.RouteTemplate = "/swagger/{documentName}.json").UseSwaggerUI(x =>
{
+ x.DocumentTitle = docOptions.DocumentTitle;
+ foreach (var group in docOptions.ApiGroups)
{
- new OpenApiSecurityScheme
- {
- Reference = new OpenApiReference {Type = ReferenceType.SecurityScheme, Id = "HeaderToken"}
- },
- new[] {"readAccess", "writeAccess"}
+ x.SwaggerEndpoint($"/swagger/{group.Id}.json", name: group.Name);
}
+
+ x.EnableValidator();
+ x.SupportedSubmitMethods(docOptions.SupportedMethod);
+ x.DisplayRequestDuration();
});
- x.AddSecurityDefinition("HeaderToken",
- new OpenApiSecurityScheme { Name = "Token", In = ParameterLocation.Header });
- });
- }
+ }
-
- ///
- /// 使用异常中间价扩展
- ///
- ///
- ///
- public static IApplicationBuilder UseSimApiException(this IApplicationBuilder builder)
- {
- return builder.UseMiddleware();
- }
-
- ///
- /// 配置上传组件必须
- ///
- ///
- ///
- public static IApplicationBuilder UseSimApiUpload(this IApplicationBuilder builder)
- {
- return builder.UseStaticFiles(new StaticFileOptions
+ if (options.EnableSimApiException)
{
- DefaultContentType = "application/x-msdownload",
- ServeUnknownFileTypes = true
- });
- }
+ builder.UseMiddleware();
+ }
- ///
- /// 使用文档UI
- ///
- ///
- /// 文档标题
- /// 文档支持的提交方式(如果不指定,默认使用POST)
- ///
- public static IApplicationBuilder UseSimApiDoc(this IApplicationBuilder builder, params SubmitMethod[] submitMethods)
- {
- return builder.UseSwagger(x => x.RouteTemplate = "docs/{documentName}.json").UseSwaggerUI(x =>
+ if (options.EnableForwardHeaders)
{
- x.RoutePrefix = "docs";
- x.DocumentTitle = DocumentTitle;
- x.SwaggerEndpoint("/docs/api.json", name: DocumentTitle);
- x.EnableValidator();
- if (submitMethods.Length > 0)
- {
- x.SupportedSubmitMethods(submitMethods);
- }
- else
- {
- x.SupportedSubmitMethods(SubmitMethod.Post);
- }
+ builder.UseForwardedHeaders();
+ }
- x.DisplayRequestDuration();
- });
+ return builder;
}
-
-
- ///
- /// 同时使用Api文档和异常处理中间件
- ///
- ///
- /// 文档标题
- /// API提交方式定义
- ///
- public static IApplicationBuilder UseSimApiDocEx(this IApplicationBuilder builder, params SubmitMethod[] submitMethods)
- {
- return builder.UseSimApiException().UseSimApiDoc(submitMethods);
- }
-
- ///
- /// 使用自定义认证中间件(依赖IDistributedCache)
- ///
- ///
- ///
- public static IApplicationBuilder UseSimApiAuth(this IApplicationBuilder builder)
- {
- return builder.UseMiddleware();
- }
-
-
}
}
\ No newline at end of file