Whiteship's Note


[Spring 3.0] HiddenHttpMethodFilter

Spring/3.0 : 2009.06.10 10:47


어제 밤에 스프링 ROO가 제공하는 REST 코드를 보다가 잠들었는데, 아침에 사부님 댓글을 보니, DELETE와 PUT method를 현재 브라우저와 HTML에서는 완전히 지원하지를 않더군요. 그래서 스프링 레퍼런스를 봤더니 역시나..

org.springframework.web.filter.HiddenHttpMethodFilter

이 녀석을 제공해주고 있었습니다. 사용하는 방법은 간단합니다.

    <filter>
        <filter-name>UrlRewriteFilter</filter-name>
        <filter-class>org.tuckey.web.filters.urlrewrite.UrlRewriteFilter</filter-class>
    </filter>

    <filter-mapping>
        <filter-name>UrlRewriteFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

이렇게 web.xml에 추가해주면 되죠. 어젯밤엔 졸려서 이부분을 그냥 지나간것 같습니다.

해주는 일은 post method로 넘겨온 파라메터 중에 _method가 가지고 온 값으로 HTTP method를 실제 원하는 method로 바꿔주는 겁니다.

<form:form method="delete">
    <p class="submit"><input type="submit" value="Delete Pet"/></p>
</form:form>

스프링 폼 태그를 이용해서 저렇게 method를 명시해주면 히든 필드 _method에 delete라는 값을 요청에 같이 포함해서 보내주며 실제 보내는 요청은 post가 됩니다. 하지만 이 필터가 _method의 값인 delete를 읽고서 HTTP method를 DELETE로 변경해주고, 그 결과 아래에 있는 핸들러가 이 요청을 처리하게 됩니다.

@RequestMapping(method = RequestMethod.DELETE)
public String deletePet(@PathVariable int ownerId, @PathVariable int petId) {
    this.clinic.deletePet(petId);
    return "redirect:/owners/" + ownerId;
}

REST 스타일을 적용할 때 꼭 필요한 라이브러리 인 듯 합니다.
top

  1. Favicon of http://jjaeko.tistory.com BlogIcon 째코 2009.06.10 13:19 PERM. MOD/DEL REPLY

    ssimini에서도 restful을 지원할때 별도의 파라미터(_http__method)를 이용 하빈다...
    restful 정말 매력적인듯 하네요.

    Favicon of http://whiteship.me BlogIcon 기선 2009.06.10 17:44 PERM MOD/DEL

    오호 ssimini도 REST를 지원하는군요~

  2. Favicon of http://kwon37xi.egloos.com BlogIcon 권남 2009.07.06 15:35 PERM. MOD/DEL REPLY

    저기 HiddenHttpMethodFilter 랑 org.tuckey.web.filters.urlrewrite.UrlRewriteFilter 가 무슨상관인지 잘 모르겠어요... org.tuckey.web.filters.urlrewrite.UrlRewriteFilter 를 설정하면 자동으로 HiddenHttpMethodFilter가 적용되는건가요??

    Favicon of https://whiteship.tistory.com BlogIcon 기선 2009.07.06 15:39 신고 PERM MOD/DEL

    UrlRewriteFilter랑은 아무 관계가 없는 것 같습니다. 다만 이 글을 올릴 때 제가 전에 못 봤었던 부분을 같이 긁어다 붙인것 같네요.

Write a comment.


스프링 ROO에서 RESTful 코드 보기

모하니?/Coding : 2009.06.09 22:58


vote.roo 예제에서 ChoiceController_Roo_Controller 애스팩트에서 URL 맵핑과 뷰 맵핑 정보를 위주로 살펴보겠습니다.

/choice, POST => create => "choice/create" or "redirect:/choice/" + choice.getId();
/choice/form, GET => createForm => "choice/create"
/choice/{id}, GET => show => "choice/show"
/choice, GET => list => "choice/list"
PUT => update => "choice/update" or "redirect:/choice/" + choice.getId();
/choice/{id}/form, GET => updateForm => "choice/update"
/choice/{id}, DELETE => delete => "redirect:/choice"

이 정보들이 개인적으로 제일 궁금했었기 때문에, 이렇게 간추려서 정리해봤습니다. 이 정보들을 보다가 뷰에서 요청을 보내는 방법이 궁금했습니다. 흠.. GET은 알겠고, POST는 폼에서 보냈을 테고 DELETE는 어케 보냈을까나... 하면서 /choice/list.jsp 파일을 열어 보았습니다.

                    <td>
                        <form:form action="/vote/choice/${choice.id}" method="GET">
                            <input alt="Show choice" src="/vote/static/images/show.png" title="Show choice" type="image" value="Show choice"/>
                        </form:form>
                    </td>
                    <td>
                        <form:form action="/vote/choice/${choice.id}/form" method="GET">
                            <input alt="Update choice" src="/vote/static/images/update.png" title="Update choice" type="image" value="Update choice"/>
                        </form:form>
                    </td>
                    <td>
                        <form:form action="/vote/choice/${choice.id}" method="DELETE">
                            <input alt="Delete choice" src="/vote/static/images/delete.png" title="Delete choice" type="image" value="Delete choice"/>
                        </form:form>
                    </td>

