- HttpEntity
RestTemplateServiceRestTemplateService를 쓰기 위한 기본 셋팅URI 만들기RestTemplate이용한 get, postHttpEntity, @ReqestHeaderIssue
RestTemplateService
RestTemplateService를 쓰기 위한 기본 셋팅
@RestController @RequestMapping("/api/client") public class ApiController { private final RestTemplateService restTemplateService; public ApiController(RestTemplateService restTemplateService) { this.restTemplateService = restTemplateService; } @GetMapping public Req<UserResponse> getHello(){ return restTemplateService.genericExchange(); } @PostMapping public UserResponse post(@RequestBody UserRequest request){ return restTemplateService.post(request); } } @Service public class RestTemplateService { ... }
- 위와 같이 @Service를 붙여줌으로써 Spring Container가 해당 클래스를 검색해서 자동으로 Spring Bean에 등록하게 됨 → @RestController가 붙은 ApiController를 생성하면서 해당 class를 자동으로 넣어서 생성하게 해줌
URI 만들기
//query parameter 추가 URI uri = UriComponentsBuilder.fromUriString("http://localhost:9090") .path("/api/server/hello") .queryParam("name", "steve") .queryParam("age", 20) .encode().build().toUri(); //path variable URI uri = UriComponentsBuilder.fromUriString("http://localhost:9090") .path("/api/server/user/{userId}/name/{userName}") .encode().build(100, "steve");
- UriComponentsBuilder, UriComponents 등의 클래스 사용.
- 여기에서 encode() 부분이 URLEncoding을 진행함
queryParam("query", "지역").encode().build().toUri() //-> query=%EC%A4%91 ... encode() 안하면 그냥 한글로 들어가게됨
RestTemplate이용한 get, post
- RestTemplate : Synchronous client to perform HTTP requests
RestTemplate restTemplate = new RestTemplate(); //Get ResponseEntity<UserResponse> result = restTemplate.getForEntity(uri, UserResponse.class); UserResponse res = restTemplate.getForObject(uri, UserResponse.class) //Post ResponseEntity<UserResponse> res = restTemplate.postForEntity(uri, request, UserResponse.class); // RequestEntity를 통한 http 요청보내기 RequestEntity<UserRequest> requestEntity = RequestEntity .post(uri) .contentType(MediaType.APPLICATION_JSON) .header("x-authorization", "abcd") .header("custom-header", "sadf") .body(req); RestTemplate restTemplate = new RestTemplate(); ResponseEntity<UserResponse> response = restTemplate.exchange(requestEntity, UserResponse.class); // Json의 body 부분의 key 값이 계속해서 바뀔 때는 generic type을 이용! Req<UserRequest> req = new Req<>(); req.setHeader(new Req.Header()); req.setResponseBody(userReq); RequestEntity<Req<UserRequest>> requestEntity = RequestEntity .post(uri) .contentType(MediaType.APPLICATION_JSON) .header("x-authorization", "abcd") .header("custom-header", "sadf") .body(req); RestTemplate restTemplate = new RestTemplate(); // generic은 .class를 쓸수 없음! => ParameterizedTypeReferecne 이용하기 ResponseEntity<Req<UserResponse>> response = restTemplate.exchange(requestEntity, new ParameterizedTypeReference<Req<UserResponse>>(){});
HttpEntity, @ReqestHeader
import org.springframework.http.HttpEntity;
- 해당 HttpEntity를 서버 측에서 파라미터로 받게 되면 Client에서 보낸 request body를 그대로 확인이 가능함
@PostMapping("/user/{userId}/name/{userName}") public Req<User> post(@PathVariable String userId, @PathVariable String userName, HttpEntity<String> entity, @RequestBody Req<User> user, @RequestHeader("x-authorization") String authorization, @RequestHeader("custom-header") String customHeader){ log.info("authorization : {} custom : {}", authorization, customHeader); log.info("http entity : {}", entity);
Issue
- Testresttemplate을 사용할 때 , @Transactional을 붙여서 테스트를 하면 실제로는 데이터 커밋을 하지 않기 때문에 해당 request가 데이터를 아예 찾지를 못하게 됨 → [StackOverflow Spring boot test fails if @Transactional applied]
- Test rest template 은 실제로 HTTP request를 새로운 커넥션을 열어서 실행하기에, Transaction이 새로 생성되게 됨. 즉 다른 Transaction임
- MockMvc는 같은 Transaction을 사용해서 상관이 없는데
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @AutoConfigureMockMvc //@Transactional class UserRestControllerTest { @BeforeEach void setup(){ userRepository.save(new User(email, password, username, "", "")); } @Test @DisplayName("[GET] /api/user") void apitest3() throws Exception { UserDto.UserLoginRequest request = new UserDto.UserLoginRequest(); request.request = new UserDto.UserLoginRequest.Request(email, password); String userResponse = client.postForObject("/api/users/login", request, String.class); // 여기서 login을 하려고 할 때 db에 데이터가 없어서 실패가 됨 Transactional 붙이면 System.out.println(userResponse); } }