일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 |
- #정규표현식
- bootstrap
- Spring Boot
- 프로젝트 시작
- WebJar
- #Gradle Multi project with IntelliJ
- #Microservice
- #단축키
- 감사
- 년말
- 토익
- Microservices
- 평가인증
- 분석 작업
- #화면캡쳐 #macOS
- java
- Lambda
- 방법론
- jv
- docker #docker tutorial
- 2010
- Today
- Total
사랑해 마니마니
Spring Boot로 RESTful Service 만들기 본문
Engin Yöyen 의 Building Microservices with Spring Boot 따라 해보기: 책 참 좋다.
0. Spring Boot 프로젝트 만들어 보자
start.spring.io
- grade project
- java: 1.8
- Spring boot: 2.0.5
- group: com.example
- artifact: http-programming
- dependencies: Web, Lombok
Lombok 설치하기
- IntelliJ plugin
- IntelliJ IDEA > Preferences > Build, Execution, Deployment > Compile > Annotation Processes > Enable Annotation Processing
Git 설정하기
- root에서 git init 실행
.gitignore 파일 만들기
- gitignore.io 사이트에서
- java, gradle, git, intellij를 항목으로 .gitignore 파일 생성하기
- .gitignore 파일 만들고 git init
Project 실행하기
- Terminal에서 gradle bootRun
- Control + Shift + R
프로젝트 Packaging
- gradle build
- java -jar build/libs/java -jar build/libs/http-programming-0.0.1-SNAPSHOT.jar
@SpringBootApplication
- HttpProgrammingApplication.java 파일을 ApplicationStarter.java로 rename
package com.example;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class ApplicationStarter {
public static void main(String[] args) {
SpringApplication.run(ApplicationStarter.class, args);
}
}
package com.example.controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
@RequestMapping(value = "/hello/{name}")
String hello(@PathVariable String name) {
return String.format("Hello %s", name);
}
}
브라우져에서 http://localhost:8080/hello/min 로 접근하면 화면상에 min 출력됨
1. Http Request와 Http Response 메시지 to Spring Annotation
http://localhost:8080/compute/function/sumhttp://localhost:8080/compute/function/sum?limit=5http://localhost:8080/compute/function/max?limit=8http://localhost:8080/compute/function/min?limit=6
2. Controller
Postman을 설치한 후에 Chrome에서 주소창에 chrome://apps
을 실행하고, postman 실행
NOT_IMPLEMENTED
public static final HttpStatus NOT_IMPLEMENTED
501 Not Implemented
See Also:
HTTP/1.1: Semantics and Content, section 6.6.2
결과 - Status: 501 Not implemented가 출력됨
User model 추가 및 /user에 대한 Get Request 구현
package com.example.model;
import lombok.*;
@NoArgsConstructor
@AllArgsConstructor
@Getter
@Setter
@ToString
public class User {
private Long id;
private String username;
}
package com.example.controller;
import com.example.model.User;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import java.util.Arrays;
import java.util.List;
@Controller
public class Users {
@ResponseBody
@GetMapping("/users")
List<User> getAll() {
User jane = new User(1L, "Jane");
User john = new User(2L, "John");
return Arrays.asList(jane, john);
}
@ResponseStatus(HttpStatus.NOT_IMPLEMENTED)
@RequestMapping(value = "/users/jane", method = RequestMethod.GET)
void get() {
}
@ResponseStatus(HttpStatus.NOT_IMPLEMENTED)
@RequestMapping(value = "/users", method = RequestMethod.PUT)
void put() {
}
@ResponseStatus(HttpStatus.NOT_IMPLEMENTED)
@RequestMapping(value = "/users/jane", method = RequestMethod.POST)
void post() {
}
@ResponseStatus(HttpStatus.NOT_IMPLEMENTED)
@RequestMapping(value = "/users/jane", method = RequestMethod.DELETE)
void delete() {
}
}
Terminal에서 curl로 확인해 보면
~: $ curl -X GET http://localhost:8080/users -i
HTTP/1.1 200
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Sun, 07 Oct 2018 11:53:46 GMT
[{"id":1,"username":"Jane"},{"id":2,"username":"John"}]
@RestController 사용하기
@RestController
어노테이션은 Spring 4.0에 도입됨
@RestController
= @Controller
+ @ResponseBody
3. Request Body 관련 기능 만들기
package com.example.controller;
import com.example.model.User;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import java.util.Arrays;
import java.util.List;
@RestController
@RequestMapping("/users")
public class Users {
@GetMapping
List<User> getAll() {
User jane = new User(1L, "Jane");
User john = new User(2L, "John");
return Arrays.asList(jane, john);
}
@ResponseStatus(HttpStatus.NOT_IMPLEMENTED)
@GetMapping(value = "/jane")
void get() {
}
@ResponseStatus(HttpStatus.NOT_IMPLEMENTED)
@PutMapping
void put() {
}
@PostMapping(value = "/jane")
User post(@RequestBody User user) {
return user;
}
@ResponseStatus(HttpStatus.NOT_IMPLEMENTED)
@DeleteMapping("/jane")
void delete() {
}
}
Postman에서 Request를
{
"id": 198555,
"username": "Jane"
}
보내면
Response가 Status : 200 OK로 해서 아래와 같이 Return 됨.
{
"id": 198555,
"username": "Jane"
}
여기서 { "_id_": 198555, "_username_": "Jane" }
와 같이 잘못된 파라메터로 보내면
Response가 Status : 200 OK
, { "id": null, "username": null }
로 리턴됨
Status : 200 OK
가 아닌 에러가 출력되도록 하기 위하서는
application.properties
에
spring.jackson.deserialization.fail_on_unknown_properties=true
를 셋팅해야 한다
다시 { "_id_": 198555, "_username_": "Jane" }
로 Reqeust를 보내면
{
"timestamp": "2018-10-07T12:17:27.746+0000",
"status": 400,
"error": "Bad Request",
"message": "JSON parse error: Unrecognized field \"-id\" (class com.example.model.User), not marked as ignorable; nested exception is com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field \"-id\" (class com.example.model.User), not marked as ignorable (2 known properties: \"id\", \"username\"])\n at [Source: (PushbackInputStream); line: 2, column: 16] (through reference chain: com.example.model.User[\"-id\"])",
"path": "/users/jane"
}
를 출력함.
4. URI Template
scheme:[//[user:password@]host[:port]][/]path[?query][#fragment]
GET /users/:username/repos?page=2&size=10
http://example.com/users/jane/repos?page=2&size=10
Path Variable 처리하기
http://example.com/users/jane/repos?page=2&size=10
과 같은 request를 이제 처리해 보자
@PathVariable
어노테이션을 이용하여 path variable을 URI 매핑하는 데 이용해 보자
http://example.com/users/jane/repos?page=2&size=10
에서 jane을 Path Variable로 받을 수 있도록
package com.example.controller;
import com.example.model.User;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;
import java.util.Arrays;
import java.util.List;
@RestController
@RequestMapping("/users")
public class Users {
@GetMapping
List<User> getAll() {
User jane = new User(1L, "Jane");
User john = new User(2L, "John");
return Arrays.asList(jane, john);
}
@GetMapping("/{username}")
User get(@PathVariable String username) {
return new User(1L, username);
}
@ResponseStatus(HttpStatus.NOT_IMPLEMENTED)
@PutMapping
void put() {
}
@PostMapping(value = "/jane")
User post(@RequestBody User user) {
return user;
}
@ResponseStatus(HttpStatus.NOT_IMPLEMENTED)
@DeleteMapping("/jane")
void delete() {
}
}
min: ~$ curl -X GET http://localhost:8080/users/anyone -i
HTTP/1.1 200
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Mon, 08 Oct 2018 15:49:06 GMT
{"id":1,"username":"anyone"}
Query String 처리하기
http://example.com/users/jane/repos?page=2&size=10
에서 page, size 값을 Variable로 받을 수 있도록
package com.example.controller;
import com.example.model.User;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;
import java.util.Arrays;
import java.util.List;
@RestController
@RequestMapping("/users")
@Slf4j
public class Users {
@GetMapping
List<User> getAll(@RequestParam(value = "page", required = false) Integer page,
@RequestParam(value = "size", required = false) Integer size) {
log.info(String.format("Requested all users with page %d and size %d ", page, size));
User jane = new User(1L, "Jane");
User john = new User(2L, "John");
return Arrays.asList(jane, john);
}
@GetMapping("/{username}")
User get(@PathVariable String username) {
return new User(1L, username);
}
@ResponseStatus(HttpStatus.NOT_IMPLEMENTED)
@PutMapping
void put() {
}
@PostMapping(value = "/jane")
User post(@RequestBody User user) {
return user;
}
@ResponseStatus(HttpStatus.NOT_IMPLEMENTED)
@DeleteMapping("/jane")
void delete() {
}
}
@RequestParam
으로 "page"값과 "size" 변수를 각각 Integer page
, Integer size
값으로 받는다.
로그를 찍기 위해서 Lombok의 @Slf4j
어노테이션을 사용하고 log.info(...)
로 로그 출력
Postman에서 GET 방식으로 http://localhost:8080/users?page=2&size=10을 던지면
Console(Log)에 INFO 17003 --- [nio-8080-exec-2] com.example.controller.Users : Requested all users with page 2 and size 10
이 출력됨
request = false
대신 Java 1.8 의 java.util.Optinal
을 사용하면
List<User> getAll(@RequestParam(value = "page") Optional<Integer> page,
@RequestParam(value = "size") Optional<Integer> size) {
log.info(String.format("Requested all users with page %d and size %d ", page.orElse(null), size.orElse(null)));
User jane = new User(1L, "Jane");
User john = new User(2L, "John");
return Arrays.asList(jane, john);
}
5. HTTP Response 만들기
RequestEntity를 사용한다면
- 도메인 객체를 return하지 않고
- 좀더 flexisble한 프로그램을 원하거나
- 다른 형태의 response body를 원한다면
- 또는 error를 throw하기를 원한다면
package com.example.controller;
import com.example.model.User;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
@RestController
@RequestMapping("/users")
@Slf4j
public class Users {
@GetMapping
List<User> getAll(@RequestParam(value = "page") Optional<Integer> page,
@RequestParam(value = "size") Optional<Integer> size) {
log.info(String.format("Requested all users with page %d and size %d ", page.orElse(null), size.orElse(null)));
User jane = new User(1L, "Jane");
User john = new User(2L, "John");
return Arrays.asList(jane, john);
}
@GetMapping("/{username}")
User get(@PathVariable String username) {
return new User(1L, username);
}
@PutMapping
ResponseEntity<?> put(@RequestBody User user) {
if (user.getUsername() != null && user.getUsername().length() < 5) {
return new ResponseEntity(HttpStatus.UNPROCESSABLE_ENTITY);
}
//create user first than
return new ResponseEntity(user, HttpStatus.CREATED);
}
@PostMapping(value = "/jane")
User post(@RequestBody User user) {
return user;
}
@ResponseStatus(HttpStatus.NOT_IMPLEMENTED)
@DeleteMapping("/jane")
void delete() {
}
}
Postman으로
- PUT method
- URI http://localhost:8080/users
- Request Body { "id" : 198555, "username" : "Jane" }
요청하면 Status: 422 Unprocessable Entity (WebDAV) (RFC 4918) 에러 상태임
만약 Request Body에서 "username" : "Jane.hi"로 user.getUsername.length()가 5보다 크면 Status: 201 Created 상태가 출력됨
다른 형태의 Response Body
오류가 발생하면 Http Status code (422) 정보만으로는 부족하다. 그래서 error message 을 ResponseEntity에 실어 보내는 방법을 사용한다.
먼저 Error Message를 담을 모델을 만들자
package com.example.model;
import lombok.AllArgsConstructor;
import lombok.Getter;
@AllArgsConstructor
@Getter
public class ErrorMessage {
private String message;
private String error;
}
package com.example.controller;
import com.example.model.ErrorMessage;
import com.example.model.User;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
@RestController
@RequestMapping("/users")
@Slf4j
public class Users {
@GetMapping
List<User> getAll(@RequestParam(value = "page") Optional<Integer> page,
@RequestParam(value = "size") Optional<Integer> size) {
log.info(String.format("Requested all users with page %d and size %d ", page.orElse(null), size.orElse(null)));
User jane = new User(1L, "Jane");
User john = new User(2L, "John");
return Arrays.asList(jane, john);
}
@GetMapping("/{username}")
User get(@PathVariable String username) {
return new User(1L, username);
}
@PutMapping
ResponseEntity<?> put(@RequestBody User user) {
if (user.getUsername() != null && user.getUsername().length() < 5) {
ErrorMessage error = new ErrorMessage("Validation Failed", "User name is less than 5 character");
return new ResponseEntity(error, HttpStatus.UNPROCESSABLE_ENTITY);
}
//create user first than
return new ResponseEntity(user, HttpStatus.CREATED);
}
@PostMapping(value = "/jane")
User post(@RequestBody User user) {
return user;
}
@ResponseStatus(HttpStatus.NOT_IMPLEMENTED)
@DeleteMapping("/jane")
void delete() {
}
}
앞에서와 같이 Postman으로
- PUT method
- URI http://localhost:8080/users
- Request Body { "id" : 198555, "username" : "Jane" }
요청하면 Status: 422 Unprocessable Entity (WebDAV) (RFC 4918) 에러 상태와 함께 response body에
{
"message": "Valication Failed",
"error": "User name is less than 5 character"
}
Response Header에 정보 보내기
HTTP status와 body와 함께 response header도 ResponseEntity
에 실어 보낼수 있음.
(e.g. Rate-Limiting 정보)
package com.example.controller;
import com.example.model.ErrorMessage;
import com.example.model.User;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
@RestController
@RequestMapping("/users")
@Slf4j
public class Users {
@GetMapping
List<User> getAll(@RequestParam(value = "page") Optional<Integer> page,
@RequestParam(value = "size") Optional<Integer> size) {
log.info(String.format("Requested all users with page %d and size %d ", page.orElse(null), size.orElse(null)));
User jane = new User(1L, "Jane");
User john = new User(2L, "John");
return Arrays.asList(jane, john);
}
@GetMapping("/{username}")
ResponseEntity<?> get(@PathVariable String username) {
User user = new User(1L, username);
HttpHeaders responseHeaders = new HttpHeaders();
responseHeaders.set("X-RateLimit-Limit", "1000");
responseHeaders.set("X-RateLimit-Remaining", "500");
responseHeaders.set("X-RateLimit-Reset", "1457020923");
return new ResponseEntity(user, responseHeaders, HttpStatus.OK);
}
@PutMapping
ResponseEntity<?> put(@RequestBody User user) {
if (user.getUsername() != null && user.getUsername().length() < 5) {
ErrorMessage error = new ErrorMessage("Validation Failed", "User name is less than 5 character");
return new ResponseEntity(error, HttpStatus.UNPROCESSABLE_ENTITY);
}
//create user first than
return new ResponseEntity(user, HttpStatus.CREATED);
}
@PostMapping(value = "/jane")
User post(@RequestBody User user) {
return user;
}
@ResponseStatus(HttpStatus.NOT_IMPLEMENTED)
@DeleteMapping("/jane")
void delete() {
}
}
Postman으로 GET, http://localhost:8080/users/jane
을 보내면
- content-type →application/json;charset=UTF-8
- date →Tue, 09 Oct 2018 00:36:31 GMT
- transfer-encoding →chunked
- x-ratelimit-limit →1000
- x-ratelimit-remaining →500
- x-ratelimit-reset →1457020923
-
ResponseEntity
public ResponseEntity(@Nullable T body, @Nullable MultiValueMap<java.lang.String,java.lang.String> headers, HttpStatus status)
Create a newHttpEntity
with the given body, headers, and status code.- Parameters:
body
- the entity bodyheaders
- the entity headersstatus
- the status code
6. Request Header 사용
Corelation ID등을 HTTP Request등을 Header에 실어 보낼 때 사용
- mendetory 나 optional로 header를 받을 것인지를 선택할 수 있음
- mendetory로 설정했는 데 HTTP Request에 필요로 하는 값이 넘어 오지 않을 경우 status code 400(Bad Request) return
package com.example.controller;
import com.example.model.ErrorMessage;
import com.example.model.User;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.*;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
@RestController
@RequestMapping("/users")
@Slf4j
public class Users {
@GetMapping
List<User> getAll(@RequestParam(value = "page") Optional<Integer> page,
@RequestParam(value = "size") Optional<Integer> size) {
log.info(String.format("Requested all users with page %d and size %d ", page.orElse(null), size.orElse(null)));
User jane = new User(1L, "Jane");
User john = new User(2L, "John");
return Arrays.asList(jane, john);
}
@GetMapping("/{username}")
ResponseEntity<?> get(@PathVariable String username) {
User user = new User(1L, username);
HttpHeaders responseHeaders = new HttpHeaders();
responseHeaders.set("X-RateLimit-Limit", "1000");
responseHeaders.set("X-RateLimit-Remaining", "500");
responseHeaders.set("X-RateLimit-Reset", "1457020923");
return new ResponseEntity(user, responseHeaders, HttpStatus.OK);
}
@PutMapping
ResponseEntity<?> put(@RequestBody User user) {
if (user.getUsername() != null && user.getUsername().length() < 5) {
ErrorMessage error = new ErrorMessage("Validation Failed", "User name is less than 5 character");
return new ResponseEntity(error, HttpStatus.UNPROCESSABLE_ENTITY);
}
//create user first than
return new ResponseEntity(user, HttpStatus.CREATED);
}
@PostMapping(value = "/jane")
User post(@RequestBody User user) {
return user;
}
@ResponseStatus(HttpStatus.NO_CONTENT)
@DeleteMapping("/jane")
void delete(@PathVariable String username,
@RequestHeader MultiValueMap<String, String> headers) {
headers.forEach((k, v) -> log.info(String.format("%s : %s ", k, v)));
//here is where the user will be deleted
}
}
curl -i -X DELETE http://localhost:8080/users/jane을 보내면
HTTP/1.1 500
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Tue, 09 Oct 2018 00:51:18 GMT
Connection: close
{
"timestamp":"2018-10-09T00:51:18.401+0000",
"status":500,"error":"Internal Server Error",
"message":"Missing URI template variable 'username' for method parameter of type String",
"path":"/users/jane"
}
? Engin yoyen 책에서는 400 Bad Request를 return 한다고 했는 데 해보니 Status code가 500으로 return된다.
7. Exception 처리하기
앞에서 validaion Exception이 발생했을 때 Status code 422 Unprocessable Entity를 return하도록 처리했는 데 .. 좀더 개선해 보자
Spring에서는 Controller에서 Exception이 발생했을 때 2가지 방식으로 처리함
- ExceptionHandler: exception이 발생(throw)했을 때 항상 call이 됨
- @ControllerAdvice: @RequestMapping에 대한 exception 처리
package com.example.exception;
public class ValidationException extends Exception {
public ValidationException(String message) {
super(message);
}
}
package com.example.controller;
import com.example.exception.ValidationException;
import com.example.model.ErrorMessage;
import com.example.model.User;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.*;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
@RestController
@RequestMapping("/users")
@Slf4j
public class Users {
@GetMapping
List<User> getAll(@RequestParam(value = "page") Optional<Integer> page,
@RequestParam(value = "size") Optional<Integer> size) {
log.info(String.format("Requested all users with page %d and size %d ", page.orElse(null), size.orElse(null)));
User jane = new User(1L, "Jane");
User john = new User(2L, "John");
return Arrays.asList(jane, john);
}
@GetMapping("/{username}")
ResponseEntity<?> get(@PathVariable String username) {
User user = new User(1L, username);
HttpHeaders responseHeaders = new HttpHeaders();
responseHeaders.set("X-RateLimit-Limit", "1000");
responseHeaders.set("X-RateLimit-Remaining", "500");
responseHeaders.set("X-RateLimit-Reset", "1457020923");
return new ResponseEntity(user, responseHeaders, HttpStatus.OK);
}
@PutMapping
ResponseEntity<?> put(@RequestBody User user) throws Exception{
if (user.getUsername() != null && user.getUsername().length() < 5) {
//ErrorMessage error = new ErrorMessage("Validation Failed", "User name is less than 5 character");
//return new ResponseEntity(error, HttpStatus.UNPROCESSABLE_ENTITY);
throw new ValidationException("User name is less than 5 character");
}
//create user first than
return new ResponseEntity(user, HttpStatus.CREATED);
}
@PostMapping(value = "/jane")
User post(@RequestBody User user) {
return user;
}
@ResponseStatus(HttpStatus.NO_CONTENT)
@DeleteMapping("/jane")
void delete(@PathVariable String username,
@RequestHeader MultiValueMap<String, String> headers) {
headers.forEach((k, v) -> log.info(String.format("%s : %s ", k, v)));
//here is where the user will be deleted
}
}
Postman으로 PUT, http://localhost:8080/users를 던지면
connection →close
content-type →application/json;charset=UTF-8
date →Tue, 09 Oct 2018 01:13:56 GMT
transfer-encoding →chunked
Status: 500 Internal Server Error
{
"timestamp": "2018-10-09T01:13:56.258+0000",
"status": 500,
"error": "Internal Server Error",
"message": "User name is less than 5 character",
"path": "/users"
}
2018-10-09 10:32:58.252 ERROR 19394 --- [nio-8080-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is com.example.exception.ValidationException: User name is less than 5 character] with root cause
com.example.exception.ValidationException: User name is less than 5 character
at com.example.controller.Users.put(Users.java:49) ~[classes/:na]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_121]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_121]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_121]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_121]
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:209) ~[spring-web-5.0.9.RELEASE.jar:5.0.9.RELEASE]
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:136) ~[spring-web-5.0.9.RELEASE.jar:5.0.9.RELEASE]
....
Status Code가 우리가 원하는 코드가 아님. 그래서 재대로 동작하기 위해 @ControllerAdvice를 사용하여 Exception 처리 Class를 만들어
package com.example.controller;
import com.example.exception.ValidationException;
import com.example.model.ErrorMessage;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
@ControllerAdvice
class ExceptionHandlerAdvice {
@ExceptionHandler(value = ValidationException.class)
public ResponseEntity validationExceptionHandler(Exception e) {
ErrorMessage error = new ErrorMessage("Validation Failed", e.getMessage());
return new ResponseEntity(error, HttpStatus.UNPROCESSABLE_ENTITY);
}
}
content-type →application/json;charset=UTF-8
date →Tue, 09 Oct 2018 01:30:48 GMT
transfer-encoding →chunked
Status: 422 Unprocessable Entity (WebDAV) (RFC 4918)
{
"message": "Validation Failed",
"error": "User name is less than 5 character"
}
log에 Exception은 찍히지 않음(깨끗해 졌다.)
8. HTTP Caching (Comming soon)
Time-based Cache Headers
Conditional Cache Headers