cURL 이란?
- 다양한 통신 프로토콜을 이용하여 데이터를 전송하기 위한 라이브러리
- https://github.com/curl/curl/
- http 프로토콜 테스트 시 유용하게 사용할 수 있다. 매번 검색하면서 사용해서 이번 기회에 정리한다
명령어 정리
shortcut | options | description | example |
---|---|---|---|
-X | —request | 요청 메서드를 설정 | curl -X GET http://localhost:8080 |
-H | —header | 요청 헤더를 추가 | curl -X GET http://localhost:8080/api/v1/users -H "Authorization: Bearer {token}" |
-d | —data | body 데이터를 쿼리스트링 으로 보냄 |
curl -X POST http://localhost:8080/api/v1/users/form -H“Content-Type:application/x-www-form-urlencoded” -d name=하잉 -d age=34 |
—data-urlencode | urlEncode | curl -X POST http://localhost:8080/api/v1/users/form -H“Content-Type:application/x-www-form-urlencoded” --data-urlencoded name=하잉 -d age=3 | |
-F | --form | multipart MIME 데이터 전송 | curl -X POST http://localhost:8080/api/v1/users/multipart -H “Content-Type:multipart/form-data” -F files=@/home/haedoang/1.log -F files=@/home/haedoang/2.log |
테스트 구성
# Controller
@Slf4j
@RestController
@RequestMapping("api/v1/users")
@RequiredArgsConstructor
public class UserRestController {
private final UserService userService;
@GetMapping
public ResponseEntity<List<User>> findAll(@RequestParam(required = false) String name, @RequestParam(required = false) String age) {
return ResponseEntity.ok().body(userService.list());
}
@PostMapping(value = "/json", consumes = APPLICATION_JSON_VALUE)
public ResponseEntity<User> saveJson(@RequestBody User user) {
User savedUser = userService.save(user);
return ResponseEntity.created(URI.create("api/v1/users/json/" + savedUser.getId())).body(savedUser);
}
@PostMapping(value = "/form", consumes = APPLICATION_FORM_URLENCODED_VALUE)
public ResponseEntity<User> saveFormData(User user, HttpServletRequest request) {
printHeaders(request);
User savedUser = userService.save(user);
return ResponseEntity.created(URI.create("api/v1/users/form/" + savedUser.getId())).body(savedUser);
}
@PostMapping(value = "/multipart", consumes = MULTIPART_FORM_DATA_VALUE)
public ResponseEntity<List<String>> saveMultipart(@RequestPart(value = "files", required = false) MultipartFile[] files, HttpServletRequest request) {
printHeaders(request);
return ResponseEntity.ok().body(Arrays.stream(files).map(MultipartFile::getOriginalFilename)
.collect(Collectors.toList()));
}
@PostMapping(value = "/multipart2", consumes = MULTIPART_FORM_DATA_VALUE)
public ResponseEntity<User> saveMultipartWithObject(@RequestPart(value = "files", required = false) MultipartFile[] files, @ModelAttribute User user, HttpServletRequest request) {
printHeaders(request);
User savedUser = userService.save(user);
log.info("uploadFiles : {}", Arrays.stream(files)
.map(MultipartFile::getOriginalFilename)
.collect(Collectors.toList()));
return ResponseEntity.created(URI.create("api/v1/users/multipart2/" + savedUser.getId())).body(savedUser);
}
private void printHeaders(HttpServletRequest request) {
Enumeration<String> headerNames = request.getHeaderNames();
while (headerNames.hasMoreElements()) {
String headerName = headerNames.nextElement();
log.info("Header Name - {}, Value - {}", headerName, request.getHeader(headerName));
}
}
}
# Test
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class UserAcceptanceTest {
public static final String BASE_URI = "api/v1/users";
@LocalServerPort
int port;
@BeforeEach
void setUp() {
RestAssured.port = port;
}
@Test
@DisplayName("리스트 조회")
public void getRequest() {
// when
ExtractableResponse<Response> response = RestAssured.given().log().all()
.accept(MediaType.APPLICATION_JSON_VALUE)
.when()
.get(BASE_URI)
.then().log().all()
.extract();
// then
assertThat(response.statusCode()).isEqualTo(HttpStatus.OK.value());
}
@Test
@DisplayName("사용자 등록 by json")
public void postJson() {
// given
User request = User.valueOf(null, "새로운사용자", 55);
// when
ExtractableResponse<Response> actual = RestAssured.given().log().all()
.urlEncodingEnabled(true)
.accept(MediaType.APPLICATION_JSON_VALUE)
.contentType(MediaType.APPLICATION_JSON_VALUE)
.when()
.body(request)
.post(BASE_URI + "/json")
.then().log().all()
.extract();
// then
assertThat(actual.statusCode()).isEqualTo(HttpStatus.CREATED.value());
}
@Test
@DisplayName("사용자 등록 by formdata")
public void postFormData() {
// given
User request = User.valueOf(null, "새로운사용자", 55);
// when
ExtractableResponse<Response> actual = RestAssured.given().log().all()
.contentType("application/x-www-form-urlencoded;charset=UTF-8")
.when()
.formParam("name", request.getName())
.formParam("age", request.getAge())
.post(BASE_URI + "/form")
.then().log().all()
.extract();
// then
assertThat(actual.statusCode()).isEqualTo(HttpStatus.CREATED.value());
assertThat(actual.jsonPath().getObject("", User.class).getName()).isEqualTo("새로운사용자");
}
@Test
@DisplayName("multiple file upload by multipart")
public void postMultipart() {
// when
ExtractableResponse<Response> actual = RestAssured.given().log().all()
.contentType(MediaType.MULTIPART_FORM_DATA_VALUE)
.multiPart("files", Files.newTemporaryFile())
.multiPart("files", Files.newTemporaryFile())
.multiPart("files", Files.newTemporaryFile())
.when()
.post(BASE_URI + "/multipart")
.then().log().all()
.extract();
// then
assertThat(actual.statusCode()).isEqualTo(HttpStatus.OK.value());
assertThat(actual.body().jsonPath().getList("", String.class)).hasSize(3);
}
@Test
@DisplayName("파일 업로드와 오브젝트 파라미터 by multipart")
public void postMultipartWithObj() {
// given
User request = User.valueOf(null, "새로운사용자", 34);
// when
ExtractableResponse<Response> actual = RestAssured.given().log().all()
.contentType(MediaType.MULTIPART_FORM_DATA_VALUE)
.multiPart("files", Files.newTemporaryFile())
.multiPart("files", Files.newTemporaryFile())
.multiPart("files", Files.newTemporaryFile())
.multiPart(new MultiPartSpecBuilder(request.getName()).controlName("name").charset("UTF-8").build())
.multiPart("age", request.getAge())
.when()
.post(BASE_URI + "/multipart2")
.then().log().all()
.extract();
// then
assertThat(actual.statusCode()).isEqualTo(HttpStatus.CREATED.value());
}
- 테스트는 https://rest-assured.io/ 로 검증을 하였다.
- rest-assured는 실제 web envoronment 요청을 보내기 때문에 e2e 테스트 시 용이하다.
이슈
- 한글 인코딩 이슈가 있어서
x-www-form-urlencoded
요청 시charset=UTF-8
을 설정해주어야 한다. multipart-form/data
에서는multiPart("key", "value")
로 보낼 경우 file로 인식한다.- 아래와 같이 charset을 지정하여 해결할 수 있다. (삽질엄청했다ㅜ ㅜ)
new MultiPartSpecBuilder(request.getName()).controlName("name").charset("UTF-8").build();
https://github.com/haedoang/springboot/tree/master/spring-sample-rest