22using System . Text . Json ;
33using Microsoft . EntityFrameworkCore ;
44using Microsoft . AspNetCore . Mvc ;
5+ using ProjectVG . Common . Exceptions ;
56
67namespace ProjectVG . Api . Middleware
78{
89 public class GlobalExceptionHandler
910 {
1011 private readonly RequestDelegate _next ;
1112 private readonly ILogger < GlobalExceptionHandler > _logger ;
13+ private readonly IWebHostEnvironment _environment ;
1214
13- public GlobalExceptionHandler ( RequestDelegate next , ILogger < GlobalExceptionHandler > logger )
15+ public GlobalExceptionHandler ( RequestDelegate next , ILogger < GlobalExceptionHandler > logger , IWebHostEnvironment environment )
1416 {
1517 _next = next ;
1618 _logger = logger ;
19+ _environment = environment ;
1720 }
1821
1922 public async Task InvokeAsync ( HttpContext context )
@@ -37,15 +40,16 @@ private async Task HandleExceptionAsync(HttpContext context, Exception exception
3740
3841 var jsonResponse = JsonSerializer . Serialize ( errorResponse , new JsonSerializerOptions
3942 {
40- PropertyNamingPolicy = JsonNamingPolicy . CamelCase
43+ PropertyNamingPolicy = JsonNamingPolicy . CamelCase ,
44+ WriteIndented = _environment . IsDevelopment ( )
4145 } ) ;
4246
4347 await context . Response . WriteAsync ( jsonResponse ) ;
4448 }
4549
4650 private ErrorResponse CreateErrorResponse ( Exception exception , HttpContext context )
4751 {
48- if ( exception is ValidationException validationEx )
52+ if ( exception is ProjectVG . Common . Exceptions . ValidationException validationEx )
4953 {
5054 return HandleValidationException ( validationEx , context ) ;
5155 }
@@ -75,6 +79,31 @@ private ErrorResponse CreateErrorResponse(Exception exception, HttpContext conte
7579 return HandleKeyNotFoundException ( keyNotFoundEx , context ) ;
7680 }
7781
82+ if ( exception is ArgumentException argumentEx )
83+ {
84+ return HandleArgumentException ( argumentEx , context ) ;
85+ }
86+
87+ if ( exception is InvalidOperationException invalidOpEx )
88+ {
89+ return HandleInvalidOperationException ( invalidOpEx , context ) ;
90+ }
91+
92+ if ( exception is UnauthorizedAccessException unauthorizedEx )
93+ {
94+ return HandleUnauthorizedAccessException ( unauthorizedEx , context ) ;
95+ }
96+
97+ if ( exception is TimeoutException timeoutEx )
98+ {
99+ return HandleTimeoutException ( timeoutEx , context ) ;
100+ }
101+
102+ if ( exception is HttpRequestException httpEx )
103+ {
104+ return HandleHttpRequestException ( httpEx , context ) ;
105+ }
106+
78107 return HandleGenericException ( exception , context ) ;
79108 }
80109
@@ -140,23 +169,38 @@ private ErrorResponse HandleExternalServiceException(ExternalServiceException ex
140169
141170 private ErrorResponse HandleDbUpdateException ( DbUpdateException exception , HttpContext context )
142171 {
143- if ( exception . InnerException ? . Message . Contains ( "duplicate" ) == true )
172+ var innerMessage = exception . InnerException ? . Message ? . ToLowerInvariant ( ) ?? string . Empty ;
173+
174+ if ( innerMessage . Contains ( "duplicate" ) || innerMessage . Contains ( "unique" ) )
144175 {
145176 _logger . LogWarning ( exception , "데이터베이스 중복 키 오류 발생" ) ;
146177 return new ErrorResponse
147178 {
148- ErrorCode = "리소스_중복 " ,
179+ ErrorCode = "RESOURCE_CONFLICT " ,
149180 Message = "이미 존재하는 데이터입니다" ,
150181 StatusCode = 409 ,
151182 Timestamp = DateTime . UtcNow ,
152183 TraceId = context . TraceIdentifier
153184 } ;
154185 }
155186
187+ if ( innerMessage . Contains ( "foreign key" ) || innerMessage . Contains ( "constraint" ) )
188+ {
189+ _logger . LogWarning ( exception , "데이터베이스 제약 조건 위반" ) ;
190+ return new ErrorResponse
191+ {
192+ ErrorCode = "CONSTRAINT_VIOLATION" ,
193+ Message = "관련 데이터가 존재하여 삭제할 수 없습니다" ,
194+ StatusCode = 400 ,
195+ Timestamp = DateTime . UtcNow ,
196+ TraceId = context . TraceIdentifier
197+ } ;
198+ }
199+
156200 _logger . LogError ( exception , "데이터베이스 업데이트 오류 발생" ) ;
157201 return new ErrorResponse
158202 {
159- ErrorCode = "데이터베이스_오류 " ,
203+ ErrorCode = "DATABASE_ERROR " ,
160204 Message = "데이터베이스 처리 중 오류가 발생했습니다" ,
161205 StatusCode = ( int ) HttpStatusCode . InternalServerError ,
162206 Timestamp = DateTime . UtcNow ,
@@ -170,26 +214,105 @@ private ErrorResponse HandleKeyNotFoundException(KeyNotFoundException exception,
170214
171215 return new ErrorResponse
172216 {
173- ErrorCode = "리소스_찾을_수_없음 " ,
217+ ErrorCode = "RESOURCE_NOT_FOUND " ,
174218 Message = exception . Message ,
175219 StatusCode = ( int ) HttpStatusCode . NotFound ,
176220 Timestamp = DateTime . UtcNow ,
177221 TraceId = context . TraceIdentifier
178222 } ;
179223 }
180224
225+ private ErrorResponse HandleArgumentException ( ArgumentException exception , HttpContext context )
226+ {
227+ _logger . LogWarning ( exception , "잘못된 인수: {Message}" , exception . Message ) ;
228+
229+ return new ErrorResponse
230+ {
231+ ErrorCode = "INVALID_ARGUMENT" ,
232+ Message = "잘못된 요청 파라미터입니다" ,
233+ StatusCode = ( int ) HttpStatusCode . BadRequest ,
234+ Timestamp = DateTime . UtcNow ,
235+ TraceId = context . TraceIdentifier ,
236+ Details = _environment . IsDevelopment ( ) ? new List < string > { exception . Message } : null
237+ } ;
238+ }
239+
240+ private ErrorResponse HandleInvalidOperationException ( InvalidOperationException exception , HttpContext context )
241+ {
242+ _logger . LogWarning ( exception , "잘못된 작업: {Message}" , exception . Message ) ;
243+
244+ return new ErrorResponse
245+ {
246+ ErrorCode = "INVALID_OPERATION" ,
247+ Message = "요청한 작업을 수행할 수 없습니다" ,
248+ StatusCode = ( int ) HttpStatusCode . BadRequest ,
249+ Timestamp = DateTime . UtcNow ,
250+ TraceId = context . TraceIdentifier ,
251+ Details = _environment . IsDevelopment ( ) ? new List < string > { exception . Message } : null
252+ } ;
253+ }
254+
255+ private ErrorResponse HandleUnauthorizedAccessException ( UnauthorizedAccessException exception , HttpContext context )
256+ {
257+ _logger . LogWarning ( exception , "권한 없음: {Message}" , exception . Message ) ;
258+
259+ return new ErrorResponse
260+ {
261+ ErrorCode = "UNAUTHORIZED" ,
262+ Message = "접근 권한이 없습니다" ,
263+ StatusCode = ( int ) HttpStatusCode . Unauthorized ,
264+ Timestamp = DateTime . UtcNow ,
265+ TraceId = context . TraceIdentifier
266+ } ;
267+ }
268+
269+ private ErrorResponse HandleTimeoutException ( TimeoutException exception , HttpContext context )
270+ {
271+ _logger . LogWarning ( exception , "타임아웃 발생: {Message}" , exception . Message ) ;
272+
273+ return new ErrorResponse
274+ {
275+ ErrorCode = "TIMEOUT" ,
276+ Message = "요청 처리 시간이 초과되었습니다" ,
277+ StatusCode = ( int ) HttpStatusCode . RequestTimeout ,
278+ Timestamp = DateTime . UtcNow ,
279+ TraceId = context . TraceIdentifier
280+ } ;
281+ }
282+
283+ private ErrorResponse HandleHttpRequestException ( HttpRequestException exception , HttpContext context )
284+ {
285+ _logger . LogError ( exception , "HTTP 요청 오류: {Message}" , exception . Message ) ;
286+
287+ return new ErrorResponse
288+ {
289+ ErrorCode = "HTTP_REQUEST_ERROR" ,
290+ Message = "외부 서비스와의 통신 중 오류가 발생했습니다" ,
291+ StatusCode = ( int ) HttpStatusCode . BadGateway ,
292+ Timestamp = DateTime . UtcNow ,
293+ TraceId = context . TraceIdentifier
294+ } ;
295+ }
296+
181297 private ErrorResponse HandleGenericException ( Exception exception , HttpContext context )
182298 {
183- _logger . LogError ( exception , "예상치 못한 예외 발생: {ExceptionType} - {Message}" ,
184- exception . GetType ( ) . Name , exception . Message ) ;
299+ var exceptionType = exception . GetType ( ) . Name ;
300+ var isDevelopment = _environment . IsDevelopment ( ) ;
301+
302+ _logger . LogError ( exception , "예상치 못한 예외 발생: {ExceptionType} - {Message}" , exceptionType , exception . Message ) ;
185303
186304 return new ErrorResponse
187305 {
188- ErrorCode = "내부_서버_오류 " ,
189- Message = "서버에서 예상치 못한 오류가 발생했습니다" ,
306+ ErrorCode = "INTERNAL_SERVER_ERROR " ,
307+ Message = isDevelopment ? exception . Message : "서버에서 예상치 못한 오류가 발생했습니다" ,
190308 StatusCode = ( int ) HttpStatusCode . InternalServerError ,
191309 Timestamp = DateTime . UtcNow ,
192- TraceId = context . TraceIdentifier
310+ TraceId = context . TraceIdentifier ,
311+ Details = isDevelopment ? new List < string >
312+ {
313+ $ "Exception Type: { exceptionType } ",
314+ $ "Stack Trace: { exception . StackTrace } "
315+ } : null
193316 } ;
194317 }
195318 }
0 commit comments