본문 바로가기
Spring

[Spring] HTTP PUT 전송시 Command Object 바인딩(binding) 실패 (on RESTful)

by Real Iron 2019. 2. 8.

J2EE Servlet 명세에서 인코딩된(content type : application/x-www-form-urlencoded) HTTP PUT 메소드는 지원하지 않기 때문에 폼에 값을 담은 후 PUT으로 서버에 전송할 경우 Command Object 바인딩이 자동으로 되지 않는다.


해결책은 아래와 같다. 



출처 : http://static.springsource.org/spring/docs/current/spring-framework-reference/html/mvc.html


16.3.3.11 Working with "application/x-www-form-urlencoded" data


The previous sections covered use of @ModelAttribute to support form submission requests from browser clients. The same annotation is recommended for use with requests from non-browser clients as well. However there is one notable difference when it comes to working with HTTP PUT requests. Browsers can submit form data via HTTP GET or HTTP POST. Non-browser clients can also submit forms viaHTTP PUT. This presents a challenge because the Servlet specification requires the ServletRequest.getParameter*() family of methods to support form field access only for HTTP POST, not for HTTP PUT.

To support HTTP PUT requests, the spring-web module provides the filter HttpPutFormContentFilter, which can be configured in web.xml:


필터 추가

WEB.xml
<filter>
  <filter-name>httpPutFormFilter</filter-name>
  <filter-class>org.springframework.web.filter.HttpPutFormContentFilter</filter-class>
</filter>

<filter-mapping>
  <filter-name>httpPutFormFilter</filter-name>
  <servlet-name>dispatcherServlet</servlet-name>
</filter-mapping>

<servlet>
  <servlet-name>dispatcherServlet</servlet-name>
  <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
</servlet>

 


The above filter intercepts HTTP PUT requests with content type application/x-www-form-urlencoded, reads the form data from the body of the request, and wraps the ServletRequest in order to make the form data available through the ServletRequest.getParameter*() family of methods.


참고 : http://static.springsource.org/spring/docs/3.1.x/javadoc-api/org/springframework/web/filter/HttpPutFormContentFilter.html




기타2




Spring HttpPutFormContentFilter



<현상>

- REST API를 설계하던 중 form data가 POST방식에서는 정상 동작하지만 PUT에서는 동작하지 않는 현상을 발견


<내용>

- POST의 경우 idempotent  하지 않으며, 리소스의 위치를 지정하지 않을 경우 리소스를 새로 생성한다.

- PUT의 경우 idempotent  하며,  리소스의 위치를 명확히 지정하여 생성/수정을 위해서 사용한다.

- PATCH의 경우 idempotent 하며, 리소스의 위치를 명확히 지정하여 일부 속성을 수정하기 위해서 사용한다.   


<확인>

- 현재 Servlet Spec에서 HTTP POST를 위해서는 form data를 사용하지만, HTTP PUT or PATCH에 대해서는 사용이 불가능하다.

 아마도 수정기능을 위해서 support할 필요가 없다고 느껴서 일지도 모르겠지만 개발을 하다보면 불편한 점을 느끼게 된다.


<해결>

- Spring에서 다음과 같은 Class를 제공한다. 

Class HttpPutFormContentFilter


이 필터는 'application/x-www-form-urlencoded' type의 HTTP PUT & PATCH request를 받아서 HTTP POST Request와 같은 형태로 이용할 수 있도록 form data를 만든다.




참고사이트

http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.6

http://docs.spring.io/spring/docs/current/javadoc-pi/org/springframework/web/filter/HttpPutFormContentFilter.html

https://1ambda.github.io/javascripts/rest-api-put-vs-post/





기타3




이번에 새로 스프링(Spring) 기반의 프로젝트를 구성하면서,
Rest 관련 메소드에 HttpMethod 를 다양하게 적용해보려고 했다.

컨트롤러를 작성하면서 RestController 메소드들에
GET / POST / PUT / DELETE 를 통한 기능 연결을 사용해서 개발하고 있었다.






용도에 따른 확실한 분류를 적용해볼 수 있다고 생각했는데.
미쳐 몰랐던 사실이 하나 있었다.

HttpMethod.PUT 을 적용한 컨트롤러 메소드에서는
@RequestBody 는 파라마터로 받는데,
@RequestParam 은 파라미터로 받지 못했다.
403 에러가 떨어져서 이유찾느라 삽질을 좀 했다는.. ㅠㅠ

처음에는 오타 같은 실수인줄 알고 헤맸는데...
찾아보니 원래 PUT메소드의 설계 자체가 그렇게 되어있다고 한다.



(참고 링크)
http://stackoverflow.com/questions/8250439/can-spring-mvc-have-request-parameters-for-an-http-put-method-or-must-i-use-pos


stackoverflow 질문

Can Spring MVC have request parameters for an HTTP PUT method, or must I use post? Which should I use to be RESTful?

그래서 해결방법은, Configuration 에 PUT메소드 관련한 필터 두개만 달아주면 된다.

이렇게하면 Put에서 parameter 를 받을 수 있게 해줌.


1. 필터 추가

Java Configuration
@Bean
public HttpPutFormContentFilter httpPutFormFilter() {
return new HttpPutFormContentFilter();
}
@Bean
public OrderedHttpPutFormContentFilter httpPutFormContentFilter() {
return new OrderedHttpPutFormContentFilter();
}

 

@Configuration 클래스에 요렇게 필터 두개만 달아주면 해결!