@ExceptionHandler({ MethodArgumentTypeMismatchException.class })
public ResponseEntity handleMethodArgumentTypeMismatch(
MethodArgumentTypeMismatchException ex, WebRequest request) {
String error =
ex.getName() + " should be of type " + ex.getRequiredType().getName();
ApiError apiError =
new ApiError(HttpStatus.BAD_REQUEST, ex.getLocalizedMessage(), error);
return new ResponseEntity(
apiError, new HttpHeaders(), apiError.getStatus());
}
3.2. クライアントからのAPIの消費
ここで、MethodArgumentTypeMismatchExceptionに遭遇するテストを見てみましょう。send a request with id as String instead of long:
@Test
public void whenMethodArgumentMismatch_thenBadRequest() {
Response response = givenAuth().get(URL_PREFIX + "/api/foos/ccc");
ApiError error = response.as(ApiError.class);
assertEquals(HttpStatus.BAD_REQUEST, error.getStatus());
assertEquals(1, error.getErrors().size());
assertTrue(error.getErrors().get(0).contains("should be of type"));
}
そして最後に、この同じリクエストを検討します::
Request method: GET
Request path: http://localhost:8080/spring-security-rest/api/foos/ccc
この種のJSONエラー応答は次のようになります。
{
"status": "BAD_REQUEST",
"message":
"Failed to convert value of type [java.lang.String]
to required type [java.lang.Long]; nested exception
is java.lang.NumberFormatException: For input string: \"ccc\"",
"errors": [
"id should be of type java.lang.Long"
]
}
{
"status":"NOT_FOUND",
"message":"No handler found for DELETE /spring-security-rest/api/xx",
"errors":[
"No handler found for DELETE /spring-security-rest/api/xx"
]
}
{
"status":"METHOD_NOT_ALLOWED",
"message":"Request method 'DELETE' not supported",
"errors":[
"DELETE method is not supported for this request. Supported methods are GET "
]
}
@Override
protected ResponseEntity handleHttpMediaTypeNotSupported(
HttpMediaTypeNotSupportedException ex,
HttpHeaders headers,
HttpStatus status,
WebRequest request) {
StringBuilder builder = new StringBuilder();
builder.append(ex.getContentType());
builder.append(" media type is not supported. Supported media types are ");
ex.getSupportedMediaTypes().forEach(t -> builder.append(t + ", "));
ApiError apiError = new ApiError(HttpStatus.UNSUPPORTED_MEDIA_TYPE,
ex.getLocalizedMessage(), builder.substring(0, builder.length() - 2));
return new ResponseEntity(
apiError, new HttpHeaders(), apiError.getStatus());
}
この問題に遭遇した簡単なテストを次に示します。
@Test
public void whenSendInvalidHttpMediaType_thenUnsupportedMediaType() {
Response response = givenAuth().body("").post(URL_PREFIX + "/api/foos");
ApiError error = response.as(ApiError.class);
assertEquals(HttpStatus.UNSUPPORTED_MEDIA_TYPE, error.getStatus());
assertEquals(1, error.getErrors().size());
assertTrue(error.getErrors().get(0).contains("media type is not supported"));
}
最後に、リクエストの例を次に示します。
Request method: POST
Request path: http://localhost:8080/spring-security-
Headers: Content-Type=text/plain; charset=ISO-8859-1
そしてthe error JSON response:
{
"status":"UNSUPPORTED_MEDIA_TYPE",
"message":"Content type 'text/plain;charset=ISO-8859-1' not supported",
"errors":["text/plain;charset=ISO-8859-1 media type is not supported.
Supported media types are text/xml
application/x-www-form-urlencoded
application/*+xml
application/json;charset=UTF-8
application/*+json;charset=UTF-8 */"
]
}
@ExceptionHandler({ Exception.class })
public ResponseEntity handleAll(Exception ex, WebRequest request) {
ApiError apiError = new ApiError(
HttpStatus.INTERNAL_SERVER_ERROR, ex.getLocalizedMessage(), "error occurred");
return new ResponseEntity(
apiError, new HttpHeaders(), apiError.getStatus());
}
8. 結論
Spring REST API用の適切で成熟したエラーハンドラーを構築するのは難しく、間違いなく反復的なプロセスです。 このチュートリアルが、APIのためにそれを行うための良い出発点であり、また、APIのクライアントがエラーを迅速かつ簡単に診断し、それらを通過するのを支援する方法の良いアンカーになることを願っています。