From 52a76625a3142b3597e3030f10df94b100f343c5 Mon Sep 17 00:00:00 2001 From: Zeeshaun Date: Thu, 19 Mar 2026 16:54:06 -0500 Subject: [PATCH] Location error handling --- .../Controllers/LocationsController.cs | 64 ++++++++++++++----- microservices/LocationsApi/Program.cs | 35 ++++++++++ 2 files changed, 83 insertions(+), 16 deletions(-) diff --git a/microservices/LocationsApi/Controllers/LocationsController.cs b/microservices/LocationsApi/Controllers/LocationsController.cs index b24e067..3cf947b 100644 --- a/microservices/LocationsApi/Controllers/LocationsController.cs +++ b/microservices/LocationsApi/Controllers/LocationsController.cs @@ -17,12 +17,14 @@ public class LocationsController : ControllerBase private readonly LocationStore _locations; private readonly IHttpClientFactory _httpClientFactory; private readonly IConfiguration _configuration; + private readonly ILogger _logger; - public LocationsController(LocationStore locations, IHttpClientFactory httpClientFactory, IConfiguration configuration) + public LocationsController(LocationStore locations, IHttpClientFactory httpClientFactory, IConfiguration configuration, ILogger logger) { _locations = locations; _httpClientFactory = httpClientFactory; _configuration = configuration; + _logger = logger; } [HttpPost] @@ -140,26 +142,56 @@ public class LocationsController : ControllerBase if (!string.IsNullOrWhiteSpace(token)) request.Headers.Authorization = AuthenticationHeaderValue.Parse(token); - using var response = await client.SendAsync(request); - var responseBody = await response.Content.ReadAsStringAsync(); - if (!response.IsSuccessStatusCode) + HttpResponseMessage response; + try + { + response = await client.SendAsync(request); + } + catch (HttpRequestException ex) { if (interact.PreviousObject is not null) await _locations.RestoreObjectInteractionAsync(id, interact.PreviousObject); - return StatusCode((int)response.StatusCode, responseBody); + + _logger.LogError( + ex, + "Failed to reach InventoryApi while processing location interaction for location {LocationId}, character {CharacterId}, object {ObjectId}", + id, + req.CharacterId, + req.ObjectId + ); + + return StatusCode(StatusCodes.Status502BadGateway, new + { + type = "https://httpstatuses.com/502", + title = "Bad Gateway", + status = 502, + detail = $"Failed to reach InventoryApi at {inventoryBaseUrl}.", + traceId = HttpContext.TraceIdentifier + }); } - return Ok(new InteractLocationObjectResponse + using (response) { - LocationId = id, - CharacterId = req.CharacterId, - ObjectId = interact.ObjectId, - ObjectType = interact.ObjectType, - ItemKey = interact.ItemKey, - QuantityGranted = interact.QuantityGranted, - RemainingQuantity = interact.RemainingQuantity, - Consumed = interact.Consumed, - InventoryResponseJson = responseBody - }); + var responseBody = await response.Content.ReadAsStringAsync(); + if (!response.IsSuccessStatusCode) + { + if (interact.PreviousObject is not null) + await _locations.RestoreObjectInteractionAsync(id, interact.PreviousObject); + return StatusCode((int)response.StatusCode, responseBody); + } + + return Ok(new InteractLocationObjectResponse + { + LocationId = id, + CharacterId = req.CharacterId, + ObjectId = interact.ObjectId, + ObjectType = interact.ObjectType, + ItemKey = interact.ItemKey, + QuantityGranted = interact.QuantityGranted, + RemainingQuantity = interact.RemainingQuantity, + Consumed = interact.Consumed, + InventoryResponseJson = responseBody + }); + } } } diff --git a/microservices/LocationsApi/Program.cs b/microservices/LocationsApi/Program.cs index fdfbe7c..a6b869f 100644 --- a/microservices/LocationsApi/Program.cs +++ b/microservices/LocationsApi/Program.cs @@ -1,5 +1,6 @@ using LocationsApi.Services; using Microsoft.AspNetCore.Authentication.JwtBearer; +using Microsoft.AspNetCore.Diagnostics; using Microsoft.IdentityModel.Tokens; using Microsoft.OpenApi.Models; using System.Text; @@ -60,6 +61,40 @@ builder.Services.AddAuthorization(); var app = builder.Build(); +app.UseExceptionHandler(errorApp => +{ + errorApp.Run(async context => + { + var feature = context.Features.Get(); + var exception = feature?.Error; + var logger = context.RequestServices.GetRequiredService().CreateLogger("GlobalException"); + var traceId = context.TraceIdentifier; + + if (exception is not null) + { + logger.LogError( + exception, + "Unhandled exception for {Method} {Path}. TraceId={TraceId}", + context.Request.Method, + context.Request.Path, + traceId + ); + } + + context.Response.StatusCode = StatusCodes.Status500InternalServerError; + context.Response.ContentType = "application/problem+json"; + + await context.Response.WriteAsJsonAsync(new + { + type = "https://httpstatuses.com/500", + title = "Internal Server Error", + status = 500, + detail = exception?.Message ?? "An unexpected server error occurred.", + traceId + }); + }); +}); + app.MapGet("/healthz", () => Results.Ok("ok")); app.UseSwagger(); app.UseSwaggerUI(o =>