REST APIをホストするためにSpringブートを使用しています。ブラウザがURLとカスタムにアクセスしている場合でも、標準のエラー応答ではなく常にJSON応答を送信したいと思いますデータ構造。
カスタム例外の場合、@ ControllerAdviceと@ExceptionHandlerを使用してこれを行うことができます。しかし、404や401のような標準のエラーと処理されたエラーに対してこれを行う良い方法を見つけることができません。
これを行う方法の良いパターンはありますか?
404エラーはDispatcherServletによって処理されます。プロパティthrowExceptionIfNoHandlerFoundがあり、これをオーバーライドできます。
Applicationクラスでは、新しいBeanを作成できます。
@Bean
DispatcherServlet dispatcherServlet () {
DispatcherServlet ds = new DispatcherServlet();
ds.setThrowExceptionIfNoHandlerFound(true);
return ds;
}
...次にNoHandlerFoundException例外をキャッチします
@EnableWebMvc
@ControllerAdvice
public class GlobalControllerExceptionHandler {
@ExceptionHandler
@ResponseStatus(value=HttpStatus.NOT_FOUND)
@ResponseBody
public ErrorMessageResponse requestHandlingNoHandlerFound(final NoHandlerFoundException ex) {
doSomething(LOG.debug("text to log"));
}
}
404ケースの応答をオーバーライドする方法のサンプルソリューションを提供しました。解決策はかなりシンプルで、サンプルコードを投稿していますが、元のスレッドで詳細を確認できます。 Spring Boot Rest-How to configure 404-resource not found
最初:エラーケースを処理し、応答を上書きするコントローラーを定義します。
@ControllerAdvice
public class ExceptionHandlerController {
@ExceptionHandler(NoHandlerFoundException.class)
@ResponseStatus(value= HttpStatus.NOT_FOUND)
@ResponseBody
public ErrorResponse requestHandlingNoHandlerFound() {
return new ErrorResponse("custom_404", "message for 404 error code");
}
}
2番目:404の場合は例外をスローするようにSpringに指示する必要があります(ハンドラを解決できませんでした):
@SpringBootApplication
@EnableWebMvc
public class Application {
public static void main(String[] args) {
ApplicationContext ctx = SpringApplication.run(Application.class, args);
DispatcherServlet dispatcherServlet = (DispatcherServlet)ctx.getBean("dispatcherServlet");
dispatcherServlet.setThrowExceptionIfNoHandlerFound(true);
}
}
私は同じ問題を抱えていましたが、別の方法を使用して修正しました。カスタム応答で404、401およびその他のステータスを返すために、カスタム例外クラスに応答ステータスを追加し、例外ハンドラから呼び出すことができるようになりました。
SpringユーティリティクラスAnnotationUtilsを使用すると、findAnnotationメソッドを使用して、定義されたカスタム例外のステータスを取得できます。見つかりませんでしたなど、例外に対して定義したアノテーションを使用して適切なステータスを返します。
これが私の@RestControllerAdviceです
@RestControllerAdvice
public class MainExceptionHandler extends Throwable{
@ExceptionHandler(BaseException.class)
ResponseEntity<ExceptionErrorResponse> exceptionHandler(GeneralMainException e)
{
ResponseStatus status = AnnotationUtils.findAnnotation(e.getClass(),ResponseStatus.class);
if(status != null)
{
return new ResponseEntity<>(new ExceptionErrorResponse(e.getCode(),e.getMessage()),status.code());
}
}
不正なリクエストステータスを返すCustomParamsException
@ResponseStatus(value= HttpStatus.BAD_REQUEST)
public class CustomParamsException extends BaseException {
private static final String CODE = "400";
public CustomParamsException(String message) {
super(CODE, message);
}
}
詳細が見つかりませんでしたが、見つかりませんステータスを返します
@ResponseStatus(value= HttpStatus.NOT_FOUND)
public class DetailsNotException extends BaseException {
private static final String CODE = "400";
public DetailsNotException(String message) {
super(CODE, message);
}
}
Excetionを拡張するGeneralMainException
public class GeneralMainException extends Exception {
private String code;
private String message;
public GeneralMainException (String message) {
super(message);
}
public GeneralMainException (String code, String message) {
this.code = code;
this.message = message;
}
public String getCode() {
return code;
}
@Override
public String getMessage() {
return message;
}
}
コントローラーのアドバイスに含めることで、他のシステム例外を処理することを決定できます。
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
@ExceptionHandler(Exception.class)
ExceptionErrorResponse sysError(Exception e)
{
return new ExceptionErrorResponse(""1002", e.getMessage());
}
すべての回答とコメントをまとめると、これを行う最善の方法は次のようになると思います-
まず、application.properties
にハンドラーが見つからない場合に例外をスローするようにスプリングブートに指示します
spring.mvc.throw-exception-if-no-handler-found=true
次に、アプリケーションでNoHandlerFoundException
を処理します。私はこれを次の方法で処理します
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(NoHandlerFoundException.class)
public void handleNotFoundError(HttpServletResponse response, NoHandlerFoundException ex) {
ErrorDto errorDto = Errors.URL_NOT_FOUND.getErrorDto();
logger.error("URL not found exception: " + ex.getRequestURL());
prepareErrorResponse(response, HttpStatus.NOT_FOUND, errorDto);
}
}
カスタムのErrorPageオブジェクトを追加して、web.xmlのエラーページ定義に関連付けることができます。 Spring Bootが例を示します ...
@Bean
public EmbeddedServletContainerCustomizer containerCustomizer(){
return new MyCustomizer();
}
// ...
private static class MyCustomizer implements EmbeddedServletContainerCustomizer {
@Override
public void customize(ConfigurableEmbeddedServletContainer container) {
container.addErrorPages(new ErrorPage(HttpStatus.UNAUTHORIZED, "/unauthorized.html"));
container.addErrorPages(new ErrorPage(HttpStatus.NOT_FOUND, "/not-found.html"));
}
}
EDIT:エラーページをコントローラーにレストさせる場合、上記の方法は機能すると思いますが、さらに簡単な方法は、カスタムErrorController以下のような...
@Bean
public ErrorController errorController(ErrorAttributes errorAttributes) {
return new CustomErrorController(errorAttributes);
}
// ...
public class CustomErrorController extends BasicErrorController {
public CustomErrorController(ErrorAttributes errorAttributes) {
super(errorAttributes);
}
@Override
@RequestMapping(value = "${error.path:/error}")
@ResponseBody
public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
ResponseEntity<Map<String, Object>> error = super.error(request);
HttpStatus statusCode = error.getStatusCode();
switch (statusCode) {
case NOT_FOUND:
return getMyCustomNotFoundResponseEntity(request);
case UNAUTHORIZED:
return getMyCustomUnauthorizedResponseEntity(request);
default:
return error;
}
}
}
Springバージョン5以降では、クラスResponseStatusExceptionを使用できます。
@GetMapping("example")
public ResponseEntity example() {
try {
throw new MyException();
} catch (MyException e) {
throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "My Exception", e);
}
}
適切に注釈が付けられたメソッドを導入する必要があるようです。サポートされていないメディアタイプ(415)の場合:
@ExceptionHandler(MethodArgumentNotValidException)
public ResponseEntity handleMethodArgumentNotValidException(HttpServletRequest req, MethodArgumentNotValidException e) {
logger.error('Caught exception', e)
def response = new ExceptionResponse(
error: 'Validation error',
exception: e.class.name,
message: e.bindingResult.fieldErrors.collect { "'$it.field' $it.defaultMessage" }.join(', '),
path: req.servletPath,
status: BAD_REQUEST.value(),
timestamp: currentTimeMillis()
)
new ResponseEntity<>(response, BAD_REQUEST)
}
ただし、401と404はDispatcherServlet
に到達する前にスローされる可能性があるため、これができない可能性があります。この場合、ControllerAdvice
は機能しません。
Spring Boot RESTサービス例外処理 を参照してください。ディスパッチャサーブレットに「ルートが見つかりません」という例外を発行するように指示する方法と、それらの例外をキャッチする方法を示しています。 (私が働いている場所)は、これを現在のRESTサービスの本番環境で使用しています。