-
Notifications
You must be signed in to change notification settings - Fork 178
/
Copy pathExceptionHandlingMiddleware.cs
145 lines (132 loc) · 5.88 KB
/
ExceptionHandlingMiddleware.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
// -------------------------------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information.
// -------------------------------------------------------------------------------------------------
using System;
using System.Net;
using System.Runtime.ExceptionServices;
using System.Threading.Tasks;
using EnsureThat;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Microsoft.Health.Abstractions.Exceptions;
using Microsoft.Health.Api.Features.Audit;
using Microsoft.Health.Dicom.Core.Exceptions;
using NotSupportedException = Microsoft.Health.Dicom.Core.Exceptions.NotSupportedException;
namespace Microsoft.Health.Dicom.Api.Features.Exceptions
{
public class ExceptionHandlingMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger<ExceptionHandlingMiddleware> _logger;
public ExceptionHandlingMiddleware(RequestDelegate next, ILogger<ExceptionHandlingMiddleware> logger)
{
EnsureArg.IsNotNull(next, nameof(next));
EnsureArg.IsNotNull(logger, nameof(logger));
_next = next;
_logger = logger;
}
public async Task Invoke(HttpContext context)
{
EnsureArg.IsNotNull(context, nameof(context));
ExceptionDispatchInfo exceptionDispatchInfo = null;
try
{
await _next(context);
}
catch (Exception exception)
{
if (context.Response.HasStarted)
{
_logger.LogWarning("The response has already started, the base exception middleware will not be executed.");
throw;
}
// Get the Exception, but don't continue processing in the catch block as its bad for stack usage.
exceptionDispatchInfo = ExceptionDispatchInfo.Capture(exception);
}
if (exceptionDispatchInfo != null)
{
IActionResult result = MapExceptionToResult(exceptionDispatchInfo.SourceException);
await ExecuteResultAsync(context, result);
}
}
private IActionResult MapExceptionToResult(Exception exception)
{
HttpStatusCode statusCode = HttpStatusCode.InternalServerError;
string message = exception.Message;
switch (exception)
{
case ValidationException _:
case NotSupportedException _:
case AuditHeaderCountExceededException _:
case AuditHeaderTooLargeException _:
statusCode = HttpStatusCode.BadRequest;
break;
case ResourceNotFoundException _:
statusCode = HttpStatusCode.NotFound;
break;
case NotAcceptableException _:
case TranscodingException _:
statusCode = HttpStatusCode.NotAcceptable;
break;
case DataStoreException _:
statusCode = HttpStatusCode.ServiceUnavailable;
break;
case InstanceAlreadyExistsException _:
case ExtendedQueryTagsAlreadyExistsException _:
case ExtendedQueryTagsOutOfDateException _:
statusCode = HttpStatusCode.Conflict;
break;
case UnsupportedMediaTypeException _:
statusCode = HttpStatusCode.UnsupportedMediaType;
break;
case ServiceUnavailableException _:
statusCode = HttpStatusCode.ServiceUnavailable;
break;
case ItemNotFoundException _:
// One of the required resources is missing.
statusCode = HttpStatusCode.InternalServerError;
break;
case UnauthorizedDicomActionException udae:
_logger.LogInformation("Expected data actions not available: {DataActions}", udae.ExpectedDataActions);
statusCode = HttpStatusCode.Forbidden;
break;
case DicomServerException _:
statusCode = HttpStatusCode.ServiceUnavailable;
break;
}
// Log the exception and possibly modify the user message
switch (statusCode)
{
case HttpStatusCode.ServiceUnavailable:
_logger.LogWarning(exception, "Service exception.");
break;
case HttpStatusCode.InternalServerError:
// In the case of InternalServerError, make sure to overwrite the message to
// avoid internal message.
_logger.LogCritical(exception, "Unexpected service exception.");
message = DicomApiResource.InternalServerError;
break;
default:
_logger.LogWarning(exception, "Unhandled exception");
break;
}
return GetContentResult(statusCode, message);
}
private static IActionResult GetContentResult(HttpStatusCode statusCode, string message)
{
return new ContentResult
{
StatusCode = (int)statusCode,
Content = message,
};
}
protected internal virtual async Task ExecuteResultAsync(HttpContext context, IActionResult result)
{
EnsureArg.IsNotNull(context, nameof(context));
EnsureArg.IsNotNull(result, nameof(result));
await result.ExecuteResultAsync(new ActionContext { HttpContext = context });
}
}
}