반응형

해당 내용은 POST 방식만을 고려한 내용이다.

Multipart/form-data의 HTTP 간단한 구조 알기


이미지를 업로드를 할 때 이미지만 업로드하는 것이 아닌 부가적인 정보들도 같이 업로드 해야하는 경우가 많다.

헤더는 Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW와 같이

요청을 하고, boundary로 각각의 정보를 구분하여 전송하게 된다.

HTTP Reqeust

POST /urls/url/test
Host: localhost:8080
Cache-Control: no-cache
Postman-Token: f15dcf2b-ffdf-6e53-3411-a7bf27226668
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW

------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="user_name"

"monkey"
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="image"; filename="스크린샷 2022-11-26 오후 6.58.50.png"
Content-Type: image/png

------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="banner"; filename="짱구 이미지.png"
Content-Type: image/png

------WebKitFormBoundary7MA4YWxkTrZu0gW--

간단하게 설명하자면,

  • Content-Type을 multipart/form-data: form-data로 multipart를 보내겠다는 의미이다.
  • bonudary: 랜덤으로 브라우저가 생성하는 값이다. 이 값으로 강 form-data들을 구분한다.
  • name: 파라미터의 이름을 담는 필드이다. 현재 여기서 보낸 필드명은 user_name이다.
    user_name의 값은 단순한 텍스트이다.
    값은 monkey이다.
  • image/png: user_name과 다르게 파일은 Content-Type이 image/png이다.
  • 마지막 코드에서 boundary 마지막에 ‘--’이 붙어있는데 이건 boundary의 마지막을 의미한다.

MultiPart만 받는 방법


@RequestParam

@RequestParam(value = "imageOne") MultipartFile image1,
@RequestParam(value = "imageTwo", required = false) MultipartFile image2

@RequestPart

@RequestPart(value = "imageOne") MultipartFile image1,
@RequestPart(value = "imageTwo", required = false) MultipartFile image2

애노테이션 사용하지 않기

MultipartFile image1,
MultipartFile image2

위 두 방법의 차이점

  • @RequestPart와 @RequestParam은 reuquire = true가 default이기 때문에 필수로 파라미터를 넘겨주지 않으면
    Required request part 'imageTwo' is not present 에러가 발생한다. require = false로 해결할 수 있다.
    파일이 없어도 동일하다. 이유는 파일이 없으면 Conten-Type도 없기 때문이다.
  • Content-Disposition: form-data; name="imageTwo"; filename="" Content-Type:
  • 반면 애노테이션을 사용하지 않으면 그냥 null로 넘어온다.

공통점은

  • require = false일 경우 파라미터를 넘기지 않으면 null로 들어온다.

@RequestParam, @RequstPart의 차이점


@RequestParam이란?

공식 문서(since 2.5)

Annotation which indicates that a method parameter should be bound to a web request parameter.

Supported for annotated handler methods in Spring MVC and Spring WebFlux as follows:

  • In Spring MVC, "request parameters" map to query parameters, form data, and parts in multipart requests. This is because the Servlet API combines query parameters and form data into a single map called "parameters", and that includes automatic parsing of the request body.
  • In Spring WebFlux, "request parameters" map to query parameters only. To work with all 3, query, form data, and multipart data, you can use data binding to a command object annotated with [ModelAttribute](https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/bind/annotation/ModelAttribute.html).

If the method parameter type is [Map](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/Map.html) and a request parameter name is specified, then the request parameter value is converted to a [Map](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/Map.html) assuming an appropriate conversion strategy is available.

If the method parameter is [Map<String, String>](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/Map.html) or [MultiValueMap<String, String>](https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/util/MultiValueMap.html) and a parameter name is not specified, then the map parameter is populated with all request parameter names and values.

  1. request parameter, mulitpart를 지원한다.

@RequestPart란?

공식 문서(since spring 3.1)

Annotation that can be used to associate the part of a "multipart/form-data" request with a method argument.

