Last active
November 5, 2021 02:57
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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."); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using Microsoft.AspNetCore.Mvc; | |
public interface IStandardResponse<out T> : IActionResult | |
{ | |
T? Data { get; } | |
string? Message { get; } | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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}>"; | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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