✨
This commit is contained in:
142
StopShopping.Api/Middlewares/DevelopmentCookieMiddleware.cs
Normal file
142
StopShopping.Api/Middlewares/DevelopmentCookieMiddleware.cs
Normal file
@@ -0,0 +1,142 @@
|
||||
using System.Text;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
using Microsoft.Net.Http.Headers;
|
||||
using StopShopping.Services.Extensions;
|
||||
|
||||
namespace StopShopping.Api.Middlewares;
|
||||
|
||||
public class DevelopmentCookieMiddleware
|
||||
{
|
||||
public DevelopmentCookieMiddleware(
|
||||
RequestDelegate requestDelegate,
|
||||
IOptions<AppOptions> options)
|
||||
{
|
||||
_next = requestDelegate;
|
||||
_appOptions = options.Value;
|
||||
}
|
||||
|
||||
private readonly RequestDelegate _next;
|
||||
private readonly AppOptions _appOptions;
|
||||
|
||||
public async Task InvokeAsync(HttpContext httpContext)
|
||||
{
|
||||
int? port = null;
|
||||
var origin = httpContext.Request.Headers[HeaderNames.Origin];
|
||||
if (origin.Count > 0 && null != origin[0])
|
||||
{
|
||||
Uri uri = new(origin[0]!);
|
||||
port = uri.Port;
|
||||
}
|
||||
else
|
||||
{
|
||||
var referer = httpContext.Request.Headers[HeaderNames.Referer];
|
||||
if (referer.Count > 0 && null != referer[0])
|
||||
{
|
||||
Uri uri = new(referer[0]!);
|
||||
port = uri.Port;
|
||||
}
|
||||
}
|
||||
|
||||
if (port.HasValue)
|
||||
{
|
||||
var modified = ModifyCookie(httpContext.Request.Headers[HeaderNames.Cookie], port.Value);
|
||||
httpContext.Request.Headers[HeaderNames.Cookie] = modified;
|
||||
}
|
||||
|
||||
httpContext.Response.OnStarting(() =>
|
||||
{
|
||||
if (port.HasValue)
|
||||
{
|
||||
var cookieHeader = httpContext.Response.Headers[HeaderNames.SetCookie];
|
||||
ModifyResponseCookie(cookieHeader, httpContext, port.Value);
|
||||
}
|
||||
return Task.CompletedTask;
|
||||
});
|
||||
|
||||
await _next(httpContext);
|
||||
}
|
||||
|
||||
private void ModifyResponseCookie(StringValues cookieHeader, HttpContext httpContext, int port)
|
||||
{
|
||||
foreach (var cookieItem in cookieHeader)
|
||||
{
|
||||
if (cookieItem != null)
|
||||
{
|
||||
var cookies = cookieItem.Split(';');
|
||||
foreach (var item in cookies)
|
||||
{
|
||||
if (null != item)
|
||||
{
|
||||
var pairs = item.Split('=');
|
||||
if (2 == pairs.Length)
|
||||
{
|
||||
if (pairs[0].Trim() == _appOptions.CSRFCookieName)
|
||||
{
|
||||
var val = pairs[1];
|
||||
httpContext.Response.Cookies.Delete(pairs[0]);
|
||||
httpContext.Response.Cookies.Append($"{_appOptions.CSRFCookieName}_{port}", val);
|
||||
}
|
||||
else if (pairs[0].Trim() == HttpExtensions.REFRESH_TOKEN_COOKIE_KEY)
|
||||
{
|
||||
var val = pairs[1];
|
||||
httpContext.Response.Cookies.Delete(pairs[0]);
|
||||
httpContext.Response.Cookies.Append($"{HttpExtensions.REFRESH_TOKEN_COOKIE_KEY}_{port}", val);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private StringValues ModifyCookie(StringValues cookie, int port)
|
||||
{
|
||||
List<string> result = [];
|
||||
var csrfCookieName = $"{_appOptions.CSRFCookieName}_{port}";
|
||||
var refreshTokenCookieName = $"{HttpExtensions.REFRESH_TOKEN_COOKIE_KEY}_{port}";
|
||||
foreach (var cookieItem in cookie)
|
||||
{
|
||||
if (null != cookieItem)
|
||||
{
|
||||
StringBuilder itemBuilder = new();
|
||||
var cookies = cookieItem.Split(';');
|
||||
foreach (var item in cookies)
|
||||
{
|
||||
if (null != item)
|
||||
{
|
||||
var pairs = item.Split('=');
|
||||
if (pairs.Length == 2)
|
||||
{
|
||||
if (0 != itemBuilder.Length)
|
||||
itemBuilder.Append(';');
|
||||
if (pairs[0].Trim() == csrfCookieName)
|
||||
{
|
||||
itemBuilder.Append(_appOptions.CSRFCookieName);
|
||||
itemBuilder.Append('=');
|
||||
itemBuilder.Append(pairs[1].Trim());
|
||||
}
|
||||
else if (pairs[0].Trim() == refreshTokenCookieName)
|
||||
{
|
||||
itemBuilder.Append(HttpExtensions.REFRESH_TOKEN_COOKIE_KEY);
|
||||
itemBuilder.Append('=');
|
||||
itemBuilder.Append(pairs[1].Trim());
|
||||
}
|
||||
else
|
||||
{
|
||||
itemBuilder.Append(item);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
itemBuilder.Append(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result.Add(itemBuilder.ToString());
|
||||
}
|
||||
}
|
||||
return new StringValues(result.ToArray());
|
||||
}
|
||||
}
|
||||
100
StopShopping.Api/Middlewares/GlobalExceptionHandlerMiddleware.cs
Normal file
100
StopShopping.Api/Middlewares/GlobalExceptionHandlerMiddleware.cs
Normal file
@@ -0,0 +1,100 @@
|
||||
using Microsoft.AspNetCore.Antiforgery;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using StopShopping.Services.Models.Resp;
|
||||
|
||||
namespace StopShopping.Api.Middlewares;
|
||||
|
||||
public class GlobalExceptionHandlerMiddleware
|
||||
{
|
||||
public GlobalExceptionHandlerMiddleware(RequestDelegate requestDelegate,
|
||||
ILogger<GlobalExceptionHandlerMiddleware> logger,
|
||||
IProblemDetailsService problemDetailsService)
|
||||
{
|
||||
_next = requestDelegate;
|
||||
_logger = logger;
|
||||
_problemDetailsService = problemDetailsService;
|
||||
}
|
||||
|
||||
private readonly RequestDelegate _next;
|
||||
private readonly ILogger<GlobalExceptionHandlerMiddleware> _logger;
|
||||
private readonly IProblemDetailsService _problemDetailsService;
|
||||
|
||||
public async Task InvokeAsync(HttpContext httpContext)
|
||||
{
|
||||
try
|
||||
{
|
||||
await _next(httpContext);
|
||||
}
|
||||
catch (BadHttpRequestException ex) when (ex.InnerException is AntiforgeryValidationException)
|
||||
{
|
||||
var problemDetails = new ProblemDetails
|
||||
{
|
||||
Detail = ex.InnerException.Message,
|
||||
Instance = httpContext.Request.Path,
|
||||
Status = StatusCodes.Status400BadRequest,
|
||||
Title = "CSRF 错误",
|
||||
};
|
||||
|
||||
problemDetails.AddErrorCode(ProblemDetailsCodes.CsrfValidationFailed);
|
||||
|
||||
httpContext.Response.StatusCode = StatusCodes.Status400BadRequest;
|
||||
httpContext.Response.ContentType = "application/problem+json";
|
||||
|
||||
await _problemDetailsService.WriteAsync(new ProblemDetailsContext
|
||||
{
|
||||
HttpContext = httpContext,
|
||||
ProblemDetails = problemDetails
|
||||
});
|
||||
}
|
||||
catch (BadHttpRequestException ex)
|
||||
{
|
||||
_logger.LogError(ex, "参数错误");
|
||||
|
||||
var problemDetails = new ProblemDetails
|
||||
{
|
||||
Detail = ex.Message,
|
||||
Instance = httpContext.Request.Path,
|
||||
Status = StatusCodes.Status400BadRequest,
|
||||
Title = "参数错误",
|
||||
};
|
||||
|
||||
problemDetails.AddErrorCode(ProblemDetailsCodes.BadParameters);
|
||||
|
||||
httpContext.Response.StatusCode = StatusCodes.Status400BadRequest;
|
||||
httpContext.Response.ContentType = "application/problem+json";
|
||||
|
||||
await _problemDetailsService.WriteAsync(new ProblemDetailsContext
|
||||
{
|
||||
HttpContext = httpContext,
|
||||
ProblemDetails = problemDetails
|
||||
});
|
||||
}
|
||||
catch (ServiceException ex)//业务层抛出提示
|
||||
{
|
||||
await httpContext.Response.WriteAsJsonAsync(ApiResponse.Failed(ex.Message));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogCritical(ex, "意外的异常");
|
||||
|
||||
var problemDetails = new ProblemDetails
|
||||
{
|
||||
Detail = ex.Message,
|
||||
Instance = httpContext.Request.Path,
|
||||
Status = StatusCodes.Status500InternalServerError,
|
||||
Title = "服务器错误",
|
||||
};
|
||||
|
||||
problemDetails.AddErrorCode(ProblemDetailsCodes.ServerError);
|
||||
|
||||
httpContext.Response.StatusCode = StatusCodes.Status500InternalServerError;
|
||||
httpContext.Response.ContentType = "application/problem+json";
|
||||
|
||||
await _problemDetailsService.WriteAsync(new ProblemDetailsContext
|
||||
{
|
||||
HttpContext = httpContext,
|
||||
ProblemDetails = problemDetails,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
23
StopShopping.Api/Middlewares/ProblemDetailsExtensions.cs
Normal file
23
StopShopping.Api/Middlewares/ProblemDetailsExtensions.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace StopShopping.Api.Middlewares;
|
||||
|
||||
public static class ProblemDetailsExtensions
|
||||
{
|
||||
private const string CODE_FIELD = "code";
|
||||
public static ProblemDetails AddErrorCode(this ProblemDetails problemDetails, ProblemDetailsCodes code)
|
||||
{
|
||||
problemDetails.Extensions ??= new Dictionary<string, object?>();
|
||||
|
||||
problemDetails.Extensions.Add(CODE_FIELD, (int)code);
|
||||
return problemDetails;
|
||||
}
|
||||
}
|
||||
|
||||
public enum ProblemDetailsCodes
|
||||
{
|
||||
CsrfValidationFailed = 1000,
|
||||
ParametersValidationFailed = 1001,
|
||||
BadParameters = 1002,
|
||||
ServerError = 1003,
|
||||
}
|
||||
Reference in New Issue
Block a user