Skip to content

Support for relative href links? #330

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
pimlottc opened this issue Apr 7, 2015 · 18 comments
Open

Support for relative href links? #330

pimlottc opened this issue Apr 7, 2015 · 18 comments

Comments

@pimlottc
Copy link

pimlottc commented Apr 7, 2015

The Stateless.co HAL documentation and IETF draft spec both use relative URLs (with absolute paths) in all their examples, e.g.

{ "_links": { "self": { "href": "/orders/523" } } }

However, the links generated by ControllerLinkBuilder and EntityLink are all absolute:

{ "_links": { "self": { "href": "http://localhost:8080/orders/523" } } }

Is there any way to make them generate relative links instead?

@odrotbohm
Copy link
Member

hrefs are entirely in your control:

Link link = new Link("/orders/523", "self")

@pimlottc
Copy link
Author

pimlottc commented Apr 8, 2015

As I said, I'm talking about links generated by the framework, e.g.

entityLinks.linkToCollectionResource(Project.class);

or

ControllerLinkBuilder.linkTo(methodOn(LanguagesController.class).getAllLanguages());

@moravcik
Copy link

Actually, absolute links which are generated automatically are causing problems in my project too.
I am using Spring Boot with Spring Data Rest on server side and AngularJS with Restangular on client side. Development mode goes through browserSync with proxy to server. So all requests based on resource links (PUT, DELETE, GET) are failing on 'Access-Control-Allow-Origin' error.
I would be nice to have an option to enable/disable relative links generation.

@gregturn
Copy link
Contributor

Spring HATEOAS observes x-forwarded-* headers. Case in point: http://spring-a-gram.cfapps.io forwards /api calls to spring-a-gram-backend.cfapps.io. Look at the following calls done to each, and you'll see the hostnames adjust properly.

$ curl -v -u greg:turnquist spring-a-gram.cfapps.io/api
{
  "_links" : {
    "galleries" : {
      "href" : "http://spring-a-gram.cfapps.io/api/galleries{?page,size,sort}",
      "templated" : true
    },
    "items" : {
      "href" : "http://spring-a-gram.cfapps.io/api/items{?page,size,sort,projection}",
      "templated" : true
    },
    "profile" : {
      "href" : "http://spring-a-gram.cfapps.io/api/alps"
    }
  }
}

Here is the same call straight to the backend:

$ curl -v -u greg:turnquist spring-a-gram-backend.cfapps.io/api
{
  "_links" : {
    "galleries" : {
      "href" : "http://spring-a-gram-backend.cfapps.io/api/galleries{?page,size,sort}",
      "templated" : true
    },
    "items" : {
      "href" : "http://spring-a-gram-backend.cfapps.io/api/items{?page,size,sort,projection}",
      "templated" : true
    },
    "profile" : {
      "href" : "http://spring-a-gram-backend.cfapps.io/api/alps"
    }
  }
}

To use shortcut tactics like relative paths to evade CORS sounds like the wrong solution and a band-aid response in lieu of configuring your proxy with proper headers. If your angular app is not served from the location as your API gateway, a proper CORS policy is also a necessary setting.

@milanov
Copy link

milanov commented Oct 14, 2015

I also needed that same funcitonality so I came up with this, which is not that sophisticated but works good (for now).

@moravcik
Copy link

Yes, setting proper X-Forwarded-Host header worked for me.

In my case I needed to add to browserSync proxy function:

req.headers["X-Forwarded-Host"] = req.headers.host;

@Doogiemuc
Copy link

I also support this: There should be at least a configuration so that the href URIs genreated by spring hateoas are relative. If you look at it from a REST client perspective: Most REST clients libraries have some kind of "backendBasePath" configured in a central place. Now if that client want's to follow a _link then it is always necessary to strip the backendBasePath from the URI to construct the request for the linked resource.

@odrotbohm
Copy link
Member

odrotbohm commented Jan 10, 2017

Actually, IMO that's exactly arguing the wrong way around. Why would you bother the client with having to concatenate Strings if the alternative is just using what's available in href as is? That's basically throwing a key benefit of URIs away: the fact that you can hand them anyone and they stay resolvable. You can always come up with some weird stunts some clients do, but I am not sure I'd want to support introducing additional complexity.

What I am trying to say is: go ahead and create those URIs that are basically unresolvable as is for a client, but I am not sure we should elevate this suboptimal habit to a first-class framework featured style.

@Doogiemuc
Copy link

Doogiemuc commented Jan 10, 2017

Hello Oliver!
I can understand both sides. Also your argument: "The URI actually IS the ID of that entitry" is a very valid argument.

But every REST client I have ever seen (might be just my liit personal experience) does have a backendBaseURL. (Think: While testing I want to use server A and in production I want to use server B). And that backendBaseURL is prepended to any request.

Examples for clients that behave like this: node-rest-client , restful.js etc.

In any client I know of it is actually the exception and needs some additional work, if you want to GET from an absolute URL.