헛 글쿠나.. 걍 폼에서 method를 명시해 줬구나.. @_@ 설정은 별로도 해줘야 할 거 없으려나..하고 web.xml과 vote-servlet.xml을 살펴봤지만 별다른 건 없더군요 아.. web.xml에 urlrewriter 필터를 적용했던데 스프링에서 사용하는 녀석이니 한 번 봐봐야겠습니다.

org.tuckey.web.filters.urlrewrite.UrlRewriteFilter

vote-servlet.xml에 있던 설정은 간단했습니다. internalResourceViewResolver에 prefix suffix 줘서 뷰이름으로 jsp 뷰 찾아가도록 빈을 등록했고, 파일 업로드를 하는지 머티파트 머시기 빈을 하나 등록했고, 발생한 예외에 따라 보여줄 뷰 이름을 등록한 예외처리뷰도 보이고 컴포넌트 스캔까지 네 개가 명시적으로 등록되어 있습니다.

REST 적용 간단해 보이네요~

참고자료: RESTful한 웹 서비스 만들기
top

  1. Favicon of http://toby.epril.com BlogIcon 2009.06.10 07:30 PERM. MOD/DEL REPLY

    DELETE, PUT이 브라우저와 서버에서 잘 먹나?

    Favicon of https://whiteship.tistory.com BlogIcon 기선 2009.06.10 09:45 신고 PERM MOD/DEL

    아니요. 잘 안 되나봐요.
    HTML5에서는 DELETE랑 PUT도 잘 지원해줄꺼라는데 뭐 IE8에서나 잘 돌아갈 것 같고 그 전꺼에서는 잘 안 되겠죠.
    그래서 org.springframework.web.filter.HiddenHttpMethodFilter
    이 녀석을 제공해주나봐요.

    이걸 빠트렸네요.

Write a comment.


REST in Spring 3: @MVC

Spring/etc : 2009.03.09 11:10


참고: REST in Spring 3: @MVC

스프링 3.0의 RESTful 기능

- URI 템플릿
@PathVariable 애노테이션으로 URI의 특정 위치의 값을 맵핑해올 수 있음.
@RequestMapping("/hotels/{hotelId}")
public String getHotel(@PathVariable hotelId, ModelMap model) {
  List<Hotel> hotels = hotelService.getHotels();
  model.addAttribute("hotels", hotels);
  return "hotels";
}
이때 /hotels/1 이런 요청이 들어오면 hotelId 매개변수로 1이라는 값을 받아올 수 있음.

두 개 이상 맵핑 할 수도 있고 변수명을 다르게 줄 수도 있음.
@RequestMapping(value="/hotels/{hotel}/bookings/{booking}", method=RequestMethod.GET)
public String getBooking(@PathVariable("hotel") long hotelId, @PathVariable("booking") long bookingId, ModelMap model) {
  Hotel hotel = hotelService.getHotel(hotelId);
  Booking booking = hotel.getBooking(bookingId);
  model.addAttribute("booking", booking);
  return "booking";
}

Ant 스타일 경로를 지정할 수도 있음.
@RequestMapping(value="/hotels/*/bookings/{booking}", method=RequestMethod.GET)
public String getBooking(@PathVariable("booking") long bookingId, ModelMap model) {

}

데이터 바인딩도 사용할 수 있음.
@InitBinder
public void initBinder(WebDataBinder binder) {
  binder.initBeanPropertyAccess();
  SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
  dateFormat.setLenient(false);
  binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, false));
}

@RequestMapping("/hotels/{hotel}/dates/{date}")
public void date(@PathVariable("hotel") String hotel, @PathVariable Date date)
                throws IOException {

}
/hotels/1/dates/2008-12-18 이런 요청을 받으면 2008-12-18 이 부분 문자열을 Date 타입으로 바인딩 해 줌.

- Content Negotiation
Accept HTTP 헤더로 뷰를 판단함. 서버 응답은 Content-Type를 통해 뷰를 전달한다. 이런 과정을 content negotiation이라고 한다.

주요 클래스: ContentNegotiatingViewResolver

- Views
RSS 피드를 만들 때 사용할 수 있는 뷰: AbstractAtomFeedView, AbstractRssFeedView
XML 뷰: MarshallingView
JSON 뷰: JacksonJsonView

- HTTP Method Conversion
HTTP에서는 GET, POST, DELETE, PUT을 제공하지만 HTML은 GET, POST만 지원한다. 따라서 POST를 사용하면서 hidden parameter로 Method를 명시하면 해당 Method로 변환해주는 HiddenHttpMethodFilter 제공.
스프링 form 태그에서 이 기능을 지원하기 때문에 form태그의 method에 delete, put도 사용 가능.
<form:form method="delete">
    <p class="submit"><input type="submit" value="Delete Pet"/></p>
