spring-hateoas: ControllerLinkBuilder does not take Spring Data REST's base path into account

I have the repository REST base path set to /rest, and a @RepositoryRestController with some custom handlers:

@RepositoryRestController
public class RestController {
    @RequestMapping(value = "/foo", method = RequestMethod.GET)
    public ResponseEntity<Bar> foo() {
        return new ResponseEntity<>(new Bar(), HttpStatus.OK);
    }
    ...
}
public class Bar extends ResourceSupport {
    public Bar() {
        ...
        add(linkTo(methodOn(RestController.class).foo()).withSelfRel());
    }
}

The endpoint works fine at http://localhost:8080/rest/foo, however, the link is incorrect:

{
  ...
  _links: {
    self: {
      href: "http://localhost:8080/foo"
    }
  }
}

For some reason, the base path (/rest) was ignored.

I’m using Spring HATEOAS 0.19.0.RELEASE (comes with Spring Boot 1.3.1.RELEASE).

About this issue

  • Original URL
  • State: open
  • Created 8 years ago
  • Reactions: 11
  • Comments: 29 (8 by maintainers)

Commits related to this issue

Most upvoted comments

I have the same problem and implemented the following service that prepends the base path as a workaround:

@Service
public class BasePathAwareLinks {
	
  private final URI contextBaseURI;
  private final URI restBaseURI;
	
  @Autowired
  public BasePathAwareLinks(ServletContext servletContext, RepositoryRestConfiguration config) {
    contextBaseURI = URI.create(servletContext.getContextPath());
    restBaseURI = config.getBasePath();
  }

  public LinkBuilder underBasePath(ControllerLinkBuilder linkBuilder) {
    return BaseUriLinkBuilder.create(contextBaseURI)
      .slash(restBaseURI)
      .slash(contextBaseURI.relativize(URI.create(linkBuilder.toUri().getPath())));
  }
}

To use it, just pass the LinkBuilder returned from linkTo to underBasePath before actually building the link:

public class MyResourceProcessor<Person> implements ResourceProcessor<Person> {
	
  @Autowired
  private BasePathAwareLinks service;
	
  public Resource<Person> process(Resource<Person> resource) {
    resource.add(
      service.underBasePath(
        linkTo(methodOn(SpecialPersonController.class).doSomething())
      )
      .withRel("something")
    );
    return resource;
  }
}

Hope this helps.

I just came across this issue as well. Did you find a workaround?

I assume we will need a RestControllerLinkBuilder class in the data-rest package…

Hey, I was fighting with this issue myself and found out that you can add base path of SDR to @RequestMapping annotation explicitly and here is what happens: you get proper link using static ControllerLinkBuilder AND your method gets registered properly as you want it - the prefix with base path will be ignored somehow.

Although I’m not sure how it is happening, this is what I’m using and it works fine for me.

@drenda happy to help! 😺

For me the first part was not important, but in general we probably should consider it as well. I guess you could extract it from linkBuilder.toUri(), maybe something like the following (didn’t test) could work:

  public LinkBuilder underBasePath(ControllerLinkBuilder linkBuilder) {
    URI uri = linkBuilder.toUri();
    URI origin = new URI(uri.getScheme(), uri.getAuthority(), null, null, null);
    URI suffix = new URI(null, null, uri.getPath(), uri.getQuery(), uri.getFragment());
    return BaseUriLinkBuilder.create(origin)
      .slash(contextBaseURI)
      .slash(restBaseURI)
      .slash(suffix);
  }

We basically take the link from the ControllerLinkBuilder and just prepend the context base URI and the rest base URI to its path, while leaving everything else intact. So maybe now it also works for templated links.

Have exactly the same problem.