<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    posts - 431,  comments - 344,  trackbacks - 0

    In the last couple of years, REST has emerged as a compelling alternative to SOAP/WSDL/WS-*-based distributed architectures. So when we started to plan our work on the next major release of Spring – version 3.0, it was quite clear to us that we had to focus on making the development of 'RESTful' Web services and applications easier. Now, what is and isn't 'RESTful' could be the topic of a whole new post all together; in this post I'll take a more practical approach, and focus on the features that we added to the @Controller model of Spring MVC.

    A Bit of Background

    Ok, I lied: there is some background first. If you really want to learn about the new features, feel free to skip to the next section.

    For me, work on REST started about two years ago, shortly after reading the highly recommended book RESTful Web Services from O'Reilly, by Leonard Richardson and Sam Ruby. Initially, I was thinking about adding REST support to Spring Web Services, but after working a couple of weeks on a prototype, it became clear to me that this wasn't a very good fit. In particular, I found out that I had to copy most of the logic from the Spring-MVC DispatcherServlet over to Spring-WS. Clearly, this was not the way to go forward.

    Around the same time we introduced the annotation-based model of Spring MVC. This model was clearly an improvement to the former, inheritance-based model. Another interesting development at that time was the development of the JAX-RS specification. My next attempt was to try and merge these two models: to try and combine the @MVC annotations with the JAX-RS annotations, and to be able to run JAX-RS applications within the DispatcherServlet. While I did get a working prototype out of this effort, the result was not satisfactory. There were a number of technical issues which I won't bore you with, but most importantly the approach felt 'clunky' and unnatural for a developer who was already used to Spring MVC 2.5.

    Finally, we decided to add the RESTful functionality to features to Spring MVC itself. Obviously, that would mean that there would be some overlap with JAX-RS, but at least the programming model would be satisfactory for Spring MVC developers, both existing and new ones. Additionally, there are already three JAX-RS implementations offering Spring support (Jersey, RESTEasy, and Restlet). Adding a fourth to this list did not seem a good use of our valuable time.

    RESTful features in Spring MVC 3.0

    Now, enough of the background, let's look at the features!

    URI Templates

    A URI template is a URI-like string, containing one or more variable names. When these variables are substituted for values, the template becomes a URI. For more information, see the proposed RFC.

    In Spring 3.0 M1, we introduced the use of URI templates through the @PathVariable annotation. For instance:

     
    1.@RequestMapping("/hotels/{hotelId}")
    2.public String getHotel(@PathVariable String hotelId, Model model) {
    3.    List<Hotel> hotels = hotelService.getHotels();
    4.    model.addAttribute("hotels", hotels);
    5.    return "hotels";
    6.}

    When a request comes in for /hotels/1, that 1 will be bound to the hotelId parameter. You can optionally specify the variable name the parameter is bound to, but when you compile your code with debugging enabled that is not necessary: we infer the path variable name from the parameter name.

    You can also have more than one path variable, like so:

     
    1.@RequestMapping(value="/hotels/{hotel}/bookings/{booking}", method=RequestMethod.GET)
    2.public String getBooking(@PathVariable("hotel") long hotelId, @PathVariable("booking") long bookingId, Model model) {
    3.    Hotel hotel = hotelService.getHotel(hotelId);
    4.    Booking booking = hotel.getBooking(bookingId);
    5.    model.addAttribute("booking", booking);
    6.    return "booking";
    7.}

    This would match requests like /hotels/1/bookings/2, for instance.

    You can also combine the use of Ant-style paths and path variables, like so:

     
    1.@RequestMapping(value="/hotels/*/bookings/{booking}", method=RequestMethod.GET)
    2.public String getBooking(@PathVariable("booking") long bookingId, Model model) {
    3.    ...
    4.}

    and you can use data binding, too:

     
    01.@InitBinder
    02.public void initBinder(WebDataBinder binder) {
    03.    SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
    04.    binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, false));
    05.}
    06.  
    07.@RequestMapping("/hotels/{hotel}/dates/{date}")
    08.public void date(@PathVariable("hotel") String hotel, @PathVariable Date date) {
    09.    ...
    10.}

    The above would match /hotels/1/dates/2008-12-18, for instance.

    Content Negotiation

    In version 2.5, Spring-MVC lets the @Controller decide which view to render for a given request, through its View, view name, and ViewResolver abstractions. In a RESTful scenario, it is common to let the client decide the acceptable representations, via the Accept HTTP header. The server responds with the delivered representation via the Content-Type header. This process is known as content negotiation.

    One issue with the Accept header is that is impossible to change it in a web browser, in HTML. For instance, in Firefox, it's fixed to

    Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8

    So what if you want to link to a PDF version of a particular resource? Looking at the file extension is a good workaround. For example, http://example.com/hotels.pdf retrieves the PDF view of the hotel list, as does http://example.com/hotels with an Accept header of application/pdf.

    This is what the ContentNegotiatingViewResolver does: it wraps one or more other ViewResolvers, looks at the Accept header or file extension, and resolves a view corresponding to these. In an upcoming blog post, Alef Arendsen will show you how to use the ContentNegotiatingViewResolver.

    Views

    We also added some new Views to Spring MVC, particularly:

    • the AbstractAtomFeedView and AbstractRssFeedView, which can be used to return an Atom and RSS feed,
    • the MarshallingView, which can be used to return an XML representation. This view is based on the Object/XML Mapping module, which has been copied from the Spring Web Services project. This module wraps XML marshalling technologies such as JAXB, Castor, JiBX, and more, and makes it easier to configure these within a Spring application context,
    • the JacksonJsonView, for JSON representations of objects in your model. This view is actually part of the Spring JavaScript project, which we'll talk about more in a future blog post.

    Obviously, these work great in combination with the ContentNegotiatingViewResolver!

    HTTP Method Conversion

    Another key principle of REST is the use of the Uniform Interface. Basically, this means that all resources (URLs) can be manipulated using the same four HTTP method: GET, PUT, POST, and DELETE. For each of methods, the HTTP specification defines exact semantics. For instance, a GET should always be a safe operation, meaning that is has no side effects, and a PUT or DELETE should be idempotent, meaning that you can repeat these operations over and over again, but the end result should be the same.

    While HTTP defines these four methods, HTML only supports two: GET and POST. Fortunately, there are two possible workarounds: you can either use JavaScript to do your PUT or DELETE, or simply do a POST with the 'real' method as an additional parameter (modeled as a hidden input field in an HTML form). This latter trick is what the HiddenHttpMethodFilter does. This filter was introduced in Spring 3.0 M1, and is a plain Servlet Filter. As such, it can be used in combination with any web framework (not just Spring MVC). Simply add this filter to your web.xml, and a POST with a hidden _method parameter will be converted into the corresponding HTTP method request.

    As an extra bonus, we've also added support for method conversion in the Spring MVC form tags. For example, the following snippet taken from the updated Petclinic sample:

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

    will actually perform an HTTP POST, with the 'real' DELETE method hidden behind a request parameter, to be picked up by the HiddenHttpMethodFilter. The corresponding @Controller method is therefore:

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

    ETag support

    An ETag (entity tag) is an HTTP response header returned by an HTTP/1.1 compliant web server used to determine change in content at a given URL. It can be considered to be the more sophisticated successor to the Last-Modified header. When a server returns a representation with an ETag header, client can use this header in subsequent GETs, in a If-None-Match header. If the content has not changed, the server will return 304: Not Modified.

    In Spring 3.0 M1, we introduced the ShallowEtagHeaderFilter. This is a plain Servlet Filter, and thus can be used in combination any web framework. As the name indicates, the filter creates so-called shallow ETags (as opposed to a deep ETags, more about that later). The way it works is quite simple: the filter simply caches the content of the rendered JSP (or other content), generates a MD5 hash over that, and returns that as a ETag header in the response. The next time a client sends a request for the same resource, it use that hash as the If-None-Match value. The filter notices this, renders the view again, and compares the two hashes. If they are equal, a 304 is returned. It is important to note that this filter will not save processing power, as the view is still rendered. The only thing it saves is bandwith, as the rendered response is not sent back over the wire.

    Deep ETags are a bit more complicated. In this case, the ETag is based on the underlying domain objects, RDMBS tables, etc. Using this approach, no content is generated unless the underlying data has changed. Unfortunately, implementing this approach in a generic way is much more difficult than shallow ETags. We might add support for deep ETags in a later version of Spring, by relying on JPA's @Version annotation, or an AspectJ aspect for instance.

    And more!

    In a following post, I will conclude my RESTful journey, and talk about the RestTemplate, which was also introduced in Spring 3.0 M2. This class gives you client-side access to RESTful resources in a fashion similar to the JdbcTemplate, JmsTemplate, etc.

    Similar Posts



    原文地址:http://blog.springsource.com/2009/03/08/rest-in-spring-3-mvc/
    posted on 2009-12-24 19:53 周銳 閱讀(1684) 評(píng)論(0)  編輯  收藏 所屬分類: RESTSpring

    只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。


    網(wǎng)站導(dǎo)航:
    博客園   IT新聞   Chat2DB   C++博客   博問  
     
    主站蜘蛛池模板: 中国在线观看免费高清完整版| 永久免费观看的毛片的网站| 亚洲第一成人在线| 国产成人高清精品免费软件| 中文字幕免费在线看电影大全 | AV片在线观看免费| jizz免费观看| 亚洲av永久无码精品天堂久久| 亚洲国产精品丝袜在线观看| 最近2019免费中文字幕6| 国产精品亚洲а∨无码播放不卡| 亚洲AV永久无码区成人网站| 热99re久久精品精品免费| 在线播放免费人成毛片乱码| 亚洲精品国产suv一区88| 亚洲爆乳无码一区二区三区| 成人影片麻豆国产影片免费观看 | 亚洲日韩精品国产一区二区三区| 国产国拍亚洲精品福利 | 在线a级毛片免费视频| 中文字幕视频免费在线观看| 亚洲 欧洲 视频 伦小说| 亚洲AV成人一区二区三区AV| 亚洲高清最新av网站| 免费看国产成年无码AV片| 久久大香伊焦在人线免费| 新最免费影视大全在线播放| 亚洲三级中文字幕| 久久精品亚洲综合专区| 亚洲国产综合精品中文字幕| 最近高清国语中文在线观看免费| 久久精品视频免费看| 精品乱子伦一区二区三区高清免费播放| 亚洲日本久久久午夜精品| 亚洲一区二区三区电影| 亚洲中文字幕无码一区二区三区 | 中文亚洲成a人片在线观看| 精品免费国产一区二区| 精品免费久久久久久久| 久久黄色免费网站| 久久美女网站免费|