web-dev-qa-db-ja.com

スプリングブート404エラーカスタムエラー応答ReST

REST APIをホストするためにSpringブートを使用しています。ブラウザがURLとカスタムにアクセスしている場合でも、標準のエラー応答ではなく常にJSON応答を送信したいと思いますデータ構造。

カスタム例外の場合、@ ControllerAdviceと@ExceptionHandlerを使用してこれを行うことができます。しかし、404や401のような標準のエラーと処理されたエラーに対してこれを行う良い方法を見つけることができません。

これを行う方法の良いパターンはありますか?

15
Markus

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"));
    }
}
3
maxie

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);
    }
}
3
Ilya Ovesnov

私は同じ問題を抱えていましたが、別の方法を使用して修正しました。カスタム応答で404401およびその他のステータスを返すために、カスタム例外クラスに応答ステータスを追加し、例外ハンドラから呼び出すことができるようになりました。

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());
 }
1
Adewagold

すべての回答とコメントをまとめると、これを行う最善の方法は次のようになると思います-

まず、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);
    }
}
1
Emdadul Sawon

カスタムの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;
        }
    }
}
0
hyness

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);
    }
}
0
R J

適切に注釈が付けられたメソッドを導入する必要があるようです。サポートされていないメディアタイプ(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は機能しません。

0
Opal

Spring Boot RESTサービス例外処理 を参照してください。ディスパッチャサーブレットに「ルートが見つかりません」という例外を発行するように指示する方法と、それらの例外をキャッチする方法を示しています。 (私が働いている場所)は、これを現在のRESTサービスの本番環境で使用しています。

0
ogradyjd