지금까지 상태코드를 지정하기 위해 HttpServletResponse 의 setStatus()와 sendError()를 사용했음.
문제점은 에러 시, JSON이 아닌 HTML 결과를 응답함.
정상, 비정상 모두 JSON 응답을 위해 ResponseEntity
사용.
public class ErrorResponse {
private String message;
public ErrorResponse(String message) {
this.message = message;
}
public String getMessage() {
return message;
}
}
ErrorResponse 객체를 이용하도록 member()
수정
@GetMapping("/api/members/{id}")
public ResponseEntity<Object> member(@PathVariable Long id, // 리턴 타입으로 일반 객체 사용
HttpServletResponse response) throws IOException{
Member member = memberDao.selectById(id);
if(member == null){
return ResponseEntity.status(HttpStatus.NOT_FOUND)
.body(new ErrorResponse("no member"));
}
return ResponseEntity.status(HttpStatus.OK)
.body(member);
}
body
: body로 지정한 객체를 사용해서 변환status
: 지정한 값을 응답 상태 코드로 사용status와 body를 이용
ResponseEntity.status(상태코드).body(객체)
상태코드는 HttpStatus에 정의된 값 이용
( Document 보러 가기 )
상태코드 OK와 body를 한번에 사용
ResponseEntity.ok(member)
body가 없을 때, build()
사용
ResponseEntity.status(HttpStatus.NOT_FOUND).build()
body가 없고, status 대신 사용하는 메서드
- noContent() : 204
- badRequest() : 400
- notFound() : 404
ResponseEntity를 정상 응답에도 사용을 하면, ResponseEntity를 사용하는 코드가 반복된다.
컨트롤러에서 익센션 발생시 작동하는 @ExceptionHandler
에 ResponseEntity
를 사용하는 코드를 만들어서 리턴하도록 코드를 수정.
@GetMapping("/api/members/{id}")
public Member member(@PathVariable Long id, // 리턴 타입으로 일반 객체 사용
HttpServletResponse response) throws IOException{
Member member = memberDao.selectById(id);
if(member == null){
return new MemberNotFoundException();
}
return member;
}
@ExceptionHandler(MemberNotFoundException.class)
public ResponseEntity<ErrorResponse> handleNoData() {
return ResponseEntity.status(HttpStatus.NOT_FOUND)
.body(new ErrorResponse("no member"));
}
@RestControllerAdvice("controller")
public class ApiExceptionAdvice {
@ExceptionHandler(MemberNotResponseException.class)
public ResponseEntity<ErrorResponse> handleNoData () {
return ResponseEntity.status(HttpStatus.NOT_FOUND)
.body(new ErrorResponse("no member"));
}
}
@Valid
붙은 커맨드 객체의 검증도 HTML 응답이므로, 이를 JSON 형식으로 바꿔보자. @PostMapping("/api/members")
public ResponseEntity<Object> newMember(
@RequestBody @Valid RegisterRequest regReq) {
try{
Long newMemberId = registerService.regist(regReq);
URI uri = URI.create("/api/members/" + newMemberId);
return ResponseEntity.created(uri).build();
} catch(DuplicateMemberException dupEx){
return ResponseEntity.status(HttpStatus.CONFLICT).build();
}
}
@PostMapping("/api/members")
public ResponseEntity<Object> newMember(
@RequestBody @Valid RegisterRequest regReq
, Errors errors){
if(errors.hasErrors()){
String errorCodes = errors.getAllErrors()
.stream()
.map(error -> error.getCodes()[0]) // error는 ObjectError
.collect(Collectors.joining("."));
return ResponseEntity
.status(HttpStatus.BAD_REQUEST)
.body(new ErrorResponse("errorCodes = "+errorCodes));
}
...생략
}
@RequestBody
가 붙은 @Valid
붙은 객체를 검증 실패했을 때, Errors 파라미터가 없으면 MethodArgumentNotValidException
발생.@ExceptionHandler
를 이용해서 익셉션 처리. @RestControllerAdvice("controller")
public class ApiExceptionAdvice {
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<ErrorResponse> handleBindException (
MethodArgumentNotValidException ex) {
String errorCodes = ex.getBindResult().getAllErrors()
.stream()
.map(error -> error.getCodes()[0])
.collect(Collectors.joining(","));
return ResponseEntity
.status(HttpStatus.BAD_REQUEST)
.body(new ErrorResponse("errorCodes = "+errorCodes));
}
}