WebApi配置
WebApi配置
模版使用的是Ater.dry
1.Program配置

namespace WebApplication1
{
public class Program
{
public static void Main(string[] args)
{
var builder = WebApplication.CreateBuilder(args);
// 1 注册和配置Web服务依赖
builder.AddDefaultWebServices();
WebApplication app = builder.Build();
// 2 使用中间件
app.UseDefaultWebServices();
app.Run();
}
}
}
2.ServiceCollectionExtension配置

using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.IdentityModel.Tokens;
using Microsoft.OpenApi.Models;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Serialization;
using System.Net;
using System.Text;
using System.Threading.RateLimiting;
namespace WebApplication1
{
public static class ServiceCollectionExtension
{
/// <summary>
/// 注册和配置Web服务依赖
/// </summary>
/// <param name="builder"></param>
/// <returns></returns>
public static IServiceCollection AddDefaultWebServices(this WebApplicationBuilder builder)
{
builder.Services.ConfigWebComponents(builder.Configuration);
builder.Services.AddHttpContextAccessor();
builder.Services.AddControllers()
.AddNewtonsoftJson(options =>
{
options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; //忽略循环引用
options.SerializerSettings.ContractResolver = new DefaultContractResolver(); //使用默认的合同解析器
options.SerializerSettings.DateFormatString = "yyyy-MM-dd HH:mm:ss"; //设置日期时间格式
//options.SerializerSettings.NullValueHandling = NullValueHandling.Ignore; //空值处理
options.SerializerSettings.DateTimeZoneHandling = DateTimeZoneHandling.Local; // 处理日期时间的时区为本地时区
options.SerializerSettings.Converters.Add(new StringEnumConverter()); //枚举值转换为字符串
});
return builder.Services;
}
#region 添加web服务组件,如身份认证/授权/swagger/cors
/// <summary>
/// 添加web服务组件,如身份认证/授权/swagger/cors
/// </summary>
/// <param name="services"></param>
/// <param name="configuration"></param>
/// <returns></returns>
public static IServiceCollection ConfigWebComponents(this IServiceCollection services, IConfiguration configuration)
{
services.AddSwagger();
services.AddJwtAuthentication(configuration);
services.AddAuthorize();
services.AddCors();
services.AddRateLimiter();
return services;
}
/// <summary>
/// 添加swagger服务
/// </summary>
/// <param name="services"></param>
/// <returns></returns>
public static IServiceCollection AddSwagger(this IServiceCollection services)
{
services.AddEndpointsApiExplorer();
services.AddSwaggerGen(c =>
{
c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
{
In = ParameterLocation.Header,
Description = "Please enter a valid token",
Name = "Authorization",
Type = SecuritySchemeType.Http,
BearerFormat = "JWT",
Scheme = "Bearer"
});
c.AddSecurityRequirement(new OpenApiSecurityRequirement
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Type=ReferenceType.SecurityScheme,
Id="Bearer"
}
},
Array.Empty<string>()
}
});
c.SwaggerDoc("admin", new OpenApiInfo
{
Title = "Demo1",
Description = "Admin API 文档. 更新时间:" + DateTime.Now.ToString("yyyy-MM-dd H:mm:ss"),
Version = "v1"
});
c.SwaggerDoc("client", new OpenApiInfo
{
Title = "Demo1 client",
Description = "Client API 文档. 更新时间:" + DateTime.Now.ToString("yyyy-MM-dd H:mm:ss"),
Version = "v1"
});
var xmlFiles = Directory.GetFiles(AppContext.BaseDirectory, "*.xml", SearchOption.TopDirectoryOnly);
foreach (var item in xmlFiles)
{
try
{
c.IncludeXmlComments(item, includeControllerXmlComments: true);
}
catch (Exception) { }
}
c.SupportNonNullableReferenceTypes();
c.DescribeAllParametersInCamelCase();
c.CustomOperationIds((z) =>
{
var descriptor = (ControllerActionDescriptor)z.ActionDescriptor;
return $"{descriptor.ControllerName}_{descriptor.ActionName}";
});
c.MapType<DateOnly>(() => new OpenApiSchema
{
Type = "string",
Format = "date"
});
});
return services;
}
/// <summary>
/// 添加 jwt 验证
/// </summary>
/// <param name="services"></param>
/// <param name="configuration"></param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
public static IServiceCollection AddJwtAuthentication(this IServiceCollection services, IConfiguration configuration)
{
services.AddAuthentication(options =>
{
// 设置默认的身份验证方案为 JWT Bearer
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
//等于builder.Services.AddAuthentication("Bearer")
// 添加 JWT Bearer 身份验证方案配置
.AddJwtBearer(cfg =>
{
cfg.SaveToken = true;
// 从配置中获取 JWT 签名
var sign = configuration.GetSection("Authentication")["Jwt:Sign"];
if (string.IsNullOrEmpty(sign))
{
throw new Exception("未找到有效的Jwt配置");
}
// 配置 Token 验证参数
cfg.TokenValidationParameters = new()
{
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(sign)),
ValidIssuer = configuration.GetSection("Authentication")["Jwt:ValidIssuer"],
ValidAudience = configuration.GetSection("Authentication")["Jwt:ValidAudiences"],
ValidateIssuer = true,
ValidateLifetime = true,
RequireExpirationTime = true,
ValidateIssuerSigningKey = true
};
});
return services;
}
/// <summary>
/// 添加 Authorize 服务
/// </summary>
/// <param name="services"></param>
/// <returns></returns>
public static IServiceCollection AddAuthorize(this IServiceCollection services)
{
services.AddAuthorizationBuilder()
.AddPolicy("User", policy => policy.RequireRole("User"))
.AddPolicy("AdminUser", policy => policy.RequireRole("SuperAdmin", "AdminUser"))
.AddPolicy("SuperAdmin", policy => policy.RequireRole("SuperAdmin"));
return services;
}
/// <summary>
/// 添加Cros服务
/// </summary>
/// <param name="services"></param>
/// <returns></returns>
public static IServiceCollection AddCors(this IServiceCollection services)
{
services.AddCors(options =>
{
options.AddPolicy("Default", builder =>
{
builder.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader();
});
});
return services;
}
/// <summary>
/// 添加速率限制
/// </summary>
/// <param name="services"></param>
/// <returns></returns>
public static IServiceCollection AddRateLimiter(this IServiceCollection services)
{
services.AddRateLimiter(options =>
{
options.RejectionStatusCode = StatusCodes.Status429TooManyRequests;
// 验证码 每10秒5次
options.AddPolicy("captcha", context =>
{
var remoteIpAddress = context.Connection.RemoteIpAddress;
if (!IPAddress.IsLoopback(remoteIpAddress!))
{
return RateLimitPartition.GetFixedWindowLimiter(remoteIpAddress!.ToString(), _ =>
new FixedWindowRateLimiterOptions
{
PermitLimit = 5,
Window = TimeSpan.FromSeconds(10),
QueueProcessingOrder = QueueProcessingOrder.OldestFirst,
QueueLimit = 3
});
}
else
{
return RateLimitPartition.GetNoLimiter(remoteIpAddress!.ToString());
}
});
// 全局限制 每10秒100次
options.GlobalLimiter = PartitionedRateLimiter.Create<HttpContext, IPAddress>(context =>
{
IPAddress? remoteIpAddress = context.Connection.RemoteIpAddress;
if (!IPAddress.IsLoopback(remoteIpAddress!))
{
return RateLimitPartition.GetFixedWindowLimiter(remoteIpAddress!, _ =>
new FixedWindowRateLimiterOptions
{
PermitLimit = 100,
Window = TimeSpan.FromSeconds(10),
QueueProcessingOrder = QueueProcessingOrder.OldestFirst,
QueueLimit = 3
});
}
return RateLimitPartition.GetNoLimiter(IPAddress.Loopback);
});
});
return services;
}
#endregion
/// <summary>
/// 使用中间件
/// </summary>
/// <param name="app"></param>
/// <returns></returns>
public static WebApplication UseDefaultWebServices(this WebApplication app)
{
// 异常统一处理
// app.UseExceptionHandler(ExceptionHandler.Handler());
if (app.Environment.IsDevelopment())
{
app.UseCors("Default");
app.UseSwagger();
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/client/swagger.json", name: "client");
c.SwaggerEndpoint("/swagger/admin/swagger.json", "admin");
});
}
else
{
app.UseCors("Default");
//app.UseHsts();
app.UseHttpsRedirection();
}
app.UseRateLimiter();
//添加默认页面
DefaultFilesOptions defaultFilesOptions = new DefaultFilesOptions();
defaultFilesOptions.DefaultFileNames.Clear();
defaultFilesOptions.DefaultFileNames.Add("index.html");
app.UseDefaultFiles(defaultFilesOptions);
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
return app;
}
}
}
3.统一异常处理

using Microsoft.AspNetCore.Diagnostics;
namespace WebApplication1
{
public static class ExceptionHandler
{
public static Action<IApplicationBuilder> Handler()
{
return builder =>
{
builder.Run(async context =>
{
context.Response.StatusCode = 500;
Exception? exception = context.Features.Get<IExceptionHandlerFeature>()?.Error;
var result = new
{
Title = "异常错误",
exception?.Source,
Detail = exception?.Message + exception?.InnerException?.Message,
exception?.StackTrace,
Status = 500,
TraceId = context.TraceIdentifier
};
await context.Response.WriteAsJsonAsync(result);
});
};
}
}
}
4.默认页面

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>默认页面</title>
<style>
body {
font-family: Arial, sans-serif;
text-align: center;
padding: 50px;
}
h1 {
color: #333;
}
p {
color: #666;
}
</style>
</head>
<body>
<h1>这里是默认页面</h1>
</body>
</html>

浙公网安备 33010602011771号