|
16 | 16 |
|
17 | 17 | package org.springframework.samples.petclinic.rest.advice;
|
18 | 18 |
|
19 |
| -import com.fasterxml.jackson.core.JsonProcessingException; |
20 |
| -import com.fasterxml.jackson.databind.ObjectMapper; |
| 19 | +import jakarta.servlet.http.HttpServletRequest; |
21 | 20 | import org.springframework.dao.DataIntegrityViolationException;
|
22 |
| -import org.springframework.http.HttpHeaders; |
23 | 21 | import org.springframework.http.HttpStatus;
|
| 22 | +import org.springframework.http.ProblemDetail; |
24 | 23 | import org.springframework.http.ResponseEntity;
|
25 | 24 | import org.springframework.samples.petclinic.rest.controller.BindingErrorsResponse;
|
26 | 25 | import org.springframework.validation.BindingResult;
|
27 | 26 | import org.springframework.web.bind.MethodArgumentNotValidException;
|
28 | 27 | import org.springframework.web.bind.annotation.ControllerAdvice;
|
29 | 28 | import org.springframework.web.bind.annotation.ExceptionHandler;
|
30 | 29 | import org.springframework.web.bind.annotation.ResponseBody;
|
31 |
| -import org.springframework.web.bind.annotation.ResponseStatus; |
32 |
| -import org.springframework.web.context.request.WebRequest; |
33 | 30 |
|
34 |
| -import static org.springframework.http.HttpStatus.BAD_REQUEST; |
| 31 | +import java.net.URI; |
| 32 | +import java.time.Instant; |
| 33 | +import java.time.LocalDateTime; |
| 34 | +import java.time.format.DateTimeFormatter; |
35 | 35 |
|
36 | 36 | /**
|
37 | 37 | * Global Exception handler for REST controllers.
|
38 | 38 | * <p>
|
39 |
| - * This class handles exceptions thrown by REST controllers and returns |
40 |
| - * appropriate HTTP responses to the client. |
| 39 | + * This class handles exceptions thrown by REST controllers and returns appropriate HTTP responses to the client. |
41 | 40 | *
|
42 | 41 | * @author Vitaliy Fedoriv
|
43 | 42 | * @author Alexander Dudkin
|
|
46 | 45 | public class ExceptionControllerAdvice {
|
47 | 46 |
|
48 | 47 | /**
|
49 |
| - * Record for storing error information. |
50 |
| - * <p> |
51 |
| - * This record encapsulates the class name and message of the exception. |
| 48 | + * Private method for constructing the {@link ProblemDetail} object passing the name and details of the exception |
| 49 | + * class. |
52 | 50 | *
|
53 |
| - * @param className The name of the exception class |
54 |
| - * @param exMessage The message of the exception |
| 51 | + * @param ex Object referring to the thrown exception. |
| 52 | + * @param status HTTP response status. |
| 53 | + * @param url URL request. |
55 | 54 | */
|
56 |
| - private record ErrorInfo(String className, String exMessage) { |
57 |
| - public ErrorInfo(Exception ex) { |
58 |
| - this(ex.getClass().getName(), ex.getLocalizedMessage()); |
59 |
| - } |
| 55 | + private ProblemDetail detailBuild(Exception ex, HttpStatus status, StringBuffer url) { |
| 56 | + ProblemDetail detail = ProblemDetail.forStatus(status); |
| 57 | + detail.setType(URI.create(url.toString())); |
| 58 | + detail.setTitle(ex.getClass().getSimpleName()); |
| 59 | + detail.setDetail(ex.getLocalizedMessage()); |
| 60 | + detail.setProperty("timestamp", Instant.now()); |
| 61 | + return detail; |
60 | 62 | }
|
61 | 63 |
|
62 | 64 | /**
|
63 | 65 | * Handles all general exceptions by returning a 500 Internal Server Error status with error details.
|
64 | 66 | *
|
65 |
| - * @param e The exception to be handled |
| 67 | + * @param e The {@link Exception} to be handled |
| 68 | + * @param request {@link HttpServletRequest} object referring to the current request. |
66 | 69 | * @return A {@link ResponseEntity} containing the error information and a 500 Internal Server Error status
|
67 | 70 | */
|
68 | 71 | @ExceptionHandler(Exception.class)
|
69 |
| - public ResponseEntity<ErrorInfo> handleGeneralException(Exception e) { |
70 |
| - ErrorInfo info = new ErrorInfo(e); |
71 |
| - return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(info); |
| 72 | + @ResponseBody |
| 73 | + public ResponseEntity<ProblemDetail> handleGeneralException(Exception e, HttpServletRequest request) { |
| 74 | + HttpStatus status = HttpStatus.INTERNAL_SERVER_ERROR; |
| 75 | + ProblemDetail detail = this.detailBuild(e, status, request.getRequestURL()); |
| 76 | + return ResponseEntity.status(status).body(detail); |
72 | 77 | }
|
73 | 78 |
|
74 | 79 | /**
|
75 |
| - * Handles {@link DataIntegrityViolationException} which typically indicates database constraint violations. |
76 |
| - * This method returns a 404 Not Found status if an entity does not exist. |
| 80 | + * Handles {@link DataIntegrityViolationException} which typically indicates database constraint violations. This |
| 81 | + * method returns a 404 Not Found status if an entity does not exist. |
77 | 82 | *
|
78 | 83 | * @param ex The {@link DataIntegrityViolationException} to be handled
|
| 84 | + * @param request {@link HttpServletRequest} object referring to the current request. |
79 | 85 | * @return A {@link ResponseEntity} containing the error information and a 404 Not Found status
|
80 | 86 | */
|
81 | 87 | @ExceptionHandler(DataIntegrityViolationException.class)
|
82 |
| - @ResponseStatus(code = HttpStatus.NOT_FOUND) |
83 | 88 | @ResponseBody
|
84 |
| - public ResponseEntity<ErrorInfo> handleDataIntegrityViolationException(DataIntegrityViolationException ex) { |
85 |
| - ErrorInfo errorInfo = new ErrorInfo(ex); |
86 |
| - return ResponseEntity.status(HttpStatus.NOT_FOUND).body(errorInfo); |
| 89 | + public ResponseEntity<ProblemDetail> handleDataIntegrityViolationException(DataIntegrityViolationException ex, HttpServletRequest request) { |
| 90 | + HttpStatus status = HttpStatus.NOT_FOUND; |
| 91 | + ProblemDetail detail = this.detailBuild(ex, status, request.getRequestURL()); |
| 92 | + return ResponseEntity.status(status).body(detail); |
87 | 93 | }
|
88 | 94 |
|
89 | 95 | /**
|
90 | 96 | * Handles exception thrown by Bean Validation on controller methods parameters
|
91 | 97 | *
|
92 |
| - * @param ex The thrown exception |
93 |
| - * |
94 |
| - * @return an empty response entity |
| 98 | + * @param ex The {@link MethodArgumentNotValidException} to be handled |
| 99 | + * @param request {@link HttpServletRequest} object referring to the current request. |
| 100 | + * @return A {@link ResponseEntity} containing the error information and a 400 Bad Request status. |
95 | 101 | */
|
96 | 102 | @ExceptionHandler(MethodArgumentNotValidException.class)
|
97 |
| - @ResponseStatus(BAD_REQUEST) |
98 | 103 | @ResponseBody
|
99 |
| - public ResponseEntity<ErrorInfo> handleMethodArgumentNotValidException(MethodArgumentNotValidException ex) { |
| 104 | + public ResponseEntity<ProblemDetail> handleMethodArgumentNotValidException(MethodArgumentNotValidException ex, HttpServletRequest request) { |
| 105 | + HttpStatus status = HttpStatus.BAD_REQUEST; |
100 | 106 | BindingErrorsResponse errors = new BindingErrorsResponse();
|
101 | 107 | BindingResult bindingResult = ex.getBindingResult();
|
102 | 108 | if (bindingResult.hasErrors()) {
|
103 | 109 | errors.addAllErrors(bindingResult);
|
104 |
| - return ResponseEntity.badRequest().body(new ErrorInfo("MethodArgumentNotValidException", "Validation failed")); |
| 110 | + ProblemDetail detail = this.detailBuild(ex, status, request.getRequestURL()); |
| 111 | + return ResponseEntity.status(status).body(detail); |
105 | 112 | }
|
106 |
| - return ResponseEntity.badRequest().build(); |
| 113 | + return ResponseEntity.status(status).build(); |
107 | 114 | }
|
108 | 115 |
|
109 | 116 | }
|
0 commit comments