Skip to content

Commit 327aeef

Browse files
committed
Restore declarative status code response functionality
1 parent d095ad4 commit 327aeef

6 files changed

Lines changed: 33 additions & 35 deletions

File tree

src/Dibix.Http.Host/Program.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ void ConfigureLogging(ILoggingBuilder logging)
6868
.AddTransient<IPostConfigureOptions<JsonOptions>, JsonPostConfigureOptions>()
6969
.AddSingleton<IControllerActivator, NotSupportedControllerActivator>();
7070

71-
services.AddExceptionHandler<DatabaseAccessExceptionHandler>();
71+
services.AddExceptionHandler<HttpRequestExecutionExceptionHandler>();
7272
services.AddProblemDetailsWithMapping()
7373
.Map<HttpRequestExecutionException>(x => x.IsClientError, (x, y) =>
7474
{

src/Dibix.Http.Host/Runtime/DatabaseAccessExceptionHandler.cs renamed to src/Dibix.Http.Host/Runtime/HttpRequestExecutionExceptionHandler.cs

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,12 @@
88

99
namespace Dibix.Http.Host
1010
{
11-
// Map sql error codes to http status codes globally
12-
// This is needed if a DatabaseAccessException is thrown outside HttpActionInvoker. For example, within the http host extension.
13-
internal sealed class DatabaseAccessExceptionHandler : IExceptionHandler
11+
// Map custom HTTP status codes to response
12+
internal sealed class HttpRequestExecutionExceptionHandler : IExceptionHandler
1413
{
1514
private readonly IProblemDetailsService _problemDetailsService;
1615

17-
public DatabaseAccessExceptionHandler(IProblemDetailsService problemDetailsService)
16+
public HttpRequestExecutionExceptionHandler(IProblemDetailsService problemDetailsService)
1817
{
1918
_problemDetailsService = problemDetailsService;
2019
}
@@ -25,27 +24,21 @@ public DatabaseAccessExceptionHandler(IProblemDetailsService problemDetailsServi
2524
// See: https://www.milanjovanovic.tech/blog/problem-details-for-aspnetcore-apis#handling-specific-exceptions-status-codes
2625
private async ValueTask<bool> TryHandleWithProblemDetails(HttpContext httpContext, Exception exception)
2726
{
28-
bool result = TryHandle(httpContext, exception, out HttpRequestExecutionException? httpRequestExecutionException);
27+
bool result = TryHandle(httpContext, exception);
2928
if (!result)
3029
return false;
3130

3231
ProblemDetailsContext problemDetailsContext = new ProblemDetailsContext
3332
{
3433
HttpContext = httpContext,
35-
Exception = httpRequestExecutionException
34+
Exception = exception
3635
};
3736
return await _problemDetailsService.TryWriteAsync(problemDetailsContext).ConfigureAwait(false);
3837
}
3938

40-
private static bool TryHandle(HttpContext httpContext, Exception exception, out HttpRequestExecutionException? httpRequestExecutionException)
39+
private static bool TryHandle(HttpContext httpContext, Exception exception)
4140
{
42-
if (exception is not DatabaseAccessException databaseAccessException)
43-
{
44-
httpRequestExecutionException = null;
45-
return false;
46-
}
47-
48-
if (!SqlHttpStatusCodeParser.TryParse(databaseAccessException, out httpRequestExecutionException))
41+
if (exception is not HttpRequestExecutionException httpRequestExecutionException)
4942
return false;
5043

5144
httpContext.Response.StatusCode = (int)httpRequestExecutionException.StatusCode;

src/Dibix.Http.Server.AspNet/HttpActionInvoker.cs

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -12,23 +12,7 @@ public class HttpActionInvoker : HttpActionInvokerBase
1212
public static async Task<object> Invoke(HttpActionDefinition action, HttpRequestMessage request, IDictionary<string, object> arguments, IControllerActivator controllerActivator, IParameterDependencyResolver parameterDependencyResolver, CancellationToken cancellationToken)
1313
{
1414
IHttpResponseFormatter<HttpRequestMessageDescriptor> responseFormatter = new HttpResponseMessageFormatter();
15-
try
16-
{
17-
return await Invoke(action, new HttpRequestMessageDescriptor(request), responseFormatter, arguments, controllerActivator, parameterDependencyResolver, cancellationToken).ConfigureAwait(false);
18-
}
19-
catch (DatabaseAccessException exception)
20-
{
21-
// Sample:
22-
// THROW 404017, N'Feature not configured', 1
23-
// 404017 => 404 17 => HttpStatusCode.NotFound (ResultCode: 17) - ResultCode can be a more specific application/feature error code
24-
//
25-
// HTTP/1.1 404 NotFound
26-
// X-Result-Code: 17
27-
if (SqlHttpStatusCodeParser.TryParse(exception, action, arguments, out HttpRequestExecutionException httpException))
28-
throw httpException;
29-
30-
throw;
31-
}
15+
return await Invoke(action, new HttpRequestMessageDescriptor(request), responseFormatter, arguments, controllerActivator, parameterDependencyResolver, cancellationToken).ConfigureAwait(false);
3216
}
3317
}
3418
}

src/Dibix.Http.Server/Runtime/HttpActionInvoker.cs renamed to src/Dibix.Http.Server/Runtime/HttpActionInvokerBase.cs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,27 @@ namespace Dibix.Http.Server
88
public abstract class HttpActionInvokerBase
99
{
1010
protected static async Task<object> Invoke<TRequest>(HttpActionDefinition action, TRequest request, IHttpResponseFormatter<TRequest> responseFormatter, IDictionary<string, object> arguments, IControllerActivator controllerActivator, IParameterDependencyResolver parameterDependencyResolver, CancellationToken cancellationToken) where TRequest : IHttpRequestDescriptor
11+
{
12+
try
13+
{
14+
return await InvokeCore(action, request, responseFormatter, arguments, controllerActivator, parameterDependencyResolver, cancellationToken).ConfigureAwait(false);
15+
}
16+
catch (DatabaseAccessException exception)
17+
{
18+
// Sample:
19+
// THROW 404017, N'Feature not configured', 1
20+
// 404017 => 404 17 => HttpStatusCode.NotFound (ResultCode: 17) - ResultCode can be a more specific application/feature error code
21+
//
22+
// HTTP/1.1 404 NotFound
23+
// X-Result-Code: 17
24+
if (SqlHttpStatusCodeParser.TryParse(exception, action, arguments, out HttpRequestExecutionException httpException))
25+
throw httpException;
26+
27+
throw;
28+
}
29+
}
30+
31+
private static async Task<object> InvokeCore<TRequest>(HttpActionDefinition action, TRequest request, IHttpResponseFormatter<TRequest> responseFormatter, IDictionary<string, object> arguments, IControllerActivator controllerActivator, IParameterDependencyResolver parameterDependencyResolver, CancellationToken cancellationToken) where TRequest : IHttpRequestDescriptor
1132
{
1233
if (action.Authorization.Any())
1334
{

src/Dibix.Http.Server/Runtime/SqlHttpStatusCodeParser.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,11 @@ public static bool TryParse(DatabaseAccessException databaseAccessException, out
1212
{
1313
return TryParse(databaseAccessException, action: null, arguments: new Dictionary<string, object>(), out httpException);
1414
}
15-
public static bool TryParse(DatabaseAccessException databaseAccessException, HttpActionDefinition action, IDictionary<string, object> arguments, out HttpRequestExecutionException httpException)
15+
internal static bool TryParse(DatabaseAccessException databaseAccessException, HttpActionDefinition action, IDictionary<string, object> arguments, out HttpRequestExecutionException httpException)
1616
{
1717
return TryParse(databaseAccessException, databaseAccessException.InnerException as SqlException, action, arguments, out httpException);
1818
}
19-
private static bool TryParse(DatabaseAccessException originalException, SqlException rootException, HttpActionDefinition action, IDictionary<string, object> arguments, out HttpRequestExecutionException httpException)
19+
private static bool TryParse(DatabaseAccessException originalException, SqlException rootException, HttpActionDefinition action, IDictionary<string, object> arguments, out HttpRequestExecutionException httpException)
2020
{
2121
if (rootException != null && HttpErrorResponseUtility.TryParseErrorResponse(rootException.Number, out int statusCode, out int errorCode, out bool isClientError))
2222
{

tests/Dibix.Http.Server.Tests/HttpActionInvokerTest.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ public async Task Invoke_DDL_WithHttpClientError_ProducedByAuthorizationBehavior
102102
await Execute(action, request.Object, responseFormatter.Object, new KeyValuePair<string, object>("context", httpAuthorizationBehaviorContext)).ConfigureAwait(false);
103103
Assert.Fail($"{nameof(HttpRequestExecutionException)} was expected but not thrown");
104104
}
105-
catch (DatabaseAccessException databaseAccessException) when (SqlHttpStatusCodeParser.TryParse(databaseAccessException, action, new Dictionary<string, object>(), out HttpRequestExecutionException requestException))
105+
catch (HttpRequestExecutionException requestException)
106106
{
107107
Assert.AreEqual("FirstAuthorizationTargetCalled", httpAuthorizationBehaviorContext.Result);
108108
requestException.AppendToResponse(response.Object);

0 commit comments

Comments
 (0)