본문 바로가기

카테고리 없음

[cURL] cURL정리

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