Skip to content

Commit

Permalink
Refactor exception handler middleware (#1129)
Browse files Browse the repository at this point in the history
After adding metrics, our 'unhandled exception' counter never increases,
probably because we handle all exceptions in ExceptionHandlerMiddleware

Put the exception handler middleware as a dedicated exception handler
rather than just a generic middleware with try/catch to ensure ASP.NET
increments the unhandled exception counter
  • Loading branch information
SapiensAnatis authored Oct 27, 2024
1 parent 20242d4 commit a757332
Show file tree
Hide file tree
Showing 4 changed files with 25 additions and 27 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ namespace DragaliaAPI.Controllers;
/// should be used.
/// </remarks>
[ApiController]
[SerializeException]
[Authorize(AuthenticationSchemes = SchemeName.Session)]
[Consumes("application/octet-stream")]
[Produces("application/x-msgpack")]
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -3,36 +3,38 @@
using DragaliaAPI.Models;
using DragaliaAPI.Services.Exceptions;
using MessagePack;
using Microsoft.AspNetCore.Diagnostics;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Primitives;
using Microsoft.IdentityModel.Tokens;

namespace DragaliaAPI.Infrastructure.Middleware;

public class ExceptionHandlerMiddleware(RequestDelegate next)
public static class ExceptionHandlerMiddleware
{
private static readonly DistributedCacheEntryOptions CacheOptions =
new() { AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(5) };

public async Task InvokeAsync(
HttpContext context,
ILogger<ExceptionHandlerMiddleware> logger,
IDistributedCache cache
)
public static async Task HandleAsync(HttpContext context)
{
Endpoint? endpoint = context.GetEndpoint();
bool serializeException =
endpoint?.Metadata.GetMetadata<SerializeExceptionAttribute>() is not null;

string? deviceId = null;
if (context.Request.Headers.TryGetValue("DeviceId", out StringValues values))
deviceId = values.FirstOrDefault();

try
{
await next(context);
deviceId = values.FirstOrDefault();
}
catch (SecurityTokenExpiredException ex) when (serializeException && deviceId is not null)

IExceptionHandlerPathFeature? exceptionHandlerPathFeature =
context.Features.Get<IExceptionHandlerPathFeature>();

Exception? exception = exceptionHandlerPathFeature?.Error;

ILogger logger = context
.RequestServices.GetRequiredService<ILoggerFactory>()
.CreateLogger(typeof(ExceptionHandlerMiddleware));

IDistributedCache cache = context.RequestServices.GetRequiredService<IDistributedCache>();

if (exception is SecurityTokenExpiredException ex && deviceId is not null)
{
logger.LogDebug("ID token was expired. Expiry: {expiry}", ex.Expires);

Expand All @@ -55,20 +57,19 @@ IDistributedCache cache
context.Response.StatusCode = StatusCodes.Status400BadRequest;
context.Response.Headers.Append("Is-Required-Refresh-Id-Token", "true");
}
catch (Exception ex)
when (serializeException && context.RequestAborted.IsCancellationRequested)
else if (context.RequestAborted.IsCancellationRequested)
{
logger.LogWarning(ex, "Client cancelled request.");
logger.LogWarning(exception, "Client cancelled request.");
context.Response.StatusCode = StatusCodes.Status400BadRequest;
}
catch (Exception ex) when (serializeException)
else
{
ResultCode code = ex is DragaliaException dragaliaException
ResultCode code = exception is DragaliaException dragaliaException
? dragaliaException.Code
: ResultCode.CommonServerError;

logger.LogError(
ex,
exception,
"Encountered unhandled exception. Returning result_code {code}",
code
);
Expand Down
4 changes: 3 additions & 1 deletion DragaliaAPI/DragaliaAPI/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,9 @@
applicationBuilder.UseMiddleware<ResultCodeLoggingMiddleware>();
applicationBuilder.UseOutputCache();
applicationBuilder.UseMiddleware<NotFoundHandlerMiddleware>();
applicationBuilder.UseMiddleware<ExceptionHandlerMiddleware>();
applicationBuilder.UseExceptionHandler(cfg =>
cfg.Run(ExceptionHandlerMiddleware.HandleAsync)
);
applicationBuilder.UseMiddleware<DailyResetMiddleware>();
applicationBuilder.UseEndpoints(endpoints =>
{
Expand Down

0 comments on commit a757332

Please sign in to comment.