Skip to content

Commit 182e68c

Browse files
Implements the use of ProblemDetail for HTTP error responses. (issue #149 and PR #173) (#175)
* feat: Implements the use of ProblemDetail for HTTP error responses. * fix: Fixing OpenAPI documentation and implementing date and time details in ProblemDetail. * fix: Implements some adjustments to properties. * fix: Adjusts the Time Sample property to the ISO 8601 standard.
1 parent cf41db1 commit 182e68c

File tree

2 files changed

+163
-159
lines changed

2 files changed

+163
-159
lines changed

src/main/java/org/springframework/samples/petclinic/rest/advice/ExceptionControllerAdvice.java

+41-34
Original file line numberDiff line numberDiff line change
@@ -16,28 +16,27 @@
1616

1717
package org.springframework.samples.petclinic.rest.advice;
1818

19-
import com.fasterxml.jackson.core.JsonProcessingException;
20-
import com.fasterxml.jackson.databind.ObjectMapper;
19+
import jakarta.servlet.http.HttpServletRequest;
2120
import org.springframework.dao.DataIntegrityViolationException;
22-
import org.springframework.http.HttpHeaders;
2321
import org.springframework.http.HttpStatus;
22+
import org.springframework.http.ProblemDetail;
2423
import org.springframework.http.ResponseEntity;
2524
import org.springframework.samples.petclinic.rest.controller.BindingErrorsResponse;
2625
import org.springframework.validation.BindingResult;
2726
import org.springframework.web.bind.MethodArgumentNotValidException;
2827
import org.springframework.web.bind.annotation.ControllerAdvice;
2928
import org.springframework.web.bind.annotation.ExceptionHandler;
3029
import org.springframework.web.bind.annotation.ResponseBody;
31-
import org.springframework.web.bind.annotation.ResponseStatus;
32-
import org.springframework.web.context.request.WebRequest;
3330

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;
3535

3636
/**
3737
* Global Exception handler for REST controllers.
3838
* <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.
4140
*
4241
* @author Vitaliy Fedoriv
4342
* @author Alexander Dudkin
@@ -46,64 +45,72 @@
4645
public class ExceptionControllerAdvice {
4746

4847
/**
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.
5250
*
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.
5554
*/
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;
6062
}
6163

6264
/**
6365
* Handles all general exceptions by returning a 500 Internal Server Error status with error details.
6466
*
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.
6669
* @return A {@link ResponseEntity} containing the error information and a 500 Internal Server Error status
6770
*/
6871
@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);
7277
}
7378

7479
/**
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.
7782
*
7883
* @param ex The {@link DataIntegrityViolationException} to be handled
84+
* @param request {@link HttpServletRequest} object referring to the current request.
7985
* @return A {@link ResponseEntity} containing the error information and a 404 Not Found status
8086
*/
8187
@ExceptionHandler(DataIntegrityViolationException.class)
82-
@ResponseStatus(code = HttpStatus.NOT_FOUND)
8388
@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);
8793
}
8894

8995
/**
9096
* Handles exception thrown by Bean Validation on controller methods parameters
9197
*
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.
95101
*/
96102
@ExceptionHandler(MethodArgumentNotValidException.class)
97-
@ResponseStatus(BAD_REQUEST)
98103
@ResponseBody
99-
public ResponseEntity<ErrorInfo> handleMethodArgumentNotValidException(MethodArgumentNotValidException ex) {
104+
public ResponseEntity<ProblemDetail> handleMethodArgumentNotValidException(MethodArgumentNotValidException ex, HttpServletRequest request) {
105+
HttpStatus status = HttpStatus.BAD_REQUEST;
100106
BindingErrorsResponse errors = new BindingErrorsResponse();
101107
BindingResult bindingResult = ex.getBindingResult();
102108
if (bindingResult.hasErrors()) {
103109
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);
105112
}
106-
return ResponseEntity.badRequest().build();
113+
return ResponseEntity.status(status).build();
107114
}
108115

109116
}

0 commit comments

Comments
 (0)