Anyway: How can I configure relative URLs? I got it working half way:

  @Bean
  public ResourceProcessor<Resource<IdeaModel>> ideaProcessor() {
    return new ResourceProcessor<Resource<IdeaModel>>() {
      @Override
      public Resource<IdeaModel> process(Resource<IdeaModel> resource) {
        resource.add(new Link("/myideaLink/1", "idea"));
        return resource;
      }
    };
  }

But this adds a second href to "_links.idea" :-( I saw that the Link class actually uses UriTemplate which has a basURI. How do I set this?

@odrotbohm
Copy link
Member

I am sorry, but this is bad design of the client libraries. If following a link, i.e. taking an hrefand executing a request against that URI is something that's hard, then by all means, please file an issue with that particular library.

It might sound harsh, but we can not let (bad design decisions of) arbitrary JS libraries drive the API of a framework to build REST APIs. If we accommodated every weird move of those libraries, we'd probably be adding and changing things quicker than anyone can turn around. Our design decisions are usually driven by RFCs and at least de-facto standardized formats. So if there's some RFC flying around that we can follow, I am happy to reconsider.

Generally speaking, whenever you implement a RESTful interaction and have to build something "for a framework", that's a smell. A Spring HATEOAS client doesn't need to (maybe even: must not) know anything about Spring HATEOAS. All they need to know is HAL and URI templates. The same way as a server framework wouldn't want to introduce tweaks for a particular client library. Because if you do so you build a foo.js to bar.java API, not a REST API. I guess you get the point. :)

@otrosien
Copy link

@olivergierke AFAIR it is advised to produce absolute links, but not mandated by any spec, or do you have a reference that I don't know..?

I also favor the current approach, but it seems to be a common feature request. see issues #408, #516 (#516 (comment))

gregturn added a commit that referenced this issue May 25, 2017
If forming a link outside a Spring web request, fallback to relative links.

Original pull-request: #410
Related issues: #330, #143, #516
gregturn added a commit that referenced this issue May 25, 2017
If forming a link outside a Spring web request, fallback to relative links.

Original pull-request: #410
Related issues: #330, #143, #516
gregturn added a commit that referenced this issue May 25, 2017
If forming a link outside a Spring web request, fallback to relative links.

Original pull-request: #410
Related issues: #330, #143, #516
@gregturn gregturn self-assigned this May 25, 2017
gregturn added a commit that referenced this issue May 25, 2017
If forming a link outside a Spring web request, fallback to relative links.

Original pull-request: #410
Related issues: #330, #143, #516
odrotbohm pushed a commit that referenced this issue Jul 27, 2017
If forming a link outside a Spring web request, fallback to relative links.

Original pull-request: #410.
Related issues: #330, #143, #516.
odrotbohm added a commit that referenced this issue Jul 27, 2017
Original pull-request: #410.
Related issues: #330, #143, #516.
@stiyyagura
Copy link

I do support having relative links despite absolute links. Here is the example:
My Rest services are running behind an AWS CloudFront and API Gateway. In this case the links generated in the response are not including the CloudFront URL of the gateway because gateway itself running as another micro service. Please let me know if this use case is not ideal to explain the need of the relative links.

@gregturn
Copy link
Contributor

gregturn commented Jul 6, 2018

Spring HATEOAS supports relative links in the sense that if you are building a link outside the context of a Spring MVC Web call, it will now fall back to relative links instead of throwing an exception. However you can’t simply switch all link generation to relative. Not sure why you’d want to considering Spring HATEOAS supports forwarder headers (X-Forwarded-Host) that your proxy should be generating anyway.

@rania-chantz
Copy link

On AWS using a load balancer and cloudfront, the combination of server.use-forward-headers=true in application.properties and a lambda that sets the host and the proto for the "Forwarded" header and that is configured in Cloudfront as origin request does the trick. The "X-Forwarded-Proto" header is blacklisted unfortunately, but "Forwarded" does the same thing. @stiyyagura I hope this helps

@getaceres
Copy link

I support this request. Nowadays is quite common to have your controller deeply buried behind layers of containers, container orchestrators, VMs, private networks, API servers and proxies. Relying on X-Forwarded-Proto being generated and forwarded and not cut at any point in the request chain to generate a link is sometimes too much to ask for very complex architectures. Having a way to specify that you want a relative link instead of an absolute one is quite convenient for situations like this.

@fc-smohr
Copy link

fc-smohr commented Dec 1, 2020

This should be an easy to implement but helpful feature. I have also worked a lot with proxies and headers, this can take a lot of time in large companies. Please implement this.

@kimsaabyepedersen
Copy link

This should be an easy to implement but helpful feature. I have also worked a lot with proxies and headers, this can take a lot of time in large companies. Please implement this.

This should be an easy to implement but helpful feature. I have also worked a lot with proxies and headers, this can take a lot of time in large companies. Please implement this.

Second that

@dns-pradeepa
Copy link

Is there any update on this? I can change the URLs in the body but not the pagination URLs since those are generated by PagedResourcesAssembler. Is there any configuration in current spring boot version or still nothing to generate relative URL?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests