HTTP/1.1 403 Forbidden
Content-Type: application/problem+json
Content-Language: en
{
"status": 403,
"type": "https://bankname.com/common-problems/low-balance",
"title": "You not have enough balance",
"detail": "Your current balance is 30 and you are transterring 50",
"instance": "/account-transfer-service"
}
关键信息如下:
ProblemDetail pd = ProblemDetail.forStatusAndDetail(HttpStatus.NOT_FOUND, "message");
pd.setType(URI.create("http://my-app-host.com/errors/not-found"));
pd.setTitle("Record Not Found");
要添加非标准字段,请使用setProperty()方法。
pd.setProperty("property-key", "property-value");
ProblemDetail pd = ProblemDetail.forStatusAndDetail(HttpStatus.INTERNAL_SERVER_ERROR, "Null Pointer Exception");
pd.setType(URI.create("http://my-app-host.com/errors/npe"));
pd.setTitle("Null Pointer Exception");
throw new ErrorResponseException(HttpStatus.INTERNAL_SERVER_ERROR, pd, npe);
@ControllerAdvice
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {
@ExceptionHandler(CustomException.class)
public ProblemDetail handleCustomException(CustomException ex, WebRequest request) {
ProblemDetail pd = //build object..
return pd;
}
}
我们可以从任何@ExceptionHandler或任何@RequestMapping方法返回ProblemDetail或ErrorResponse以呈现RFC 7807响应。
@Value("${hostname}")
private String hostname;
@GetMapping(path = "/employees/v2/{id}")
public ResponseEntity getEmployeeById_V2(@PathVariable("id") Long id) {
if (id < 100) {
return ResponseEntity.ok(new Employee(id, "lokesh", "gupta", "admin@leftso.com"));
} else {
ProblemDetail pd = ProblemDetail
.forStatusAndDetail(HttpStatus.NOT_FOUND, "Employee id '" + id + "' does no exist");
pd.setType(URI.create("http://my-app-host.com/errors/not-found"));
pd.setTitle("Record Not Found");
pd.setProperty("hostname", hostname);
return ResponseEntity.status(404).body(pd);
}
}
接下来,让我们用id=101进行测试。它将返回RFC规范中的响应。
{
"detail": "Employee id '101' does no exist",
"hostname": "localhost",
"instance": "/employees/v2/101",
"status": 404,
"title": "Record Not Found",
"type": "http://my-app-host.com/errors/not-found"
}
@GetMapping(path = "/v3/{id}")
public ResponseEntity getEmployeeById_V3(@PathVariable("id") Long id) {
try {
//somthing抛出了此异常
throw new NullPointerException("Something was expected but it was null");
}
catch (NullPointerException npe) {
ProblemDetail pd = ProblemDetail
.forStatusAndDetail(HttpStatus.INTERNAL_SERVER_ERROR,
"Null Pointer Exception");
pd.setType(URI.create("http://my-app-host.com/errors/npe"));
pd.setTitle("Null Pointer Exception");
pd.setProperty("hostname", hostname);
throw new ErrorResponseException(HttpStatus.NOT_FOUND, pd, npe);
}
}
public class RecordNotFoundException extends RuntimeException {
private final String message;
public RecordNotFoundException(String message) {
this.message = message;
}
}
我们从代码中的几个地方抛出这些异常。
@GetMapping(path = "/v1/{id}")
public ResponseEntity getEmployeeById_V1(@PathVariable("id") Long id) {
if (id < 100) {
return ResponseEntity.ok(...);
} else {
throw new RecordNotFoundException("Employee id '" + id + "' does no exist");
}
}
在此类异常中添加问题详细信息的最佳方法是在@ControllerAdvice类中。我们必须在@ExceptionHandler(RecordNotFoundException.class)方法中处理异常,并添加所需的信息。
@ControllerAdvice
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {
@Value("${hostname}")
private String hostname;
@ExceptionHandler(RecordNotFoundException.class)
public ProblemDetail handleRecordNotFoundException(RecordNotFoundException ex, WebRequest request) {
ProblemDetail body = ProblemDetail
.forStatusAndDetail(HttpStatusCode.valueOf(404),ex.getLocalizedMessage());
body.setType(URI.create("http://my-app-host.com/errors/not-found"));
body.setTitle("Record Not Found");
body.setProperty("hostname", hostname);
return body;
}
}
@Test
public void testAddEmployee_V2_FailsWhen_IncorrectId() {
try {
this.restTemplate.getForObject("/employees/v2/101", Employee.class);
} catch (RestClientResponseException ex) {
ProblemDetail pd = ex.getResponseBodyAs(ProblemDetail.class);
assertEquals("Employee id '101' does no exist", pd.getDetail());
assertEquals(404, pd.getStatus());
assertEquals("Record Not Found", pd.getTitle());
assertEquals(URI.create("http://my-app-host.com/errors/not-found"), pd.getType());
assertEquals("localhost", pd.getProperties().get("hostname"));
}
}
注意,如果我们使用Spring Webflux,我们可以使用WebClient API来验证返回的问题细节。
@Test
void testAddEmployeeUsingWebFlux_V2_FailsWhen_IncorrectId() {
this.webClient.get().uri("/employees/v2/101")
.retrieve()
.bodyToMono(String.class)
.doOnNext(System.out::println)
.onErrorResume(WebClientResponseException.class, ex -> {
ProblemDetail pd = ex.getResponseBodyAs(ProblemDetail.class);
//assertions...
return Mono.empty();
})
.block();
}
执行v3接口测试
@Test
public void testAddEmployee_V3_FailsWhen_IncorrectId() {
try {
this.restTemplate.getForObject("/employees/v3/101", Employee.class);
} catch (RestClientResponseException ex) {
ProblemDetail pd = ex.getResponseBodyAs(ProblemDetail.class);
System.out.println(format(ex.getStatusCode(), ex.getResponseHeaders(), pd));
}
}
以上测试执行结果参考
地址:https://www.leftso.com/article/1116.html