- 자원을 이름으로 구분하고, 해당 자원의 상태를 주고 받는 모든 것이 REST라고 할 수 있지만, 일반적으로 REST라고 하면 좁은 의미로 HTTP를 통해 어떤 자원에 대해서 CRUD를 실행하는 방식을 뜻합니다.
- JSON 혹은 XML를 통해 데이터를 주고 받는 것이 일반적입니다.
HTTP POST , http://localhost:8080/board
{
"board":{
"title":"제목",
"content":"내용"
}
}
리소스에 대한 행위는 HTTP Method(GET, POST, PUT, PATCH, DELETE 등)로 표현해야 합니다.
/(슬래시)는 계층 관계를 나타낼때 사용합니다.
URI 마지막 문자에 /(슬래시)를 사용하지 않습니다.
URI에 _(underscore)는 사용하지 않도록 합니다. 또한 영어 대문자보다는 소문자를 씁니다.
그리고 가독성을 위해서 긴 단어는 잘 사용하지 않습니다.
URI에 동사는 GET, POST와 같은 HTTP Method를 표현하기 때문에 동사가 아니라 명사를 사용한다.
URI에 파일의 확장자(예를들어 .json , .JPEG)를 포함 시키지 않습니다.
RESTful은 REST의 설계 가이드
REST는 위 정의들을 구현하는 방식에 제약을 두지 않기 때문에 구체적인 가이드라인이 없습니다.
REST API 설계 가이드에 따라 API를 만들어서 웹 서비스를 제공하면 해당 웹 서비스는 RESTful하다고 합니다.
API를 RESTful 하게 만들어서 API의 목적이 무엇인지 명확하게 하기 위해 RESTful 함을 지향 합니다.
Level 0 : 1 URI, 1 HTTP method
-> 하나의 POST Method로 함수이름과 인자를 넘김
-> 전달되는 서로 다른 매개변수를 통해서 하나의 endpoint로 여러 동작을 하게 된다.
-> 매개변수를 Body 로 전달하기 위해 HTTP method 는 POST 가 된다.
Level 1 : N URI, 2 HTTP method
-> Resource 별로 고유한 URI를 사용해서 식별하는 것이다.
-> URI에 /create, /update 등 표현
Level 2 : N URI, 4 HTTP method
-> URI 에 action 이 담기지 않는다.
-> 멱등성(같은 결과를 출력하는 것)을 보장하는 GET 에는 Cache 가 적용되며
-> Response 에 HTTP Status Code 가 의미있게 반환된다.
Level 3 : Hypermedia As Engine of Application State
-> API 서비스의 모든 endpoint 를 최초 진입점이 되는 URI 를 통해 Hypertext Link 형태로 제공
-> 어떤 request 의 다음 request 에 필요한 endpoint 를 제공한다는 것
Level 4 : API Versionning (어떤 단계에서도 적용 가능)
-> URI 버전 방식
-> Request Param 버전 방식
-> Header 버전 방식
-> Media Type 버전 방식
@GetMapping(path = "/users/{id}/v1") // -> URI 활용
@GetMapping(value="/users/{id}/", params = "version=1") -> param 활용
@GetMapping(value = "/users/{id}", headers = "X-API-VERSION=1") -> header 활용
@GetMapping(value = "/users/{id}", produces = "application/vnd.company.appv1+json") // media type versioning
RestTemplate : 스프링 프레임워크에서 제공하는 간단하고 동기화된 REST 클라이언트
Traverson : 스프링 HATEOAS에서 제공하는 하이퍼링크를 인식하는 동기화 REST 클라이언트로 같은 이름의 자바스크립트 라이브러리로 비롯된 것
WebClient : 스프링 5에서 소개된 반응형 비동기 REST 클라이언트
RestTemplate restTemplate = new RestTemplate();
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
public Ingredient getIngredientById(String id) {
return restTemplate.getForObject(
"http://localhost:8080/ingredients/{id}", Ingredient.class, id);
}
public Ingredient getIngredientById(String id) {
Map<String, String> urlVariables = new HashMap<>();
urlVariables.put("id", id);
return restTemplate.getForObject(
"http://localhost:8080/ingredients/{id}", Ingredient.class, urlVariables);
}
public ResponseEntity<Ingredient> getIngredientById(String id) {
// headers, body, status 정보 포함
return restTemplate.getForEntity(
"http://localhost:8080/ingredients/{id}", Ingredient.class, id);
}
public Ingredient createIngredient(Ingredient ingredient) {
return restTemplate.postForObject(
"http://localhost:8080/ingredients", ingredient, Ingredient.class);
}
public ResponseEntity<Ingredient> createIngredient(Ingredient ingredient) {
// headers, body, status 정보 포함
return restTemplate.postForEntity(
"http://localhost:8080/ingredients", ingredient, Ingredient.class);
}
public void updateIngredient(Ingredient ingredient) {
restTemplate.put(
"http://localhost:8080/ingredients/{id}", Ingredient.class, ingredient.getId());
}
public void deleteIngredient(Ingredient ingredient) {
restTemplate.delete(
"http://localhost:8080/ingredients/{id}", ingredient.getId());
ResponseEntity<List<Ingredient>> ingredients = restTemplate.exchange(getUrl
, HttpMethod.GET, null, new ParameterizedTypeReference<List<Ingredient>>() {
});
RequestEntity<Taco> requestEntity = RequestEntity
.post(postUrl)
.contentType(MediaType.APPLICATION_JSON)
.body(taco);
ResponseEntity<Taco> retTaco = restTemplate.exchange(requestEntity, Taco.class);
Traverson traverson = new Traverson(URI.create("http://localhost:8080/api")
, MediaTypes.HAL_JSON);
ParameterizedTypeReference<Resources<Ingredient>> ingredientType =
new ParameterizedTypeReference<Resources<Ingredient>>() {};
/* /api/ingredients */
Resources<Ingredient> ingredientRes = traverson.follow("ingredients").toObject(ingredientType);
Collection<Ingredient> ingredients = ingredientRes.getContent();
ParameterizedTypeReference<Resources<Taco>> tacoType =
new ParameterizedTypeReference<Resources<Taco>>() {};
/* /api/tacos/recents */
Resources<Taco> tacoRes = traverson.follow("tacos", "recents").toObject(tacoType);
Collection<Taco> tacos = tacoRes.getContent();
RestTemplate은 리소스를 쓰거나 삭제할 수 있지만, API를 이동하는 것은 쉽지 않다.
Traverson은 HATEOAS가 활성화된 API를 이동하면서 해당 API 리소스를 쉽게 가져올 수 있지만, 리소스를 쓰거나 삭제하는 메소드를 제공하지 않는다.
따라서, API 이동과 변경 삭제를 모두 해야 한다면 2가지를 함께 사용해야 한다.
private Ingredient addIngredient(Ingredient ingredient) {
/* /api/ingredients */
String url = traverson.follow("ingredients").asLink().getHref();
return restTeamplate.postForObject(url, ingredient, Ingredient.class);
}
잘 보고갑니다^_^