455 lines
17 KiB
C#
455 lines
17 KiB
C#
|
using Autofac;
|
|||
|
using Autofac.Extensions.DependencyInjection;
|
|||
|
using Microsoft.AspNetCore.Builder;
|
|||
|
using Microsoft.AspNetCore.Hosting;
|
|||
|
using Microsoft.AspNetCore.Mvc;
|
|||
|
using Microsoft.AspNetCore.Mvc.Controllers;
|
|||
|
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
|||
|
using Microsoft.Extensions.DependencyInjection;
|
|||
|
using Microsoft.Extensions.Hosting;
|
|||
|
using Microsoft.Extensions.Logging;
|
|||
|
using Microsoft.OpenApi.Models;
|
|||
|
using System;
|
|||
|
using System.IO;
|
|||
|
using System.Text;
|
|||
|
using System.Threading.Tasks;
|
|||
|
using NPlatform.Infrastructure.Config;
|
|||
|
using NPlatform.Middleware;
|
|||
|
using NPlatform.API;
|
|||
|
using NPlatform.DI;
|
|||
|
using NPlatform.Repositories;
|
|||
|
using Newtonsoft.Json;
|
|||
|
using System.Text.Json.Serialization;
|
|||
|
using SqlSugar;
|
|||
|
using IGeekFan.AspNetCore.Knife4jUI;
|
|||
|
using Microsoft.AspNetCore.Authentication;
|
|||
|
using Microsoft.AspNetCore.Authentication.Cookies;
|
|||
|
using IdentityServer4.Configuration;
|
|||
|
using System.Security.Cryptography.X509Certificates;
|
|||
|
using BZPT.Repositories;
|
|||
|
using Microsoft.Extensions.DependencyInjection.Extensions;
|
|||
|
using NPlatform.Infrastructure.Config.Section;
|
|||
|
using ServiceStack;
|
|||
|
using Microsoft.IdentityModel.Tokens;
|
|||
|
using Hangfire;
|
|||
|
using Hangfire.Redis.StackExchange;
|
|||
|
using Hangfire.Dashboard.BasicAuthorization;
|
|||
|
using BZPT.Domains.Application;
|
|||
|
using System.Security.Claims;
|
|||
|
using NPlatform.Infrastructure.IdGenerators;
|
|||
|
using NPOI.SS.Formula.Functions;
|
|||
|
using Microsoft.AspNetCore.DataProtection;
|
|||
|
using StackExchange.Redis;
|
|||
|
using BZPT.Api.Middleware;
|
|||
|
using Microsoft.AspNetCore.Server.Kestrel.Core;
|
|||
|
using Newtonsoft.Json.Linq;
|
|||
|
using Serilog.Context;
|
|||
|
using Serilog;
|
|||
|
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
|||
|
using Microsoft.Extensions.FileProviders;
|
|||
|
using BZPT.Domains.IRepositories;
|
|||
|
using BZPT.Domains.IService.Sys;
|
|||
|
using BZPT.Api;
|
|||
|
using Microsoft.AspNetCore.Http.Features;
|
|||
|
using Microsoft.AspNetCore.HttpOverrides;
|
|||
|
|
|||
|
var builder = WebApplication.CreateBuilder(args);
|
|||
|
var serviceConfig = builder.Configuration.GetServiceConfig();
|
|||
|
|
|||
|
builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory());
|
|||
|
// 替换控制器激活器
|
|||
|
builder.Services.Replace(ServiceDescriptor.Scoped<IControllerActivator, ServiceBasedControllerActivator>());
|
|||
|
|
|||
|
// 健康检查
|
|||
|
builder.Services.AddHealthChecks().AddCheck<NHealthChecks>(serviceConfig.ServiceName);
|
|||
|
// 内存缓存
|
|||
|
builder.Services.AddMemoryCache();
|
|||
|
// Autofac 配置
|
|||
|
builder.Services.AddAutofac();
|
|||
|
var redisConfig = builder.Configuration.GetRedisConfig();
|
|||
|
//从配置获取 Redis 连接字符串
|
|||
|
var redisConn = $"{redisConfig.Connections?.FirstOrDefault()},password={redisConfig.Password}";
|
|||
|
Console.WriteLine("redis 连接:" + redisConn);
|
|||
|
// 配置 Hangfire
|
|||
|
builder.Services.AddHangfire(x =>
|
|||
|
{
|
|||
|
x.SetDataCompatibilityLevel(CompatibilityLevel.Version_170)
|
|||
|
.UseSimpleAssemblyNameTypeSerializer()
|
|||
|
.UseRecommendedSerializerSettings()
|
|||
|
.UseRedisStorage(redisConn, new RedisStorageOptions
|
|||
|
{
|
|||
|
Db = 11,
|
|||
|
Prefix = "Hangfire:"
|
|||
|
});
|
|||
|
});
|
|||
|
|
|||
|
builder.Services.AddHangfireServer();
|
|||
|
|
|||
|
// 连接到 Redis
|
|||
|
var connectionMultiplexer = ConnectionMultiplexer.Connect(redisConn);
|
|||
|
// 注册 IConnectionMultiplexer
|
|||
|
builder.Services.AddSingleton<IConnectionMultiplexer>(connectionMultiplexer);
|
|||
|
|
|||
|
// 注册 IDatabase
|
|||
|
builder.Services.AddScoped<IDatabase>(provider =>
|
|||
|
{
|
|||
|
var connection = provider.GetRequiredService<IConnectionMultiplexer>();
|
|||
|
return connection.GetDatabase(); // 返回 IDatabase 实例
|
|||
|
});
|
|||
|
// 主机配置
|
|||
|
var repositoryOptions = new RepositoryOptions();
|
|||
|
builder.Host.Configure(builder.Configuration, repositoryOptions);
|
|||
|
|
|||
|
// 数据库上下文配置
|
|||
|
var dbHost = builder.Configuration["DB_HOST"];
|
|||
|
var dbPort = builder.Configuration["DB_PORT"];
|
|||
|
var dbName = builder.Configuration["DB_NAME"];
|
|||
|
var logDbName = builder.Configuration["LOG_DB_NAME"];
|
|||
|
var dbUser = builder.Configuration["DB_USER"];
|
|||
|
var dbPass = builder.Configuration["DB_PASSWORD"];
|
|||
|
|
|||
|
builder.UseMySerilog($"Server={dbHost};Port={dbPort};DATABASE={logDbName};User Id={dbUser};PWD={dbPass};", serviceConfig.ServiceName);
|
|||
|
|
|||
|
//// 配置日志
|
|||
|
//var log = builder.Logging.AddLog4Net();
|
|||
|
//log.AddConsole();
|
|||
|
//log.AddDebug();
|
|||
|
//log.SetMinimumLevel(LogLevel.Debug);
|
|||
|
|
|||
|
builder.Services.AddScoped<DBContext>(serviceProvider =>
|
|||
|
{
|
|||
|
return new DBContext($"Server={dbHost};Port={dbPort};DATABASE={dbName};User Id={dbUser};PWD={dbPass};", DbType.Dm, ConfigId: "default");
|
|||
|
});
|
|||
|
builder.Services.AddScoped<IUnitOfWorkSugar, UnitOfWorkSugar>();
|
|||
|
// 控制器配置
|
|||
|
builder.Services.AddControllersWithViews(mvcOptions =>
|
|||
|
{
|
|||
|
mvcOptions.Filters.Remove(mvcOptions.Filters.OfType<UnsupportedContentTypeFilter>().FirstOrDefault());
|
|||
|
}).AddJsonOptions(options =>
|
|||
|
{
|
|||
|
//options.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
|
|||
|
//options.JsonSerializerOptions.PropertyNameCaseInsensitive = false; // 确保区分大小写
|
|||
|
options.JsonSerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull;
|
|||
|
options.JsonSerializerOptions.Converters.Add(new DateTimeConverter("yyyy-MM-dd HH:mm:ss"));
|
|||
|
});
|
|||
|
|
|||
|
|
|||
|
// 从 appsettings.json 加载表单限制
|
|||
|
builder.Services.Configure<FormOptions>(options =>
|
|||
|
{
|
|||
|
options.MultipartBodyLengthLimit =
|
|||
|
builder.Configuration.GetValue<long>("FormOptions:MultipartBodyLengthLimit");
|
|||
|
});
|
|||
|
|
|||
|
|
|||
|
// API 行为配置
|
|||
|
builder.Services.Configure<ApiBehaviorOptions>(options =>
|
|||
|
{
|
|||
|
options.InvalidModelStateResponseFactory = actionContext =>
|
|||
|
{
|
|||
|
var errors = actionContext.ModelState?.Where(e => e.Value.Errors.Count > 0);
|
|||
|
var strError = new StringBuilder();
|
|||
|
foreach (var error in errors)
|
|||
|
{
|
|||
|
var msg = error.Value.Errors.FirstOrDefault()?.ErrorMessage;
|
|||
|
if (!string.IsNullOrWhiteSpace(msg))
|
|||
|
strError.Append(msg);
|
|||
|
}
|
|||
|
return new FailResult<object>("错误:" + strError.ToString());
|
|||
|
};
|
|||
|
});
|
|||
|
|
|||
|
// IdentityServer4 配置
|
|||
|
var serviceProvider = builder.Services.BuildServiceProvider();
|
|||
|
var dbContext = serviceProvider.GetService<DBContext>();
|
|||
|
|
|||
|
var keyPath = Path.Combine(Environment.CurrentDirectory, builder.Configuration["CERT_PATH"] ?? "");
|
|||
|
Console.WriteLine($"Certificate path: {keyPath}, Exists: {File.Exists(keyPath)}");
|
|||
|
|
|||
|
// CORS 配置
|
|||
|
|
|||
|
builder.Services.AddCors(options => options.AddPolicy("CorsMy", policy =>
|
|||
|
{
|
|||
|
// 允许特定的源访问
|
|||
|
policy.WithOrigins("*")
|
|||
|
.AllowAnyHeader()
|
|||
|
.AllowAnyMethod();
|
|||
|
|
|||
|
}));
|
|||
|
|
|||
|
if (!string.IsNullOrEmpty(keyPath) && File.Exists(keyPath))
|
|||
|
{
|
|||
|
builder.WebHost.ConfigureKestrel(serverOptions =>
|
|||
|
{
|
|||
|
serverOptions.ConfigureHttpsDefaults(httpsOptions =>
|
|||
|
{
|
|||
|
httpsOptions.ServerCertificate = new X509Certificate2(keyPath, builder.Configuration["CERT_PASSWORD"]);
|
|||
|
});
|
|||
|
});
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
Console.WriteLine("⚠️ 证书未找到或环境变量未设置,HTTPS 可能无法正常工作!");
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
Console.WriteLine($"AuthorityServer 配置: {builder.Configuration["AuthorityServer"]}");
|
|||
|
// 配置 Cookie 认证
|
|||
|
builder.Services.AddAuthentication(options =>
|
|||
|
{
|
|||
|
options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
|
|||
|
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
|
|||
|
options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
|
|||
|
options.DefaultSignOutScheme = CookieAuthenticationDefaults.AuthenticationScheme;
|
|||
|
})
|
|||
|
.AddJwtBearer(options =>
|
|||
|
{
|
|||
|
options.Authority = builder.Configuration["AuthorityServer"];
|
|||
|
options.Audience = serviceConfig.ServiceID; // 受众
|
|||
|
#if DEBUG
|
|||
|
options.RequireHttpsMetadata = false; // 开发环境允许 HTTP
|
|||
|
#else
|
|||
|
options.RequireHttpsMetadata = true; // 生产环境强制 HTTPS
|
|||
|
#endif
|
|||
|
options.TokenValidationParameters = new TokenValidationParameters
|
|||
|
{
|
|||
|
ValidateAudience = true, // 验证 Audience
|
|||
|
ValidAudiences =new string[] { serviceConfig.ServiceID }, // 或者 "api.BZPT",取决于你的 API 需要验证哪个 Audience
|
|||
|
ValidateIssuer = true, // 验证 Issuer
|
|||
|
ValidIssuer = builder.Configuration["AuthorityServer"], // 确保与 IdentityServer4 的 Issuer 配置一致
|
|||
|
ValidateLifetime = true,
|
|||
|
ClockSkew = TimeSpan.Zero // 生产环境建议设为小值或 TimeSpan.Zero,防止 Token 刚签发就被认为过期
|
|||
|
};
|
|||
|
options.BackchannelHttpHandler = new HttpClientHandler
|
|||
|
{
|
|||
|
ServerCertificateCustomValidationCallback = (message, cert, chain, sslPolicyErrors) => true // 忽略 SSL 错误
|
|||
|
};
|
|||
|
|
|||
|
options.Events = new JwtBearerEvents
|
|||
|
{
|
|||
|
OnAuthenticationFailed = context =>
|
|||
|
{
|
|||
|
Console.WriteLine($"{serviceConfig.ServiceID}|{builder.Configuration["AuthorityServer"]} ");
|
|||
|
// 记录身份验证失败的详细信息
|
|||
|
Console.WriteLine($"Authentication failed:{context.Exception.Message}");
|
|||
|
return Task.CompletedTask;
|
|||
|
},
|
|||
|
OnTokenValidated = context =>
|
|||
|
{
|
|||
|
// 记录 token 验证成功的信息
|
|||
|
Console.WriteLine("Token validated successfully.");
|
|||
|
////打印 Claims 信息以便调试
|
|||
|
if (context.Principal != null)
|
|||
|
{
|
|||
|
Console.WriteLine($"Authenticated User Name: {context.Principal.Identity?.Name}");
|
|||
|
foreach (var claim in context.Principal.Claims)
|
|||
|
{
|
|||
|
Console.WriteLine($" Claim Type: {claim.Type}, Value: {claim.Value}");
|
|||
|
}
|
|||
|
}
|
|||
|
return Task.CompletedTask;
|
|||
|
},
|
|||
|
OnMessageReceived = context =>
|
|||
|
{
|
|||
|
//Console.WriteLine("--- JwtBearer OnMessageReceived triggered ---");
|
|||
|
if (context.Request.Headers.ContainsKey("Authorization"))
|
|||
|
{
|
|||
|
Console.WriteLine($"Raw Authorization Header: {context.Request.Headers["Authorization"]}");
|
|||
|
}
|
|||
|
// 这里的 context.Token 应该已经由 JwtBearerHandler 填充,除非有其他中间件干扰
|
|||
|
Console.WriteLine($"Received token (JwtBearer): {context.Token ?? "NULL"}"); // 确保打印 NULL
|
|||
|
Console.WriteLine("--- End JwtBearer OnMessageReceived ---");
|
|||
|
return Task.CompletedTask;
|
|||
|
},
|
|||
|
OnChallenge = context =>
|
|||
|
{
|
|||
|
Console.WriteLine("JwtBearer Challenge triggered.");
|
|||
|
Console.WriteLine($"Challenge Scheme: {context.Scheme.Name}");
|
|||
|
Console.WriteLine($"Challenge Status Code: {context.Response.StatusCode}");
|
|||
|
if (!string.IsNullOrEmpty(context.Error)) Console.WriteLine($"Challenge Error: {context.Error}");
|
|||
|
if (!string.IsNullOrEmpty(context.ErrorDescription)) Console.WriteLine($"Challenge Error Description: {context.ErrorDescription}");
|
|||
|
//全自定义 401 响应,可以在这里处理
|
|||
|
// context.HandleResponse(); // 阻止默认的 401 行为
|
|||
|
// context.Response.StatusCode = StatusCodes.Status401Unauthorized;
|
|||
|
// context.Response.ContentType = "application/json";
|
|||
|
// return context.Response.WriteAsync(JsonSerializer.Serialize(new { message = "Unauthorized", detail = context.ErrorDescription }));
|
|||
|
return Task.CompletedTask;
|
|||
|
}
|
|||
|
};
|
|||
|
});
|
|||
|
|
|||
|
// Swagger 配置
|
|||
|
builder.Services.AddSwaggerGen(c =>
|
|||
|
{
|
|||
|
c.SwaggerDoc(serviceConfig.ServiceName, new OpenApiInfo { Title = $"{serviceConfig.ServiceName} 接口文档", Version = serviceConfig.ServiceVersion });
|
|||
|
c.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, "BZPT.Api.xml"), true);
|
|||
|
|
|||
|
c.AddSecurityRequirement(new OpenApiSecurityRequirement
|
|||
|
{
|
|||
|
{
|
|||
|
new OpenApiSecurityScheme {
|
|||
|
Reference = new OpenApiReference
|
|||
|
{
|
|||
|
Type = ReferenceType.SecurityScheme,
|
|||
|
Id = "Bearer"
|
|||
|
} ,
|
|||
|
Scheme = "oauth2",
|
|||
|
Name = "Bearer",
|
|||
|
In = ParameterLocation.Header,
|
|||
|
}, new List<string>() }
|
|||
|
});
|
|||
|
|
|||
|
// 定义 OAuth2 Client Credentials 安全方案
|
|||
|
c.AddSecurityDefinition("OAuth2", new OpenApiSecurityScheme
|
|||
|
{
|
|||
|
Type = SecuritySchemeType.OAuth2,
|
|||
|
Flows = new OpenApiOAuthFlows
|
|||
|
{
|
|||
|
ClientCredentials = new OpenApiOAuthFlow
|
|||
|
{
|
|||
|
TokenUrl = new Uri($"{builder.Configuration["AuthorityServer"]}/connect/token"), // IdentityServer4 令牌端点
|
|||
|
Scopes = new Dictionary<string, string>
|
|||
|
{
|
|||
|
{ "api_scope", "访问 API 的权限" }
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
});
|
|||
|
c.AddServer(new OpenApiServer { Url = "", Description = "vvv" });
|
|||
|
c.CustomOperationIds(apiDesc => (apiDesc.ActionDescriptor as ControllerActionDescriptor)?.ControllerName + "-" + (apiDesc.ActionDescriptor as ControllerActionDescriptor)?.ActionName);
|
|||
|
});
|
|||
|
|
|||
|
var redis = ConnectionMultiplexer.Connect(redisConn);
|
|||
|
builder.Services.AddDataProtection()
|
|||
|
.PersistKeysToStackExchangeRedis(redis, "DataProtection-Keys")
|
|||
|
.SetApplicationName(serviceConfig.ServiceID);
|
|||
|
builder.Services.AddAntiforgery(options =>
|
|||
|
{
|
|||
|
// 可以在这里配置防伪令牌的相关选项
|
|||
|
options.HeaderName = "X-CSRF-TOKEN";
|
|||
|
});
|
|||
|
|
|||
|
//将 Serilog 注册到默认日志工厂(非必须,但更兼容)
|
|||
|
builder.Logging.ClearProviders(); // 可选:移除默认日志提供程序(如不需要控制台重复输出)
|
|||
|
builder.Logging.AddSerilog(dispose: true); // 桥接 Microsoft.Extensions.Logging 到 Serilog
|
|||
|
|
|||
|
// 应用构建
|
|||
|
var app = builder.Build();
|
|||
|
|
|||
|
// 并且在 UseAuthentication() 和 UseAuthorization() 之前。
|
|||
|
app.UseForwardedHeaders(new ForwardedHeadersOptions
|
|||
|
{
|
|||
|
ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto | ForwardedHeaders.XForwardedHost
|
|||
|
// 注意:通常不需要 ForwardedHeaders.XForwardedPort,因为 X-Forwarded-Host 包含主机和端口
|
|||
|
// 但如果您的反向代理只设置了 X-Forwarded-Port,则可能需要添加
|
|||
|
|
|||
|
// !!!重要:如果您的反向代理是内部的,并且其IP是固定的,建议指定 KnownProxies 或 KnownNetworks,
|
|||
|
// 以防止伪造的 X-Forwarded-* 头。如果这是在容器内部,且代理是 Ingress Controller 或 Docker 自己的网络,
|
|||
|
// 通常可以安全地跳过 KnownProxies/Networks,但在生产环境中要小心。
|
|||
|
// KnownProxies = { IPAddress.Parse("YOUR_PROXY_IP_HERE") }
|
|||
|
// KnownNetworks = { new IPNetwork(IPAddress.Parse("YOUR_PROXY_NETWORK_START_IP"), YOUR_PROXY_NETWORK_CIDR) }
|
|||
|
});
|
|||
|
|
|||
|
|
|||
|
// 2. 配置 ASP.NET Core 内置异常页(开发环境)
|
|||
|
if (app.Environment.IsDevelopment())
|
|||
|
{
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
app.UseHsts();
|
|||
|
}
|
|||
|
// 1. 中间件捕获请求管道中的异常
|
|||
|
app.Use(async (context, next) =>
|
|||
|
{
|
|||
|
try
|
|||
|
{
|
|||
|
#if !DEBUG
|
|||
|
if (DateTime.Now.Hour >= 23 || DateTime.Now.Hour < 7)
|
|||
|
{
|
|||
|
context.Response.StatusCode = 400;
|
|||
|
await context.Response.WriteAsync("系统维护中~");
|
|||
|
return;
|
|||
|
}
|
|||
|
#endif
|
|||
|
// 生成唯一ID(GUID 或 Snowflake ID)
|
|||
|
var requestId = StringObjectIdGenerator.Instance.GenerateId().ToString();
|
|||
|
|
|||
|
// 注入请求头
|
|||
|
context.Request.Headers["X-Request-ID"] = requestId;
|
|||
|
|
|||
|
// 存储到 HttpContext.Items 供后续使用
|
|||
|
context.Items["X-Request-ID"] = requestId;
|
|||
|
// 将 ID 添加到日志上下文
|
|||
|
using (LogContext.PushProperty("RequestID", requestId))
|
|||
|
{
|
|||
|
await next();
|
|||
|
}
|
|||
|
// 处理响应状态码
|
|||
|
if (context.Response.StatusCode == StatusCodes.Status401Unauthorized)
|
|||
|
{
|
|||
|
context.Response.ContentType = "application/json";
|
|||
|
await context.Response.WriteAsync(JsonConvert.SerializeObject(new
|
|||
|
{
|
|||
|
code = 401,
|
|||
|
message = "未授权,请登录"
|
|||
|
}));
|
|||
|
}
|
|||
|
}
|
|||
|
catch (Exception ex)
|
|||
|
{
|
|||
|
LogContext.PushProperty("ErrorCode", 500);
|
|||
|
Serilog.Log.Fatal(ex, "全局捕获的未处理异常(中间件)");
|
|||
|
throw; // 重新抛出以触发内置错误页面
|
|||
|
}
|
|||
|
});
|
|||
|
|
|||
|
// 3. 捕获非 HTTP 管道的全局异常
|
|||
|
AppDomain.CurrentDomain.UnhandledException += (sender, e) =>
|
|||
|
{
|
|||
|
var exception = e.ExceptionObject as Exception;
|
|||
|
LogContext.PushProperty("ErrorCode", 500);
|
|||
|
Serilog.Log.Fatal(exception, "全局捕获的非 HTTP 未处理异常");
|
|||
|
};
|
|||
|
|
|||
|
TaskScheduler.UnobservedTaskException += (sender, e) =>
|
|||
|
{
|
|||
|
LogContext.PushProperty("ErrorCode", 500);
|
|||
|
Serilog.Log.Fatal(e.Exception, "全局捕获的未观察到的任务异常");
|
|||
|
e.SetObserved(); // 标记异常已处理
|
|||
|
};
|
|||
|
|
|||
|
|
|||
|
app.UseSwagger();
|
|||
|
app.UseKnife4UI(c =>
|
|||
|
{
|
|||
|
c.RoutePrefix = "swagger";
|
|||
|
c.SwaggerEndpoint($"/{serviceConfig.ServiceName}/swagger.json", serviceConfig.ServiceName);
|
|||
|
});
|
|||
|
|
|||
|
app.UseHealthChecks("/healthChecks");
|
|||
|
app.UseDefaultFiles(new DefaultFilesOptions { DefaultFileNames = new List<string> { "index.html" } });
|
|||
|
|
|||
|
|
|||
|
app.UseAntiforgery();
|
|||
|
app.UseRouting();
|
|||
|
app.UseCors("CorsMy");
|
|||
|
app.UseAuthentication();
|
|||
|
app.UseAuthorization();
|
|||
|
|
|||
|
//app.UseMiddleware<OrganizationDataFilterMiddleware>();
|
|||
|
// OPTIONS 请求处理
|
|||
|
app.MapMethods("/{**path}", new[] { "OPTIONS" }, () => Results.NoContent()).RequireCors("CorsMy");
|
|||
|
|
|||
|
try
|
|||
|
{
|
|||
|
app.UseIdentityServer();
|
|||
|
|
|||
|
}
|
|||
|
catch (Exception ex)
|
|||
|
{
|
|||
|
Console.WriteLine($"IdentityServer 中间件加载失败: {ex}");
|
|||
|
}
|
|||
|
|
|||
|
app.MapControllers();
|
|||
|
app.Run();
|