SW-PRODUCT/개발-웹닭·HTTP

PUT 메소드에서의 application/x-www-form-urlencoded

굴돌 2014. 11. 11. 00:26



최근에 보안권고 중에 GET/POST만 사용하고, PUT/DELETE를 포함한 나머지 모든 메소드를 차단하라는 권고를 봤다.

요즘 API 추세는 GET/POST/PUT/DELETE를 모두 사용하는 추세인데 두개만 쓰라니...

당황 스러웠는데...

Servlet specification에 POST만 form field에 접근할 수 있도록 제한해 두었단다..PUT은 안되고...

즉, PUT으로 form 데이터를 보내면 ServletRequest.getParameter*() 호출을 했을 때 값을 못 읽어온단다...

ㄴ 링크: http://stackoverflow.com/questions/5894270/springmvc-is-not-recognizing-request-body-parameters-if-using-put


spring framework에서는 3.1부터 이 문제 해결을 위해 HttpPutFormContentFilter를 도입했다고 한다.

ㄴ 링크: http://docs.spring.io/spring/docs/current/spring-framework-reference/html/mvc.html#mvc-ann-form-urlencoded-data


흠..REST 한답시고 ajax를 이용해 PUT으로 호출을 해봤으면 이 문제를 알았을텐데...뭐 이렇게 놓고보면 보안권고에서 "쓸데없는" PUT을 막으라고 한게 어느정도 이해는 된다....물론 이것때문에 PUT을 쓸데없다 한건지는 모르겠지만..


결국... Servlet specification을 따르는 form을 쏠 때 사용되는 content-type인 "application/x-www-form-urlencoded"를 사용한다면...

이것을 이용해서 ajax 호출을 하게 되면 PUT을 쓰는것 자체가 spec을 벗어난게 된다.


결론은... REST 스럽게 API를 구축할것이면 호출하는쪽이 브라우저가 아니라고 가정하는게 합리적인거고,

content-type이 "application/x-www-form-urlencoded"인 쪽보다는 "*/json" 혹은 "*/xml" 형태인 것이 더 적합해 보인다는거고...

Spring의 controller에서도 @ResponseBody로 받는 쪽이 더 적절하다는거다...


근데 또 골치아픈건, 사람들은 습관적으로 content-type에 "application/x-www-form-urlencoded"를 넣어서 던질텐데, 서버에서 이걸 받아준다면 PUT 호출 들어왔을 때 뻔한 문제가 발생해서 client가 삽질할 것이고,(위의 filter를 안쓴다는 가정 하에)

"application/x-www-form-urlencoded"를 무조건 튕겨내 버리면 그것도 곤란하고....


사실 현실적으로 "상식적인 상황"이라면...

브라우져에서 json/xml으로 호출하는 경우는 URL에 직접 넣은 GET 방식일 때 뿐일 것이다.

브라우져에서 PUT/POST로 json/xml을 호출한다는건 이미 submit 버튼 눌러 곧바로 요청했다기보다 ajax를 이용해 호출됐다고 봐야한다.

ajax는 이미 api client라고 봐야 하기 때문에 이걸 굳이 form과 같은 content-type으로 호출할 필요는 없어보인다.

ㄴ form 태그에 enctype이라는 이름으로 content-type을 지정할 수 있게 되어 있긴 하지만, spec상 세가지만 쓸 수 있어서 "*/json", "*/xml"류를 쓰는건 spec에 어긋난다. (http://www.w3.org/TR/html5/forms.html#attr-fs-enctype)


결론은?

브라우져에서 직접 들어오는 경우(주소창 입력=GET, form submit=POST)가 아닌 ajax 호출에서는

json 혹은 xml에 맞는 content-type을 넣어주고,

controller에서도 @ResponseBody+@RequestBody 쌍을 이용해 처리하는게 깔끔하다.

브라우져에서 직접 들어오는 경우라고 한다면 브라우져 답게 GET/POST만 사용하고.

이렇게 함으로써 Servlet Specification과 RESTful 사이에 어설프게 껴벼린 PUT의 처리를 고민할 필요가 사라지....지 않을까?