Supported method argument types include [MultipartFile](https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/multipart/MultipartFile.html) in conjunction with Spring's [MultipartResolver](https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/multipart/MultipartResolver.html) abstraction, jakarta.servlet.http.Part in conjunction with Servlet multipart requests, or otherwise for any other method argument, the content of the part is passed through an [HttpMessageConverter](https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/http/converter/HttpMessageConverter.html) taking into consideration the 'Content-Type' header of the request part. This is analogous to what @[RequestBody](https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/bind/annotation/RequestBody.html) does to resolve an argument based on the content of a non-multipart regular request.

Note that @[RequestParam](https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/bind/annotation/RequestParam.html) annotation can also be used to associate the part of a "multipart/form-data" request with a method argument supporting the same method argument types. The main difference is that when the method argument is not a String or raw MultipartFile / Part, @RequestParam relies on type conversion via a registered [Converter](https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/core/convert/converter/Converter.html) or [PropertyEditor](https://docs.oracle.com/en/java/javase/17/docs/api/java.desktop/java/beans/PropertyEditor.html) while [RequestPart](https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/bind/annotation/RequestPart.html) relies on [HttpMessageConverters](https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/http/converter/HttpMessageConverter.html) taking into consideration the 'Content-Type' header of the request part. [RequestParam](https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/bind/annotation/RequestParam.html) is likely to be used with name-value form fields while [RequestPart](https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/bind/annotation/RequestPart.html) is likely to be used with parts containing more complex content e.g. JSON, XML).

  1. MultiPart를 지원한다.
  2. @RequeestParam은 name-value를 지원하지만, @RequestPart는 보다 복잡한 JSON, XML등과 같은 것들을 지원한다.
  3. 요청 헤더의 Content-Type에 해당하는 HttpMessageConverter를 사용한다.

차이점

@RequstParam

  • query parameter, form data, Mumltipart 등 많은 요청 파라미터를 지원한다.
  • 메소드 파라미터 타입이 String 또는 MultipartFile/Part가 아닌 경우 Converter 또는 PropertyEditor를 참조해 변환을 시도한다.

@RequestPart

  • MultiPart를 지원한다.
  • @RequeestParam은 name-value를 지원하지만, @RequestPart는 보다 복잡한 JSON, XML등과 같은 것들을 지원한다.
  • 요청 헤더의 Content-Type에 해당하는 HttpMessageConverter를 사용한다.

qeury parameter와 File 같이 받기


사용이 불가한 경우

Content type 'application/octet-stream' not supported 발생

  • @RequestPart만 사용
  • MultiPart에 @RequestParam, queryParameter에 @RequestPart

사용 가능한 경우

  • @RequestParam만 사용
  • queryParameter에 @RequestParam, Multipart에 @RequestPart

DTO로 snake case → camel case로 받을 수 없을까?


POST 방식이고, body로 오는 json content-type이면, jackson의 @JsonProperty로 쉽게 해결이 가능하다.

하지만 GET 방식의 query string이거나, form-data, multipart이면 지원하지 않는다.

  • case가 같을 경우 GET, POST 모두 @ModelAttribute 지원함.
  • case가 다를 경우
    • POST json body이면 jackson @JsonProperty로 변환 가능
    • GET query string 안됨.
    • form-data, multipart는 안됨
      • form-data에 value를 json 형태로 넘기면 value자체는 @JsonProperty로 변환 가능
        즉 key는 변환

@RequestParam을 사용하면 하나씩 컨트롤로에 필드를 지정해야 하기 때문에 불편하다.

현재 나의 상황은 multipart이면서 query string인 상황이기 때문에 이 부분에 대해서 설명한다.

시도중- String만 들어옴. image는 안들어옴.

@RequestParam Map<String, Object> params
  ) throws IOException {
    ObjectMapper mapper = new ObjectMapper();
    MallImageCreateRequest user = mapper.convertValue(params, MallImageCreateRequest.class);

참고


https://somuchthings.tistory.com/160?category=983431

공식 문서(since spring 3.1)

공식 문서(since 2.5)

반응형
복사했습니다!