Skip to content

Instantly share code, notes, and snippets.

@nathan-alden-sr
Last active November 5, 2021 02:57
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nathan-alden-sr/ef61f7ee5bff54bf11d724b1ef8ac51d to your computer and use it in GitHub Desktop.
Save nathan-alden-sr/ef61f7ee5bff54bf11d724b1ef8ac51d to your computer and use it in GitHub Desktop.
ASP.NET Core 6: Standard responses, Swashbuckle filters, 400 handler, 500 handler
using Microsoft.AspNetCore.Mvc;
public static class InvalidModelStateResponseHandler
{
public static IStandardResponse<IEnumerable<InvalidModelStateResponse>> Handle(ActionContext context)
{
ThrowIfNull(context, nameof(context));
var hostEnvironment = context.HttpContext.RequestServices.GetService<IHostEnvironment>();
IEnumerable<InvalidModelStateResponse> errors =
context
.ModelState.Values
.SelectMany(a => a.Errors)
.Select(
a =>
hostEnvironment.IsDevelopment()
? new InvalidModelStateWithExceptionResponse(a.ErrorMessage, a.Exception)
: new InvalidModelStateResponse(a.ErrorMessage));
return StandardResponse.BadRequest(errors, "Request model is invalid.");
}
}
public class InvalidModelStateResponse
{
public InvalidModelStateResponse(string message)
{
Message = message;
}
public string Message { get; }
}
public sealed class InvalidModelStateWithExceptionResponse : InvalidModelStateResponse
{
public InvalidModelStateWithExceptionResponse(string message, Exception? exception) : base(message)
{
Exception = CreateExceptionResponse(exception);
}
public InvalidModelStateExceptionResponse? Exception { get; }
private static InvalidModelStateExceptionResponse? CreateExceptionResponse(Exception? exception) =>
exception is not null
? new InvalidModelStateExceptionResponse(exception.Message, exception.StackTrace, CreateExceptionResponse(exception.InnerException))
: null;
}
public sealed record InvalidModelStateExceptionResponse(string Message, string? StackTrace, InvalidModelStateExceptionResponse? InnerException);
using Microsoft.AspNetCore.Mvc;
public interface IStandardResponse<out T> : IActionResult
{
T? Data { get; }
string? Message { get; }
}
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;
public sealed class NullableSchemaFilter : ISchemaFilter
{
public void Apply(OpenApiSchema schema, SchemaFilterContext context)
{
if (context.MemberInfo?.IsNonNullableReferenceType() != true)
{
return;
}
schema.Nullable = false;
if (schema.Items is not null)
{
schema.Items.Nullable = false;
}
}
}
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Primitives;
public static class StandardResponse
{
// 100 Continue
public static StandardResponse<T> Continue<T>(T? data = default, string? message = null, Action<ActionContext>? formattingDelegate = null) =>
new(StatusCodes.Status100Continue, data, message, formattingDelegate);
public static StandardResponse<object?> Continue(Action<ActionContext>? formattingDelegate = null) =>
Continue<object?>(null, null, formattingDelegate);
// 200 OK
public static StandardResponse<T> Ok<T>(T? data = default, string? message = null, Action<ActionContext>? formattingDelegate = null) =>
new(StatusCodes.Status200OK, data, message, formattingDelegate);
public static StandardResponse<object?> Ok(Action<ActionContext>? formattingDelegate = null) =>
Ok<object?>(null, null, formattingDelegate);
// 201 Created
public static StandardResponse<T> Created<T>(
string location,
T? data = default,
string? message = null,
Action<ActionContext>? formattingDelegate = null) =>
new(
StatusCodes.Status201Created,
data,
message,
context =>
{
context.HttpContext.Response.Headers.Location = location;
formattingDelegate?.Invoke(context);
});
public static StandardResponse<object?> Created(string location, Action<ActionContext>? formattingDelegate = null) =>
Created<object?>(location, null, null, formattingDelegate);
public static StandardResponse<T> Created<T>(Uri location, T? data = default, string? message = null, Action<ActionContext>? formattingDelegate = null) =>
Created(location.ToString(), data, message, formattingDelegate);
public static StandardResponse<object?> Created(Uri location, Action<ActionContext>? formattingDelegate = null) =>
Created<object?>(location, null, null, formattingDelegate);
// 202 Accepted
public static StandardResponse<T> Accepted<T>(
string? location = null,
T? data = default,
string? message = null,
Action<ActionContext>? formattingDelegate = null) =>
new(
StatusCodes.Status202Accepted,
data,
message,
context =>
{
if (location is not null)
{
context.HttpContext.Response.Headers.Location = location;
}
formattingDelegate?.Invoke(context);
});
public static StandardResponse<object?> Accepted(string? location = null, Action<ActionContext>? formattingDelegate = null) =>
Accepted<object?>(location, null, null, formattingDelegate);
public static StandardResponse<T> Accepted<T>(
Uri? location = null,
T? data = default,
string? message = null,
Action<ActionContext>? formattingDelegate = null) =>
Accepted(location?.ToString(), data, message, formattingDelegate);
public static StandardResponse<object?> Accepted(Uri? location = null, Action<ActionContext>? formattingDelegate = null) =>
Accepted<object?>(location, null, null, formattingDelegate);
// 204 No Content
public static NoContentResponse NoContent(Action<ActionContext>? formattingDelegate = null) =>
new(formattingDelegate);
// 300 Multiple Choices
public static StandardResponse<T> MultipleChoices<T>(
string? location,
T? data = default,
string? message = null,
Action<ActionContext>? formattingDelegate = null) =>
new(
StatusCodes.Status300MultipleChoices,
data,
message,
context =>
{
context.HttpContext.Response.Headers.Location = location;
formattingDelegate?.Invoke(context);
});
public static StandardResponse<object?> MultipleChoices(string? location, Action<ActionContext>? formattingDelegate = null) =>
MultipleChoices<object?>(location, null, null, formattingDelegate);
public static StandardResponse<T> MultipleChoices<T>(
Uri? location,
T? data = default,
string? message = null,
Action<ActionContext>? formattingDelegate = null) =>
MultipleChoices(location?.ToString(), data, message, formattingDelegate);
public static StandardResponse<object?> MultipleChoices(Uri? location, Action<ActionContext>? formattingDelegate = null) =>
MultipleChoices<object?>(location, null, null, formattingDelegate);
// 301 Moved Permanently
public static StandardResponse<T> MovedPermanently<T>(
string location,
T? data = default,
string? message = null,
Action<ActionContext>? formattingDelegate = null) =>
new(
StatusCodes.Status301MovedPermanently,
data,
message,
context =>
{
context.HttpContext.Response.Headers.Location = location;
formattingDelegate?.Invoke(context);
});
public static StandardResponse<object?> MovedPermanently(string location, Action<ActionContext>? formattingDelegate = null) =>
MovedPermanently<object?>(location, null, null, formattingDelegate);
public static StandardResponse<T> MovedPermanently<T>(
Uri location,
T? data = default,
string? message = null,
Action<ActionContext>? formattingDelegate = null) =>
MovedPermanently(location.ToString(), data, message, formattingDelegate);
public static StandardResponse<object?> MovedPermanently(Uri location, Action<ActionContext>? formattingDelegate = null) =>
MovedPermanently<object?>(location, null, null, formattingDelegate);
// 302 Found
public static StandardResponse<T> Found<T>(string location, T? data = default, string? message = null, Action<ActionContext>? formattingDelegate = null) =>
new(
StatusCodes.Status302Found,
data,
message,
context =>
{
context.HttpContext.Response.Headers.Location = location;
formattingDelegate?.Invoke(context);
});
public static StandardResponse<object?> Found(string location, Action<ActionContext>? formattingDelegate = null) =>
Found<object?>(location, null, null, formattingDelegate);
public static StandardResponse<T> Found<T>(Uri location, T? data = default, string? message = null, Action<ActionContext>? formattingDelegate = null) =>
Found(location.ToString(), data, message, formattingDelegate);
public static StandardResponse<object?> Found(Uri location, Action<ActionContext>? formattingDelegate = null) =>
Found<object?>(location, null, null, formattingDelegate);
// 303 See Other
public static StandardResponse<T> SeeOther<T>(
string location,
T? data = default,
string? message = null,
Action<ActionContext>? formattingDelegate = null) =>
new(
StatusCodes.Status303SeeOther,
data,
message,
context =>
{
context.HttpContext.Response.Headers.Location = location;
formattingDelegate?.Invoke(context);
});
public static StandardResponse<object?> SeeOther(string location, Action<ActionContext>? formattingDelegate = null) =>
SeeOther<object?>(location, null, null, formattingDelegate);
public static StandardResponse<T> SeeOther<T>(Uri location, T? data = default, string? message = null, Action<ActionContext>? formattingDelegate = null) =>
SeeOther(location.ToString(), data, message, formattingDelegate);
public static StandardResponse<object?> SeeOther(Uri location, Action<ActionContext>? formattingDelegate = null) =>
SeeOther<object?>(location, null, null, formattingDelegate);
// 304 Not Modified
public static StandardResponse<T> NotModified<T>(T? data = default, string? message = null, Action<ActionContext>? formattingDelegate = null) =>
new(StatusCodes.Status304NotModified, data, message, formattingDelegate);
public static StandardResponse<object?> NotModified(Action<ActionContext>? formattingDelegate = null) =>
NotModified<object?>(null, null, formattingDelegate);
// 307 Temporary Redirect
public static StandardResponse<T> TemporaryRedirect<T>(
string location,
T? data = default,
string? message = null,
Action<ActionContext>? formattingDelegate = null) =>
new(
StatusCodes.Status307TemporaryRedirect,
data,
message,
context =>
{
context.HttpContext.Response.Headers.Location = location;
formattingDelegate?.Invoke(context);
});
public static StandardResponse<object?> TemporaryRedirect(string location, Action<ActionContext>? formattingDelegate = null) =>
TemporaryRedirect<object?>(location, null, null, formattingDelegate);
public static StandardResponse<T> TemporaryRedirect<T>(
Uri location,
T? data = default,
string? message = null,
Action<ActionContext>? formattingDelegate = null) =>
TemporaryRedirect(location.ToString(), data, message, formattingDelegate);
public static StandardResponse<object?> TemporaryRedirect(Uri location, Action<ActionContext>? formattingDelegate = null) =>
TemporaryRedirect<object?>(location, null, null, formattingDelegate);
// 308 Permanent Redirect
public static StandardResponse<T> PermanentRedirect<T>(
string location,
T? data = default,
string? message = null,
Action<ActionContext>? formattingDelegate = null) =>
new(
StatusCodes.Status308PermanentRedirect,
data,
message,
context =>
{
context.HttpContext.Response.Headers.Location = location;
formattingDelegate?.Invoke(context);
});
public static StandardResponse<object?> PermanentRedirect(string location, Action<ActionContext>? formattingDelegate = null) =>
PermanentRedirect<object?>(location, null, null, formattingDelegate);
public static StandardResponse<T> PermanentRedirect<T>(
Uri location,
T? data = default,
string? message = null,
Action<ActionContext>? formattingDelegate = null) =>
PermanentRedirect(location.ToString(), data, message, formattingDelegate);
public static StandardResponse<object?> PermanentRedirect(Uri location, Action<ActionContext>? formattingDelegate = null) =>
PermanentRedirect<object?>(location, null, null, formattingDelegate);
// 400 Bad Request
public static StandardResponse<T> BadRequest<T>(T? data = default, string? message = null, Action<ActionContext>? formattingDelegate = null) =>
new(StatusCodes.Status400BadRequest, data, message, formattingDelegate);
public static StandardResponse<object?> BadRequest(Action<ActionContext>? formattingDelegate = null) =>
BadRequest<object?>(null, null, formattingDelegate);
// 401 Unauthorized
public static StandardResponse<T> Unauthorized<T>(
StringValues wwwAuthenticate,
T? data = default,
string? message = null,
Action<ActionContext>? formattingDelegate = null) =>
new(
StatusCodes.Status401Unauthorized,
data,
message,
context =>
{
context.HttpContext.Response.Headers.WWWAuthenticate = wwwAuthenticate;
formattingDelegate?.Invoke(context);
});
public static StandardResponse<object?> Unauthorized(StringValues wwwAuthenticate, Action<ActionContext>? formattingDelegate = null) =>
Unauthorized<object?>(wwwAuthenticate, formattingDelegate);
// 403 Forbidden
public static StandardResponse<T> Forbidden<T>(T? data = default, string? message = null, Action<ActionContext>? formattingDelegate = null) =>
new(StatusCodes.Status403Forbidden, data, message, formattingDelegate);
public static StandardResponse<object?> Forbidden(Action<ActionContext>? formattingDelegate = null) =>
Forbidden<object?>(null, null, formattingDelegate);
// 404 Not Found
public static StandardResponse<T> NotFound<T>(T? data = default, string? message = null, Action<ActionContext>? formattingDelegate = null) =>
new(StatusCodes.Status404NotFound, data, message, formattingDelegate);
public static StandardResponse<object?> NotFound(Action<ActionContext>? formattingDelegate = null) =>
NotFound<object?>(null, null, formattingDelegate);
// 405 Not Found
public static StandardResponse<T> MethodNotAllowed<T>(T? data = default, string? message = null, Action<ActionContext>? formattingDelegate = null) =>
new(StatusCodes.Status405MethodNotAllowed, data, message, formattingDelegate);
public static StandardResponse<object?> MethodNotAllowed(Action<ActionContext>? formattingDelegate = null) =>
MethodNotAllowed<object?>(null, null, formattingDelegate);
// 406 Not Acceptable
public static StandardResponse<T> NotAcceptable<T>(T? data = default, string? message = null, Action<ActionContext>? formattingDelegate = null) =>
new(StatusCodes.Status406NotAcceptable, data, message, formattingDelegate);
public static StandardResponse<object?> NotAcceptable(Action<ActionContext>? formattingDelegate = null) =>
NotAcceptable<object?>(null, null, formattingDelegate);
// 407 Proxy Authentication Required
public static StandardResponse<T> ProxyAuthenticationRequired<T>(
StringValues proxyAuthenticate,
T? data = default,
string? message = null,
Action<ActionContext>? formattingDelegate = null) =>
new(
StatusCodes.Status407ProxyAuthenticationRequired,
data,
message,
context =>
{
context.HttpContext.Response.Headers.ProxyAuthenticate = proxyAuthenticate;
formattingDelegate?.Invoke(context);
});
public static StandardResponse<object?> ProxyAuthenticationRequired(StringValues proxyAuthenticate, Action<ActionContext>? formattingDelegate = null) =>
ProxyAuthenticationRequired<object?>(proxyAuthenticate, null, null, formattingDelegate);
// 408 Request Timeout
public static StandardResponse<T> RequestTimeout<T>(
bool closeConnection = true,
T? data = default,
string? message = null,
Action<ActionContext>? formattingDelegate = null) =>
new(
StatusCodes.Status408RequestTimeout,
data,
message,
context =>
{
if (closeConnection)
{
context.HttpContext.Response.Headers.Connection = "close";
}
formattingDelegate?.Invoke(context);
});
public static StandardResponse<object?> RequestTimeout(bool closeConnection = true, Action<ActionContext>? formattingDelegate = null) =>
RequestTimeout<object?>(closeConnection, null, null, formattingDelegate);
// 409 Conflict
public static StandardResponse<T> Conflict<T>(T? data = default, string? message = null, Action<ActionContext>? formattingDelegate = null) =>
new(StatusCodes.Status409Conflict, data, message, formattingDelegate);
public static StandardResponse<object?> Conflict(Action<ActionContext>? formattingDelegate = null) =>
Conflict<object?>(null, null, formattingDelegate);
// 410 Gone
public static StandardResponse<T> Gone<T>(T? data = default, string? message = null, Action<ActionContext>? formattingDelegate = null) =>
new(StatusCodes.Status410Gone, data, message, formattingDelegate);
public static StandardResponse<object?> Gone(Action<ActionContext>? formattingDelegate = null) =>
Gone<object?>(null, null, formattingDelegate);
// 411 Length Required
public static StandardResponse<T> LengthRequired<T>(T? data = default, string? message = null, Action<ActionContext>? formattingDelegate = null) =>
new(StatusCodes.Status411LengthRequired, data, message, formattingDelegate);
public static StandardResponse<object?> LengthRequired(Action<ActionContext>? formattingDelegate = null) =>
LengthRequired<object?>(null, null, formattingDelegate);
// 412 Preconditon Failed
public static StandardResponse<T> PreconditionFailed<T>(T? data = default, string? message = null, Action<ActionContext>? formattingDelegate = null) =>
new(StatusCodes.Status412PreconditionFailed, data, message, formattingDelegate);
public static StandardResponse<object?> PreconditionFailed(Action<ActionContext>? formattingDelegate = null) =>
PreconditionFailed<object?>(null, null, formattingDelegate);
// 413 Payload Too Large
public static StandardResponse<T> PayloadTooLarge<T>(T? data = default, string? message = null, Action<ActionContext>? formattingDelegate = null) =>
new(StatusCodes.Status413PayloadTooLarge, data, message, formattingDelegate);
public static StandardResponse<object?> PayloadTooLarge(Action<ActionContext>? formattingDelegate = null) =>
PayloadTooLarge<object?>(null, null, formattingDelegate);
// 414 Payload Too Large
public static StandardResponse<T> UriTooLong<T>(T? data = default, string? message = null, Action<ActionContext>? formattingDelegate = null) =>
new(StatusCodes.Status414UriTooLong, data, message, formattingDelegate);
public static StandardResponse<object?> UriTooLong(Action<ActionContext>? formattingDelegate = null) =>
UriTooLong<object?>(null, null, formattingDelegate);
// 415 Unsupported Media Type
public static StandardResponse<T> UnsupportedMediaType<T>(T? data = default, string? message = null, Action<ActionContext>? formattingDelegate = null) =>
new(StatusCodes.Status415UnsupportedMediaType, data, message, formattingDelegate);
public static StandardResponse<object?> UnsupportedMediaType(Action<ActionContext>? formattingDelegate = null) =>
UnsupportedMediaType<object?>(null, null, formattingDelegate);
// 416 Range Not Satisfiable
public static StandardResponse<T> RangeNotSatisfiable<T>(
StringValues contentRange,
T? data = default,
string? message = null,
Action<ActionContext>? formattingDelegate = null) =>
new(
StatusCodes.Status416RangeNotSatisfiable,
data,
message,
context =>
{
context.HttpContext.Response.Headers.ContentRange = contentRange;
formattingDelegate?.Invoke(context);
});
public static StandardResponse<object?> RangeNotSatisfiable(StringValues contentRange, Action<ActionContext>? formattingDelegate = null) =>
RangeNotSatisfiable<object?>(contentRange, null, null, formattingDelegate);
// 417 Expectation Failed
public static StandardResponse<T> ExpectationFailed<T>(T? data = default, string? message = null, Action<ActionContext>? formattingDelegate = null) =>
new(StatusCodes.Status417ExpectationFailed, data, message, formattingDelegate);
public static StandardResponse<object?> ExpectationFailed(Action<ActionContext>? formattingDelegate = null) =>
ExpectationFailed<object?>(null, null, formattingDelegate);
// 418 I'm a Teapot
public static StandardResponse<T> ImATeapot<T>(T? data = default, string? message = null, Action<ActionContext>? formattingDelegate = null) =>
new(StatusCodes.Status418ImATeapot, data, message, formattingDelegate);
public static StandardResponse<object?> ImATeapot(Action<ActionContext>? formattingDelegate = null) =>
ImATeapot<object?>(null, null, formattingDelegate);
// 428 Precondition Required
public static StandardResponse<T> PreconditionRequired<T>(T? data = default, string? message = null, Action<ActionContext>? formattingDelegate = null) =>
new(StatusCodes.Status428PreconditionRequired, data, message, formattingDelegate);
public static StandardResponse<object?> PreconditionRequired(Action<ActionContext>? formattingDelegate = null) =>
PreconditionRequired<object?>(null, null, formattingDelegate);
// 431 Request Header Fields Too Large
public static StandardResponse<T> RequestHeaderFieldsTooLarge<T>(
T? data = default,
string? message = null,
Action<ActionContext>? formattingDelegate = null) =>
new(StatusCodes.Status431RequestHeaderFieldsTooLarge, data, message, formattingDelegate);
public static StandardResponse<object?> RequestHeaderFieldsTooLarge(Action<ActionContext>? formattingDelegate = null) =>
RequestHeaderFieldsTooLarge<object?>(null, null, formattingDelegate);
// 451 Unavailable for Legal Reasons
public static StandardResponse<T> UnavailableForLegalReasons<T>(
T? data = default,
string? message = null,
Action<ActionContext>? formattingDelegate = null) =>
new(
StatusCodes.Status451UnavailableForLegalReasons,
data,
message,
formattingDelegate);
public static StandardResponse<object?> UnavailableForLegalReasons(Action<ActionContext>? formattingDelegate = null) =>
UnavailableForLegalReasons<object?>(null, null, formattingDelegate);
// 500 Internal Server Error
public static StandardResponse<T> InternalServerError<T>(T? data = default, string? message = null, Action<ActionContext>? formattingDelegate = null) =>
new(StatusCodes.Status500InternalServerError, data, message, formattingDelegate);
public static StandardResponse<object?> InternalServerError(Action<ActionContext>? formattingDelegate = null) =>
InternalServerError<object?>(null, null, formattingDelegate);
// 501 Not Implemented
public static StandardResponse<T> NotImplemented<T>(
StringValues? retryAfter = null,
T? data = default,
string? message = null,
Action<ActionContext>? formattingDelegate = null) =>
new(
StatusCodes.Status501NotImplemented,
data,
message,
context =>
{
if (retryAfter is not null)
{
context.HttpContext.Response.Headers.RetryAfter = retryAfter.Value;
}
formattingDelegate?.Invoke(context);
});
public static StandardResponse<object?> NotImplemented(StringValues? retryAfter = null, Action<ActionContext>? formattingDelegate = null) =>
NotImplemented<object?>(retryAfter, null, null, formattingDelegate);
// 502 Bad Gateway
public static StandardResponse<T> BadGateway<T>(T? data = default, string? message = null, Action<ActionContext>? formattingDelegate = null) =>
new(StatusCodes.Status502BadGateway, data, message, formattingDelegate);
public static StandardResponse<object?> BadGateway(Action<ActionContext>? formattingDelegate = null) =>
BadGateway<object?>(null, null, formattingDelegate);
// 503 Service Unavailable
public static StandardResponse<T> ServiceUnavailable<T>(T? data = default, string? message = null, Action<ActionContext>? formattingDelegate = null) =>
new(StatusCodes.Status503ServiceUnavailable, data, message, formattingDelegate);
public static StandardResponse<object?> ServiceUnavailable(Action<ActionContext>? formattingDelegate = null) =>
ServiceUnavailable<object?>(null, null, formattingDelegate);
// 504 Gateway Timeout
public static StandardResponse<T> GatewayTimeout<T>(T? data = default, string? message = null, Action<ActionContext>? formattingDelegate = null) =>
new(StatusCodes.Status504GatewayTimeout, data, message, formattingDelegate);
public static StandardResponse<object?> GatewayTimeout(Action<ActionContext>? formattingDelegate = null) =>
GatewayTimeout<object?>(null, null, formattingDelegate);
// 505 HTTP Version Not Supported
public static StandardResponse<T> HttpVersionNotSupported<T>(T? data = default, string? message = null, Action<ActionContext>? formattingDelegate = null) =>
new(StatusCodes.Status505HttpVersionNotsupported, data, message, formattingDelegate);
public static StandardResponse<object?> HttpVersionNotSupported(Action<ActionContext>? formattingDelegate = null) =>
HttpVersionNotSupported<object?>(null, null, formattingDelegate);
// 506 Variant Also Negotiates
public static StandardResponse<T> VariantAlsoNegotiates<T>(T? data = default, string? message = null, Action<ActionContext>? formattingDelegate = null) =>
new(StatusCodes.Status506VariantAlsoNegotiates, data, message, formattingDelegate);
public static StandardResponse<object?> VariantAlsoNegotiates(Action<ActionContext>? formattingDelegate = null) =>
VariantAlsoNegotiates<object?>(null, null, formattingDelegate);
// 510 Not Extended
public static StandardResponse<T> NotExtended<T>(T? data = default, string? message = null, Action<ActionContext>? formattingDelegate = null) =>
new(StatusCodes.Status510NotExtended, data, message, formattingDelegate);
public static StandardResponse<object?> NotExtended(Action<ActionContext>? formattingDelegate = null) =>
NotExtended<object?>(null, null, formattingDelegate);
// 511 Network Authentication Required
public static StandardResponse<T> NetworkAuthenticationRequired<T>(
T? data = default,
string? message = null,
Action<ActionContext>? formattingDelegate = null) =>
new(StatusCodes.Status511NetworkAuthenticationRequired, data, message, formattingDelegate);
public static StandardResponse<object?> NetworkAuthenticationRequired(Action<ActionContext>? formattingDelegate = null) =>
NetworkAuthenticationRequired<object?>(null, null, formattingDelegate);
}
using Microsoft.AspNetCore.Mvc;
public sealed class StandardResponse<T> : ObjectResult, IStandardResponse<T>
{
private readonly Action<ActionContext>? _formattingDelegate;
public StandardResponse(int statusCode, T? data = default, string? message = null, Action<ActionContext>? formattingDelegate = null)
: base(
new
{
Data = data,
Message = message
})
{
StatusCode = statusCode;
Data = data;
Message = message;
_formattingDelegate = formattingDelegate;
}
public T? Data { get; }
public string? Message { get; }
public override void OnFormatting(ActionContext context)
{
ThrowIfNull(context, nameof(context));
if (StatusCode is not null)
{
context.HttpContext.Response.StatusCode = StatusCode.Value;
}
_formattingDelegate?.Invoke(context);
base.OnFormatting(context);
}
}
// Copyright (c) Smoky Peak Software, LLC.
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;
public sealed class StandardResponseFilter : ISchemaFilter
{
public void Apply(OpenApiSchema schema, SchemaFilterContext context)
{
Type type = context.Type;
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IStandardResponse<>))
{
schema.Title = $"IStandardResponse<{type.GetGenericArguments()[0].Name}>";
}
}
}
using Microsoft.AspNetCore.Diagnostics;
using Microsoft.AspNetCore.Http.Json;
using Microsoft.Extensions.Options;
public static class UnhandledExceptionHandler
{
public static async Task Handle(HttpContext context)
{
ThrowIfNull(context, nameof(context));
var hostEnvironment = context.RequestServices.GetService<IHostEnvironment>();
var jsonOptions = context.RequestServices.GetService<IOptions<JsonOptions>>();
var exceptionHandlerFeature = context.Features.Get<IExceptionHandlerFeature>();
AssertNotNull(hostEnvironment);
AssertNotNull(jsonOptions);
AssertNotNull(exceptionHandlerFeature);
static UnhandledExceptionResponse? CreateUnhandledExceptionResponse(Exception? exception) =>
exception is not null
? new UnhandledExceptionResponse(exception.Message, exception.StackTrace, CreateUnhandledExceptionResponse(exception.InnerException))
: null;
StandardResponse<UnhandledExceptionResponse> response =
StandardResponse.InternalServerError(
hostEnvironment.IsDevelopment()
? CreateUnhandledExceptionResponse(exceptionHandlerFeature.Error)
: null,
"An unhandled exception has occurred.");
context.Response.StatusCode = response.StatusCode ?? StatusCodes.Status500InternalServerError;
await context.Response.WriteAsJsonAsync(response.Value, jsonOptions.Value.SerializerOptions);
}
}
public sealed record UnhandledExceptionResponse(string Message, string? StackTrace, UnhandledExceptionResponse? InnerException);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment