Spring Data REST
  1. Spring Data REST
  2. DATAREST-79

Complex path in @RestResource for repository wrongly exported

    Details

    • Type: Bug Bug
    • Status: Open
    • Priority: Major Major
    • Resolution: Unresolved
    • Affects Version/s: 1.1.0.M1
    • Fix Version/s: None
    • Component/s: None
    • Labels:
      None

      Description

      I have a repository interface declared as

      package it.mafa.smm.heatmap.persistence.repository;
      
      import it.mafa.smm.heatmap.persistence.model.HeatmapPostEntity;
      
      import java.util.Date;
      import java.util.List;
      
      import org.springframework.data.domain.Pageable;
      import org.springframework.data.jpa.repository.Query;
      import org.springframework.data.repository.CrudRepository;
      import org.springframework.data.repository.query.Param;
      import org.springframework.data.rest.repository.annotation.RestResource;
      import org.springframework.stereotype.Repository;
      
      @RestResource(path="complex/path")
      @Repository
      public interface HeatmapPostRepository extends CrudRepository<HeatmapPostEntity, Long>{
      	@RestResource(path="content")
      	@Query("select p from HeatmapPostEntity p,HeatmapAbonnementEntity a where p.abonnement = a and a.name = :name and p.createdAt between :fromDate and :toDate")
      	List<HeatmapPostEntity> findByNameAndCreatedAtBetween(@Param("name") String name,@Param("fromDate") Date fromDate,@Param("toDate") Date toDate, Pageable pageable);
      
      	@Query("select p.languageCode,count(p) from HeatmapPostEntity p,HeatmapAbonnementEntity a where p.abonnement = a and a.name = :name and p.createdAt between :fromDate and :toDate group by p.languageCode")
      	Object countByNameAndCreatedAtBetween(@Param("name") String name,@Param("fromDate") Date fromDate,@Param("toDate") Date toDate);
      	
      	@Query("select p.languageCode,count(p) from HeatmapPostEntity p,HeatmapAbonnementEntity a where p.abonnement = a and a.name = :name group by p.languageCode")
      	List<Object> countByName(@Param("name") String name);
      }
      

      The list of exported REST resources looks like

      C:\Documents and Settings\Administrator>curl -v "http://localhost:8080/heatmap/"
      * About to connect() to localhost port 8080 (#0)
      *   Trying 127.0.0.1... connected
      * Connected to localhost (127.0.0.1) port 8080 (#0)
      > GET /heatmap/ HTTP/1.1
      > User-Agent: curl/7.21.1 (i686-pc-mingw32) libcurl/7.21.1 OpenSSL/0.9.8r zlib/1.2.3
      > Host: localhost:8080
      > Accept: */*
      >
      < HTTP/1.1 200 OK
      < Content-Type: application/json
      < Transfer-Encoding: chunked
      < Server: Jetty(8.1.8.v20121106)
      <
      {
        "links" : [ {
          "rel" : "client",
          "href" : "http://localhost:8080/heatmap/client"
        }, {
          "rel" : "heatmapPost",
          "href" : "http://localhost:8080/heatmap/complex/path"
        }, {
          "rel" : "heatmapAbonnement",
          "href" : "http://localhost:8080/heatmap/abonnement"
        } ],
        "content" : [ ]
      }* Connection #0 to host localhost left intact
      * Closing connection #0
      

      In that case
      but http://localhost:8080/heatmap/complex/path resource results in a 404 error

      C:\Documents and Settings\Administrator>curl -v "http://localhost:8080/heatmap/complex/pat
      * About to connect() to localhost port 8080 (#0)
      *   Trying 127.0.0.1... connected
      * Connected to localhost (127.0.0.1) port 8080 (#0)
      > GET /heatmap/complex/path HTTP/1.1
      > User-Agent: curl/7.21.1 (i686-pc-mingw32) libcurl/7.21.1 OpenSSL/0.9.8r zlib/1.2.3
      > Host: localhost:8080
      > Accept: */*
      >
      < HTTP/1.1 404 Not Found
      < Content-Type: text/html;charset=ISO-8859-1
      < Cache-Control: must-revalidate,no-cache,no-store
      < Content-Length: 1385
      < Server: Jetty(8.1.8.v20121106)
      <
      <html>
      <head>
      <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"/>
      <title>Error 404 Not Found</title>
      </head>
      <body><h2>HTTP ERROR 404</h2>
      <p>Problem accessing /heatmap/complex/path. Reason:
      <pre>    Not Found</pre></p><hr /><i><small>Powered by Jetty://</small></i><br/>
      
      <br/>
      <br/>
      <br/>
      <br/>
      <br/>
      <br/>
      <br/>
      <br/>
      <br/>
      <br/>
      <br/>
      <br/>
      <br/>
      <br/>
      <br/>
      <br/>
      <br/>
      <br/>
      <br/>
      
      </body>
      </html>
      * Connection #0 to host localhost left intact
      * Closing connection #0
      

      Basically the lookupHandlerMethod method of RepositoryRestHandlerMapping does not find any matching repository and returns null.

        Activity

        Hide
        Jon Brisbin added a comment -

        Although you can set any string you want in the @RestResource.path annotation, Spring MVC won't route a path with a slash in it. It has to do with how Spring MVC does routing. Consequently, arbitrary depths to URLs aren't supported in Spring Data REST. You can only replace an element of a path with another value. You can't, for instance, map an arbitrary URL to a Spring Data REST resource using only Spring Data REST configuration.

        That being said, you can achieve what you want through an HAProxy, nginx, Apache etc... URL rewrite. Since you can do this quite easily external to SDR, it doesn't make a lot of sense to do the machinations involved in supporting arbitrary URL rerouting inside Spring Data REST.

        Show
        Jon Brisbin added a comment - Although you can set any string you want in the @RestResource.path annotation, Spring MVC won't route a path with a slash in it. It has to do with how Spring MVC does routing. Consequently, arbitrary depths to URLs aren't supported in Spring Data REST. You can only replace an element of a path with another value. You can't, for instance, map an arbitrary URL to a Spring Data REST resource using only Spring Data REST configuration. That being said, you can achieve what you want through an HAProxy, nginx, Apache etc... URL rewrite. Since you can do this quite easily external to SDR, it doesn't make a lot of sense to do the machinations involved in supporting arbitrary URL rerouting inside Spring Data REST.
        Hide
        Marco Fago added a comment -

        Spring MVC allows you to define a multi segments path.
        For example

        @Controller
        @RequestMapping({"/heatmap/mvc"})
        public class HeatmapHomeController {
                @RequestMapping(value = "/welcome", method = RequestMethod.GET)
        	public String home(Model model) {
                }
        }
        

        will be correctly mapped on https://localhost/heatmap/mvc/welcome .
        Looking at code, I saw that URIUtils.buildUri just allow to use multisegment path, probably it might be just the case to split the string with respect to slash before passing it to buildUri.

        The workaround with whichever proxy server might work, but requires extraeffort and might be confusing.

        Show
        Marco Fago added a comment - Spring MVC allows you to define a multi segments path. For example @Controller @RequestMapping({ "/heatmap/mvc" }) public class HeatmapHomeController { @RequestMapping(value = "/welcome" , method = RequestMethod.GET) public String home(Model model) { } } will be correctly mapped on https://localhost/heatmap/mvc/welcome . Looking at code, I saw that URIUtils.buildUri just allow to use multisegment path, probably it might be just the case to split the string with respect to slash before passing it to buildUri. The workaround with whichever proxy server might work, but requires extraeffort and might be confusing.
        Hide
        Jon Brisbin added a comment - - edited

        Although this is true for controllers you write yourself, this is not the way Spring Data REST leverages Spring MVC to do URL mapping.

        The mapping for a property of an entity looks like this: @RequestMapping("/{repository}/{id}/{property}"). You can't replace the single path element "property" with multiple path elements like "some/property". Spring MVC just doesn't work that way.

        Show
        Jon Brisbin added a comment - - edited Although this is true for controllers you write yourself, this is not the way Spring Data REST leverages Spring MVC to do URL mapping. The mapping for a property of an entity looks like this: @RequestMapping("/{repository}/{id}/{property}") . You can't replace the single path element "property" with multiple path elements like "some/property". Spring MVC just doesn't work that way.

          People

          • Assignee:
            Unassigned
            Reporter:
            Marco Fago
          • Votes:
            1 Vote for this issue
            Watchers:
            3 Start watching this issue

            Dates

            • Created:
              Updated: