Last active
December 14, 2022 23:33
-
-
Save Swimburger/2ed883c2aa9e5cae2343527ccf9227b2 to your computer and use it in GitHub Desktop.
Two different ways of doing catch-all exception handling, one for MVC only using an exception filter, one using exception handler middleware
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.AspNetCore.Mvc.Filters; | |
using Twilio.AspNet.Core; | |
using Twilio.TwiML; | |
namespace TwimlErrorMessageAttribute.Controllers; | |
[Route("twilio")] | |
public class TwilioController : Controller | |
{ | |
[GenericErrorTwimlMessage] | |
[Route("message")] | |
public IActionResult Message() | |
{ | |
var zero = 0; | |
var result = 1 / zero; | |
return new MessagingResponse() | |
.Message($"1/0 is {result}!") | |
.ToTwiMLResult(); | |
} | |
[GenericErrorTwimlVoice] | |
[Route("voice")] | |
public IActionResult Voice() | |
{ | |
var zero = 0; | |
var result = 1 / zero; | |
return new VoiceResponse() | |
.Say($"1/0 is {result}!") | |
.ToTwiMLResult(); | |
} | |
} | |
internal enum ErrorTwimlType | |
{ | |
Message, | |
Voice | |
} | |
public class GenericErrorTwimlMessage : TypeFilterAttribute | |
{ | |
public GenericErrorTwimlMessage() : base(typeof(GenericErrorTwimlExceptionFilter)) | |
{ | |
Arguments = new[] {(object) ErrorTwimlType.Message}; | |
} | |
} | |
public class GenericErrorTwimlVoice : TypeFilterAttribute | |
{ | |
public GenericErrorTwimlVoice() : base(typeof(GenericErrorTwimlExceptionFilter)) | |
{ | |
Arguments = new[] {(object) ErrorTwimlType.Voice}; | |
} | |
} | |
internal class GenericErrorTwimlExceptionFilter : IExceptionFilter | |
{ | |
private const string GenericErrorMessage = "An unexpected error occurred. Please try again."; | |
private readonly ILogger<GenericErrorTwimlExceptionFilter> logger; | |
private readonly ErrorTwimlType twimlType; | |
public GenericErrorTwimlExceptionFilter( | |
ILogger<GenericErrorTwimlExceptionFilter> logger, | |
ErrorTwimlType twimlType | |
) | |
{ | |
this.logger = logger; | |
this.twimlType = twimlType; | |
} | |
public void OnException(ExceptionContext context) | |
{ | |
logger.LogError(context.Exception, "An unhandled exception has occurred while executing the request."); | |
switch (twimlType) | |
{ | |
case ErrorTwimlType.Message: | |
context.Result = new MessagingResponse() | |
.Message(GenericErrorMessage) | |
.ToTwiMLResult(); | |
break; | |
case ErrorTwimlType.Voice: | |
context.Result = new VoiceResponse() | |
.Say(GenericErrorMessage) | |
.ToTwiMLResult(); | |
break; | |
} | |
} | |
} |
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 Twilio.AspNet.Core; | |
using Twilio.TwiML; | |
using TwimlErrorMessageAttribute; | |
var builder = WebApplication.CreateBuilder(args); | |
builder.Services.AddControllers(); | |
var app = builder.Build(); | |
app.UseExceptionHandler("/error"); | |
app.UseHttpsRedirection(); | |
app.UseRouting(); | |
app.MapErrorEndpoint(); | |
app.MapControllers(); | |
app.MapGet("/minimal-message", [CatchWithMessageTwiml]() => | |
{ | |
var zero = 0; | |
var result = 1 / zero; | |
return new MessagingResponse() | |
.Message($"1/0 is {result}!") | |
.ToTwiMLResult(); | |
}); | |
app.Run(); | |
public static class ErrorEndpoint | |
{ | |
private const string GenericErrorMessage = "An unexpected error occurred. Please try again."; | |
public static IEndpointRouteBuilder MapErrorEndpoint(this IEndpointRouteBuilder builder) | |
{ | |
builder.Map("/error", OnError); | |
return builder; | |
} | |
private static IResult OnError(HttpContext context) | |
{ | |
var exceptionFeature = context.Features.Get<IExceptionHandlerFeature>(); | |
if (exceptionFeature?.Endpoint is not null) | |
{ | |
if (exceptionFeature.Endpoint.Metadata.GetMetadata<CatchWithMessageTwimlAttribute>() is not null) | |
return TwimlMessageError(context.Response); | |
if (exceptionFeature.Endpoint.Metadata.GetMetadata<CatchWithVoiceTwimlAttribute>() is not null) | |
return TwimlVoiceError(context.Response); | |
} | |
return StatusCodeError(); | |
} | |
private static IResult StatusCodeError() | |
=> Results.StatusCode(StatusCodes.Status500InternalServerError); | |
private static IResult TwimlMessageError(HttpResponse response) | |
{ | |
response.StatusCode = StatusCodes.Status200OK; | |
return new MessagingResponse() | |
.Message(GenericErrorMessage) | |
.ToTwiMLResult(); | |
} | |
private static IResult TwimlVoiceError(HttpResponse response) | |
{ | |
response.StatusCode = StatusCodes.Status200OK; | |
return new VoiceResponse() | |
.Say(GenericErrorMessage) | |
.ToTwiMLResult(); | |
} | |
} | |
[Route("twilio")] | |
public class TwilioController : Controller | |
{ | |
[CatchWithMessageTwiml] | |
[Route("message")] | |
public IActionResult Message() | |
{ | |
var zero = 0; | |
var result = 1 / zero; | |
return new MessagingResponse() | |
.Message($"1/0 is {result}!") | |
.ToTwiMLResult(); | |
} | |
[CatchWithVoiceTwiml] | |
[Route("voice")] | |
public IActionResult Voice() | |
{ | |
var zero = 0; | |
var result = 1 / zero; | |
return new VoiceResponse() | |
.Say($"1/0 is {result}!") | |
.ToTwiMLResult(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment