修改代码
增加云商品数据
This commit is contained in:
1 parent
2f406c840f
commit
db4f030c17
58 files changed
+1550
-120
No files matched your search
@@ -0,0 +1,189 @@
|
||||
using MAX.Framework.Dtos;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.IO;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace MAX.Framework.Commons
|
||||
{
|
||||
/// <summary>
|
||||
/// 统一状态码中间件
|
||||
/// 统一返回200 固定格式
|
||||
/// </summary>
|
||||
internal class UnifyStatusCodeMiddleware
|
||||
{
|
||||
|
||||
private readonly RequestDelegate _next;
|
||||
private readonly ILogger<UnifyStatusCodeMiddleware> _logger;
|
||||
|
||||
// 静态复用,全局唯一的内存池管理器
|
||||
private static readonly RecyclableMemoryStreamManager _streamManager = new();
|
||||
|
||||
// 需要直接透传、不做缓冲的 Content-Type 前缀
|
||||
private static readonly string[] _passthroughPrefixes =
|
||||
{
|
||||
"image/",
|
||||
"audio/",
|
||||
"video/",
|
||||
"application/octet-stream",
|
||||
"application/zip",
|
||||
"application/pdf",
|
||||
"application/x-msdownload",
|
||||
"text/event-stream"
|
||||
};
|
||||
|
||||
public UnifyStatusCodeMiddleware(RequestDelegate next, ILogger<UnifyStatusCodeMiddleware> logger)
|
||||
{
|
||||
_next = next;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task InvokeAsync(HttpContext context)
|
||||
{
|
||||
// 1. 只处理 /api 路径(按需调整或删除)
|
||||
if (!context.Request.Path.StartsWithSegments("/api"))
|
||||
{
|
||||
await _next(context);
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. 二进制 / 流式响应:不缓冲,直接透传
|
||||
var accept = context.Request.Headers.Accept.ToString();
|
||||
if (IsPassthroughByRequest(context) || IsPassthroughByAccept(accept))
|
||||
{
|
||||
await _next(context);
|
||||
return;
|
||||
}
|
||||
|
||||
var originalBody = context.Response.Body;
|
||||
var buffer = _streamManager.GetStream();
|
||||
|
||||
context.Response.Body = buffer;
|
||||
|
||||
try
|
||||
{
|
||||
await _next(context);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// 兜底:Filter 没抓到的异常
|
||||
_logger.LogError(ex, "未处理异常: {Path}", context.Request.Path);
|
||||
|
||||
// 清空已写入的内容,准备返回 500 统一格式
|
||||
buffer.SetLength(0);
|
||||
context.Response.StatusCode = StatusCodes.Status500InternalServerError;
|
||||
}
|
||||
|
||||
var statusCode = context.Response.StatusCode;
|
||||
var contentType = context.Response.ContentType ?? string.Empty;
|
||||
|
||||
// 3. 响应体如果本身就是二进制/流式,透传不处理
|
||||
if (IsPassthroughByResponseContentType(contentType))
|
||||
{
|
||||
buffer.Position = 0;
|
||||
context.Response.Body = originalBody;
|
||||
await buffer.CopyToAsync(originalBody);
|
||||
return;
|
||||
}
|
||||
|
||||
// 4. 2xx 且已有响应体:透传(已被 UnifyResultFilter 包装过,不再包一层)
|
||||
if (statusCode >= 200 && statusCode < 300 && buffer.Length > 0)
|
||||
{
|
||||
buffer.Position = 0;
|
||||
context.Response.Body = originalBody;
|
||||
await buffer.CopyToAsync(originalBody);
|
||||
return;
|
||||
}
|
||||
|
||||
// 5. 非 2xx:统一改写为 200 + ApiResult
|
||||
var (code, msg) = MapStatusCode(statusCode);
|
||||
|
||||
// 如果是 2xx 但响应体为空(少见,例如 204),也当作成功
|
||||
if (statusCode >= 200 && statusCode < 300)
|
||||
{
|
||||
code = (int)ResultCode.Success;
|
||||
msg = "success";
|
||||
}
|
||||
|
||||
var result = statusCode >= 200 && statusCode < 300
|
||||
? ApiResult.Success()
|
||||
: ApiResult.Fail(msg, code);
|
||||
|
||||
// 恢复原始流并写出
|
||||
context.Response.Body = originalBody;
|
||||
context.Response.StatusCode = StatusCodes.Status200OK;
|
||||
context.Response.ContentType = "application/json; charset=utf-8";
|
||||
context.Response.ContentLength = null;
|
||||
|
||||
// 清理可能存在的旧响应头
|
||||
context.Response.Headers.Remove("WWW-Authenticate");
|
||||
|
||||
await context.Response.WriteAsJsonAsync(result, new JsonSerializerOptions
|
||||
{
|
||||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
|
||||
});
|
||||
|
||||
buffer.Dispose();
|
||||
}
|
||||
|
||||
private static bool IsPassthroughByRequest(HttpContext context)
|
||||
{
|
||||
// 上传大文件等场景,请求体大,避免缓冲
|
||||
var contentLength = context.Request.ContentLength;
|
||||
if (contentLength.HasValue && contentLength.Value > 1_000_000) // >1MB
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Range 请求(断点续传)不缓冲
|
||||
if (context.Request.Headers.ContainsKey("Range"))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool IsPassthroughByAccept(string accept)
|
||||
{
|
||||
if (string.IsNullOrEmpty(accept)) return false;
|
||||
|
||||
foreach (var prefix in _passthroughPrefixes)
|
||||
{
|
||||
if (accept.Contains(prefix, StringComparison.OrdinalIgnoreCase))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool IsPassthroughByResponseContentType(string contentType)
|
||||
{
|
||||
if (string.IsNullOrEmpty(contentType)) return false;
|
||||
|
||||
foreach (var prefix in _passthroughPrefixes)
|
||||
{
|
||||
if (contentType.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static (int code, string msg) MapStatusCode(int statusCode) => statusCode switch
|
||||
{
|
||||
400 => ((int)ResultCode.ValidationError, "请求参数错误"),
|
||||
401 => ((int)ResultCode.Unauthorized, "未授权,请先登录"),
|
||||
403 => ((int)ResultCode.Forbidden, "禁止访问"),
|
||||
404 => ((int)ResultCode.NotFound, "资源不存在"),
|
||||
405 => ((int)ResultCode.Fail, "请求方法不允许"),
|
||||
429 => ((int)ResultCode.Fail, "请求过于频繁"),
|
||||
500 => ((int)ResultCode.ServerError, "服务器内部错误"),
|
||||
502 => ((int)ResultCode.ServerError, "网关错误"),
|
||||
503 => ((int)ResultCode.ServerError, "服务暂不可用"),
|
||||
504 => ((int)ResultCode.ServerError, "网关超时"),
|
||||
_ => ((int)ResultCode.Fail, $"请求失败({statusCode})")
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -211,6 +211,7 @@ namespace MAX.Framework.DependencyInjection
|
||||
|
||||
public static IApplicationBuilder UseStore(this IApplicationBuilder app)
|
||||
{
|
||||
app.UseMiddleware<UnifyStatusCodeMiddleware>();
|
||||
foreach (IStartup startup in Startups)
|
||||
{
|
||||
startup.Start((app as IHost).Services);
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
<PackageReference Include="FreeSql.Provider.MySql" Version="3.5.311" />
|
||||
<PackageReference Include="FreeSql.Repository" Version="3.5.311" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="10.0.10" />
|
||||
<PackageReference Include="Microsoft.IO.RecyclableMemoryStream" Version="3.0.1" />
|
||||
<PackageReference Include="NLog.Web.AspNetCore" Version="6.2.0" />
|
||||
<PackageReference Include="Riok.Mapperly" Version="4.3.1" />
|
||||
</ItemGroup>
|
||||
|
||||
Reference in new issue
Block a user