45
53

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

[SpringBoot]RestController正常時と異常時で別の型のJSONを返却する

Posted at

SpringBootでは@RestControllerを使えばお手軽にJSONを返すコントローラーが作れて大変便利。

@RestController
public class HogeController{
    @Autowired
    EmpRepository empRepository;

    @GetMapping
    public List<Employee> getEmp(){
        return empRepository.findAll();
    }
}

こんな感じにしてあげればEmployeeクラスのリストをJSONにして返却してくれる。

でもでも、エラーのときはエラー情報返してあげたい。
そんでもってその時の型はEmployeeのリストじゃなくてエラーメッセージとかエラーコードとかを返したい。
もちろんHTTPステータスもつけたい。
戻り値の型をStringにしてJSON文字列に変換して・・・なんてやったら台無しだし!

というわけでいい感じにエラー情報を返してみましょう。
先にざっくりまとめると、通常処理はExceptionで抜けて例外ハンドリングして別のレスポンス作って返せばOKです。

#1.例外ハンドラーを作成
@ControllerAdvice@ExceptionHandlerを使って例外をハンドリングするクラスを作ります。
共通処理にするんじゃなくて、元のクラス専用の例外ハンドリングをしたい場合は、元のクラス内に@ExceptionHandlerを付けたメソッドを作成すればOK。

例外ハンドラーその1
@ControllerAdvice
public class BadRequestExceptionHandler {

	@ExceptionHandler(BadRequestException.class)
	public ResponseEntity<ErrorResponse> getException(HttpServletRequest req, BadRequestException e){
        // エラーレスポンス。のちほど解説
		return ErrorResponse.createResponse(e);

	}
}

#2.自前のExceptionを作成
これは必須ではないですが、任意のタイミング・内容でExceptionを投げるために作っておくと便利。
上で既に使っているBadRequestExceptionですね。
(getter、setterは省略してます。)

自前Exception
public class BadRequestException extends Exception {

	private String keyName;
	private String keyValue;
	private String error;
	
	public BadRequestException(String error, String keyName, String keyValue){
		this.keyName = keyName;
		this.keyValue = keyValue;
		this.error = error;
	}

}

エラーとなった項目、その値、エラー情報を渡せるようにしてみました。

#3.エラーレスポンス用のクラスを作成
エラーレスポンス用の情報を入れるクラスを作ります。POJOです。

エラーレスポンス用のクラス
public class ErrorResponse {
	private String KeyName;
	private String KeyValue;
	private String Message;
	
	public ErrorResponse(String keyName,String  keyValue,String  message){
		this.KeyName = keyName;
		this.KeyValue = keyValue;
		this.Message = message;
	}
}

##3.1.返却するレスポンスについて
レスポンスを作成するにはResponseEntity<T>を使います。

new ResponseEntity<ErrorResponse>(errorResponse, HttpStatus.BAD_REQUEST)

みたいなかんじですね。
これは先ほど作ったErrorResponseクラスの中で生成してもらいましょう。

ErrorResponse.createResponse
public ResponseEntity<ErrorResponse> createResponse(HttpStatus status){
	return new ResponseEntity<ErrorResponse>(this, status);
}

ついでにExceptionをからResponseEntityを作成するstaticクラスも作ってしまいましょう。

ErrorResponse.createResponse(static)
public static ResponseEntity<ErrorResponse> createResponse(BadRequestException e){
	return new ResponseEntity<ErrorResponse>(
			new ErrorResponse(e.getKeyName(),e.getKeyValue(),e.getMessage())
				,HttpStatus.BAD_REQUEST);
}

#4.できた!
ここまで実装してあげたらBadRequestExceptionHandlerが例外をキャッチしてErrorResponse.createResponse(e)でレスポンスを作成してErrorResponseのJSONをクライアントに返却できるようになります。
やったね!

#5.一般例外のハンドリング
せっかくなので一般例外もハンドリングします。
リクエストパラメータが受け取り側の変数型に変換できないときに発生するHttpMessageNotReadableException
該当URL無しのNoHandlerFoundException
GET用処理にPOSTしてきたときなどに発生するHttpRequestMethodNotSupportedException
とりあえず全部例外拾っとくかのException
これらを追加した例外ハンドラーがこちら。

@ControllerAdvice
public class BadRequestExceptionHandler {

	@ExceptionHandler(BadRequestException.class)
	public ResponseEntity<ErrorResponse> getException(HttpServletRequest req, BadRequestException e){

		return ErrorResponse.createResponse(e);

	}
	
	/**
	 * 400 - Bad Reque
	 */
	@ExceptionHandler(HttpMessageNotReadableException.class)
	public ResponseEntity<ErrorResponse> notReadable(HttpServletRequest req){
		return new ResponseEntity<ErrorResponse>(
				new ErrorResponse("All","unknown","NotReadable")
				,HttpStatus.BAD_REQUEST);
	}
	
	/**
	 * 404 - Not Found
	 */
	@ExceptionHandler(NoHandlerFoundException.class)
	public ResponseEntity<ErrorResponse> notFound(HttpServletRequest req){
		return new ResponseEntity<ErrorResponse>(
				new ErrorResponse(null,null,"Not Found")
				,HttpStatus.NOT_FOUND);
	}
	
	/**
	 * 405 - Method Not Allowed
	 */
	@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
	public ResponseEntity<ErrorResponse> notSupported(HttpServletRequest req){
		return new ResponseEntity<ErrorResponse>(
				new ErrorResponse(null,null,"Method Not Allowed")
				,HttpStatus.METHOD_NOT_ALLOWED);
	}

	@ExceptionHandler(Exception.class)
	public ResponseEntity<ErrorResponse> anotherException(Exception e, HttpServletRequest req){
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss.SSS");
		
		// ログは、大事
		System.err.println("ERROR(500) " + (sdf.format((Calendar.getInstance()).getTime()))
				+ ": remoteAddr:" + req.getRemoteAddr()
				+ ", remoteHost:" + req.getRemoteHost()
				+ ", requestURL:" + req.getRequestURL()
				);
		e.printStackTrace();
		
		return new ResponseEntity<ErrorResponse>(
				new ErrorResponse(null,null,"unknown error")
				,HttpStatus.INTERNAL_SERVER_ERROR);
	}
}

以上!

#参考

Exception Handling in Spring REST Web Service
https://dzone.com/articles/exception-handling-spring-rest

45
53
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
45
53

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?