1_[spring]server to server 통신 Resttemplate03.헤더값 넣기
Server to Server 통신-RestTemplate03.헤더값 넣기
TIL/[Spring]Server to Server 통신-RestTemplate02.POST요청.md at main · hy6219/TIL
이번에는 이전에는 헤더의 contents부분이 비어져 있는 경우가 있었는데, 바로 앞에서 POST방식에서 주로 볼 수 있었다
이번에는 헤더의 값을 채워넣어주자
예제는 앞에서 이용한 예제를 확장해서 진행하도록 하자
RequestEntity
- HttpEntity의 확장된 개념
- HTTP 메서드와 대상 url을 노출
- RestTemplate에서 @Controller에 있는 메서드의 요청 입력에 대해서 준비하기 위해 필요한 객체
- GET, POST, PUT 요청과 관련된 메서드 등이 존재
🌹public static RequestEntity.BodyBuilder post(URI url)
주어진 url을 이용해서 HTTP POST 요청을 만들어냄
RequestEntity (Spring Framework 5.3.9 API)
🌹 RequestEntity.BodyBuilder contentType(MediaType contentType)
body의 media type을 지정(content-type헤더에)
🌹
- body 내용을 설정
🌹 B header(String headerName, String… headerValues)
- 주어진 단일 헤더값(headerValues)를 주어진 헤더이름(headerName)에 넣기
(클라이언트)
먼저, 지난 POST 방식에서 설정한 uri는 그대로 갖는 것으로 하고,
📌헤더값을 변경하기 위해서는 RestTemplate의 exchange(RequestEntity, 응답형태와 관련된 클래스)를 이용해주어야 한다
그런데 보통 json 데이터를 처리할 경우에는 요청과 응답에도 POST를 사용하는 것이 보편적이고, 편리하므로 POST 방식을 이용해서 요청을 하기 위해서
- RequestEntity를 생성할 때, post메서드를 이용하자(post(uri))
그리고 해더의 경우
- Content-type 헤더는 미디어타입을 json으로 하고
- 커스텀헤더는 x-authorization과 custom-header라는 이름으로 해서 실어보내자
그리고 body에는 userRequest라는 user정보 관련 객체를 담아주자
package com.example.client.service;
import com.example.client.dto.UserRequest;
import com.example.client.dto.UserResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.RequestEntity;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;
import java.net.URI;
@Slf4j
@Service
public class RestTemplateService {
public UserResponse exchangeHeader(UserRequest userRequest,long userId, String name){
//주소 동일하게 사용
URI uri=UriComponentsBuilder.fromUriString("http://localhost:9090")
.path("/api/server/userId/{userId}/name/{name}")
.encode()
.build()
.expand(userId,name)
.toUri();
log.info("request: {}",uri);
RestTemplate restTemplate=new RestTemplate();
**RequestEntity<UserRequest> req=RequestEntity.post(uri)
.contentType(MediaType.APPLICATION_JSON)
.header("x-authorization","abcd")
.header("custom-header","aaa")
.body(userRequest);**
ResponseEntity<UserResponse> res= restTemplate.exchange(req,UserResponse.class);
HttpHeaders headers= res.getHeaders();
log.info("status code: {}",res.getStatusCode());
log.info("status code value:{}",res.getStatusCodeValue());
log.info("header info:content-type-{}\n " +
"content-length-{}\n" +
"Date-{}\n" +
"Keep-Alive-{}\n" +
"Connection-{}",headers.get("Content-type"),
headers.get("Content-Length"),
headers.get("Date"),
headers.get("Keep-Alive"),
headers.get("Connection"));
log.info("body:{}",res.getBody());
return res.getBody();
}
}
(서버)
서버측에서 요청측에서 보낸 헤더값을 확인해보기 위해서 “@RequestHeader(name=”헤더명”)”을 이용해서 확인해보자
package com.example.server.controller;
import com.example.server.dto.User;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
@Slf4j
@RestController
@RequestMapping("/api/server")
public class ServerRestApiController {
// @GetMapping("/hello")
// public User hello(@RequestParam String name, @RequestParam int age){
// User user =new User();
// user.setName(name);
// user.setAge(age);
// log.info("request user : {}",user);
// return user;
// }
@PostMapping("/userId/{userId}/name/{name}")
public User hello(@RequestBody User user,
@PathVariable long userId,
@PathVariable String name,
**@RequestHeader(name="x-authorization") String xAuth,
@RequestHeader(name="custom-header") String custom**){
log.info("request user : {}",user);
log.info("x-authorization header: {}",xAuth);
log.info("custom header: {}",custom);
return user;
}
}
그리고 요청은 POST 방식으로, 아래와 같은 json 요청을 보내면, 서버측에서 클라이언트 서버에서 보낸 헤더값을 확인해볼 수 있게 된다
{
"name":"steve",
"age":20
}
🌟🌟🌟body 내부에 header와 body가 들어가고, 그 내용이 변경되는 경우🌟🌟🌟 -재사용성에 탁월!
지금 공부에 참고하고 있는 강의에서, 핀테크 산업 등의 경우에 아래처럼
body 내부에 header와 body가 들어가고, 그 내부 값이 변경되는 경우가 있다고 한다.
{
"header": {
"response_code": "200"
},
**"body"**: {
"name": "steve",
"age": 10
}
}
(클라이언트 & 서버)
이 경우, body 내부의 body 부분(위에서 굵게 표시된 부분)은 내부 값 및 형태가 변경될 수 있으므로 제네릭스를 이용해서 필드로 만들어주자
package com.example.client.dto;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Req<T> {
private Header header;
**private T body;**
@Data
@NoArgsConstructor
@AllArgsConstructor
public static class Header{
private String responseCode;
}
}
package com.example.server.dto;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Req<T> {
private Header header;
**private T body;**
@Data
@NoArgsConstructor
@AllArgsConstructor
public static class Header{
private String responseCode;
}
}
(클라이언트)
먼저, 요청을 보낼때에는 response code는 없기 때문에 아래처럼 header의 response_body 부분은 비워진 채로 보내져야 한다
{
"header": {
**"response_code": ""**
},
"body": {
"name": "steve",
"age": 10
}
}
🌟 클라이언트와 서버측에서 요청과 응답에 관련된 dto의 필드명은 맞춰주어야 한다! 그렇지 않으면 값이 null로 들어오거나 에러가 발생할 수 있다!
그런데, 제네릭스는 class를 사용할 수 없기 때문에 restTemplate에서 제공되는 parameterizedTypeReference를 이용해서
ResponseEntity<Req
위와 같이 사용할 수 있다
그리고 body에 들어가는 클래스가 바뀔때마다 전체를 건드리지 않고 내부 클래스만 바꿔주기 위해서 리턴타입을 UserResponse에서 Req
public **Req<UserResponse>** genericExchange(UserRequest userRequest,long userId, String name){
URI uri=UriComponentsBuilder.fromUriString("http://localhost:9090")
.path("/api/server/userId/{userId}/name/{name}")
.encode()
.build()
.expand(userId,name)
.toUri();
log.info("request: {}",uri);
Req<UserRequest> req= new Req();
req.setHeader(
new Req.Header()
);
req.setBody(
userRequest
);
RequestEntity<Req<UserRequest>> requestEntity=RequestEntity.post(uri)
.contentType(MediaType.APPLICATION_JSON)
.header("x-authorization","abcd")
.header("custom-header","aaa")
.body(req);
RestTemplate restTemplate= new RestTemplate();
//제네릭스는 class를 붙일 수 없음
//ParameterizedType을 이용하여 제네릭스를 하나의 타입으로 만들어주기
ResponseEntity<Req<UserResponse>> response=restTemplate.exchange(requestEntity,new ParameterizedTypeReference<Req<UserResponse>>(){});
return **response.getBody()**;
}
그러면 컨트롤러부분에서도 이 메서드를 호출시켜주고, 리턴타입을 맞춰주어야 한다
package com.example.client.controller;
import com.example.client.dto.Req;
import com.example.client.dto.UserRequest;
import com.example.client.dto.UserResponse;
import com.example.client.service.RestTemplateService;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api/client")
@RequiredArgsConstructor
public class RestApiController {
private final RestTemplateService restTemplateService;
@PostMapping("/userId/{userId}/name/{name}")
public Req<UserResponse> exchangeHeader(@RequestBody UserRequest user, @PathVariable long userId, @PathVariable String name){
return restTemplateService.genericExchange(user,userId,name);
}
}
(서버)
서버측에서는 이제
{
"header": {
"response_code": "200"
},
"body": {
"name": "steve",
"age": 10
}
}
위와 같이 상태코드도 같이 내려주어야 하는데, 이를 간단하게
요청으로 전달된 객체값이 null이면 400, 아니면 200을 내려주도록 해보자
package com.example.server.controller;
import com.example.server.dto.Req;
import com.example.server.dto.User;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
@Slf4j
@RestController
@RequestMapping("/api/server")
public class ServerRestApiController {
@PostMapping("/userId/{userId}/name/{name}")
public **Req<User>** hello(
@RequestBody Req<User> user,
@PathVariable long userId,
@PathVariable String name,
@RequestHeader(name="x-authorization") String xAuth,
@RequestHeader(name="custom-header") String custom){
log.info("request user : {}",user);
log.info("x-authorization header: {}",xAuth);
log.info("custom header: {}",custom);
Req<User> response=
new Req<>();
**Req.Header header =new Req.Header();
User body=user.getBody();
if(body==null){
header.setResponseCode("400");
}else{
header.setResponseCode("200");
}
response.setHeader(
header
);
response.setBody(
body
);**
return response;
}
}
사실 int가 null에 대해서 명확히 처리하기 위해서는 래퍼 클래스인 Integer를 사용함이 보다 맞겠지만 이해를 위해서 빠르게 진행해보자
요청을 http://localhost:8089/api/client/userId/100/name/steve 로 POST 요청을 아래와 같이 보내면,
{
"name":"steve",
"age":20
}
이번에는 위에서 설계한 것처럼 body 내부에 header와 body가 들어있는 형태로 확인해볼 수 있게 될 것이다!