</form:form>

@RequestMapping(method = RequestMethod.DELETE)
public String deletePet(@PathVariable int ownerId, @PathVariable int petId) {
    this.clinic.deletePet(petId);
    return "redirect:/owners/" + ownerId;
}

- ETag support
ETga는 HTTP 1.1 호환 응답 헤더로 해당 URL에 있는 컨텐츠가 변경 됐는지 확인할 때 사용.
스프링은 ShallowEtagHeaderFilter를 제공하여 JSP로 랜더링한 결과를 캐싱하고 MD5 해시를 만들고 응답에 ETag를 반환해준다. 다음에도 사용자가 같은 자원을 요청하면 이전에 만든 해시를 If-None-Match 값으로 사용한다. 두 개 해시값을 비교해서 같으면 304(Not Modified)를 반환한다.

차후에 제공할 deep ETag는 모데인 객체나 RDBMS 테이블과 관련되어 있어 좀 더 복잡한데 JPA의 @Version(낙천적-롹킹할 때 사용하는 애노테이션) 기반 또는 ApsectJ의 애스팩트로 지원할 예정~ 이라고 합니다.
top

  1. slgmoney 2009.03.09 12:21 PERM. MOD/DEL REPLY

    제가 궁금해서 그러는데요. 지금 스프링3 m2 버전 .. 이 버전 사용해도 괜찮은건지 궁금합니다. 스프링3가 나올때까지 기다려야 하는건지.. m2 라는것이 완전하진 않지만 사용해도 괜찮다는건지 모르겠네요.. 초보의 질문;; 일단 m이라는게 마일스톤이란 뜻이 맞나요? 그리고 이걸 왜 m2 라고 붙이나요?

    Favicon of http://whiteship.me BlogIcon 기선 2009.03.09 12:46 PERM MOD/DEL

    사용해도 괜찮습니다. 스프링은 충분한 개발자 테스트를 제품과 함께 제공하기 때문이죠.

    마일스톤이라는 뜻 맞구요. m2는 두 번째 마일스톤 버전을 줄여서 표현한 겁니다.

  2. ISU 2010.07.08 19:53 PERM. MOD/DEL REPLY

    안녕하십니까 spring 관련 정보를 많이 얻고 있습니다. 먼저 감사 드리고..
    @PathVariable을 사용하는데 도움을 얻고자 이렇게 문의를 드립니다.

    @RequestMapping(value={"/user/{id}","/user/{id}/book/{cd}"})
    public String home(@PathVariable String id, @PathVariable String cd

    이렇게 처리를 하면 첫번째 url로 접속시 에러가 발생합니다.

    @RequestParam(value = "cd", required = false) String cd

    로 처리를 하면 두번째 url로 접속을 하면 cd가 null로 떨어집니다

    url에 따라 cd도 param으로 받아야 하는데... 가능한 방법이 있을까요?

    Favicon of http://whiteship.me BlogIcon 기선 2010.07.08 23:36 PERM MOD/DEL

    첫번째 질문, URL 두개를 하나의 핸들러에 매핑시켰는데 그 중 첫번째 것으로 접근시 에러나는건 @PathVariable String cd에 바인딩할 부분이 URL에 없어서 그런것 같구요.

    두번째 질문, @RequestParam을 사용한 코드는 URL의 파라미터 부분.. 그러니깐.. member?cd=xxx 이런 부분을 매핑할 때 사용하는 것이지 URI 템플릿을 바인딩 할땐 @PathVariable을 사용하셔야 됩니다. 따라서 요청하신 URL에서 ?cd=xxx 이런 부분이 없기 때문에 null이 들어오는게 아닐까 싶네요.

    말씀하신대로 핸드러를 둘로 나누시는게 좋겠습니다.

  3. ISU 2010.07.08 20:24 PERM. MOD/DEL REPLY

    일단 url별로 두개로 나누고 한쪽에서 return "forward:~" 처리 했습니다.
    혹 더 좋은 솔루션이 있으면 코멘트 부탁드립니다.

    Favicon of http://whiteship.me BlogIcon 기선 2010.07.08 23:38 PERM MOD/DEL

    잘 처리하신것 같네요. forward: 프리픽스를 저럴때 사용할 수 있겠구요. 전 forward:를 잘 안써봐가지고요.ㅋㅋ

  4. ISU 2010.07.09 08:13 PERM. MOD/DEL REPLY

    ^^ 답변 감사드립니다~

    Favicon of http://whiteship.me BlogIcon 기선 2010.07.09 09:59 PERM MOD/DEL

    넵~ 보통은 질문만하고 가지는데 감사인사.. 감사합니다.

Write a comment.