Base URI of the request

166 views
Skip to first unread message

Stevo Slavić

unread,
Feb 23, 2015, 7:34:49 AM2/23/15
to spray...@googlegroups.com
Hello Spray community,

Please correct me if wrong, but from what I see in Spray 1.3.x APIs there's no existing directive or support in request context to extract/determine base URI path of the current request, relative to matched path, without repeating path matcher expression.

In my case I need base URI for every path, and that means a lot of unwanted repetition.

As explained in the docs (see #1) pathPrefix/path directives are eating into unmatched path of request context. It would be nice if like unmatchedPath there was a RequestContext base URI (path) property, which would be the prefix diff between request.uri.path and so far matched path.
Does this make sense?

Kind regards,
Stevo Slavic.

Jim Newsham

unread,
Feb 24, 2015, 2:51:46 PM2/24/15
to spray...@googlegroups.com

I had the same requirement, and wrote the following.  Note that since I wasn't sure if the base uri could always be extracted, I return an Option.  I agree it would be useful for Spray to have a directive for this out-of-the-box.

  /**
   * A custom spray directive which tries to extract the base uri from the request, by comparing the full request uri 
   * to the unmatched path.  If  the base uri cannot be determined, None will be returned.
   */
  def baseUri: Directive1[Option[String]] =
    extract(identity) map { context => getBaseUri(context) }

  /**
   * Attempts to extract the base uri from the request.  The base uri is the part of the path which has already been
   * matched/consumed.  If the base uri cannot be determined, None will be returned.
   */
  private def getBaseUri(context: RequestContext): Option[String] = {
    val requestUri = context.request.uri.path.toString
    val unmatchedPath = context.unmatchedPath.toString
    if (requestUri endsWith unmatchedPath) 
      Some(requestUri.substring(0, requestUri.length - unmatchedPath.length)) 
    else {
      logger.warn(s"failed to get base uri.  request uri was '${requestUri}' and unmatched path was '${unmatchedPath}'")
      None
    }
  }

To use in your route:

baseUri { baseUri =>
  ...
}

Mathias Doenitz

unread,
Feb 26, 2015, 3:09:56 AM2/26/15
to spray...@googlegroups.com
Stevo,

I’m not sure I fully understand what you are after.
Can you show an example to illustrate you needs?

Cheers,
Mathias

---
mat...@spray.io
http://spray.io
> --
> You received this message because you are subscribed to the Google Groups "spray.io User List" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to spray-user+...@googlegroups.com.
> Visit this group at http://groups.google.com/group/spray-user.
> To view this discussion on the web visit https://groups.google.com/d/msgid/spray-user/CAAUywg-kjxfxuAmcsVyY7C%3DEjneEnUMaeSodUVV-Dgd%2BdT7EnQ%40mail.gmail.com.
> For more options, visit https://groups.google.com/d/optout.

Stevo Slavić

unread,
Feb 26, 2015, 4:49:54 AM2/26/15
to spray...@googlegroups.com
Thanks Jim for sharing your solution. At first look, I wonder does it handle well root path requests, request uri does not end with empty string.

Mathias, here's an example. Say I have a spray service handling "segment1" and "segment2/segment3" paths. When I deploy same service in different environments it will be mapped to handle requests on different URLs. E.g. http://int.bar.foo.com/segment1 and https://ext.foo.com/bar/segment1. There are use cases where one needs to find out the base URI of service, http://int.bar.foo.com in first and https://ext.foo.com/bar in second environment.

My current solution is to have this provided as configuration to the service, so it does not use request URI at all. Benefit is that one does not need to evaluate it for every request. Downside is that it's not flexible and does not work in all situations - e.g. if same service deployment is handling int and ext requests.
Other solution would be a directive, extractor inner to the path. Directive to properly extract base uri from request uri would have to know outer path that is being handled. One could to the baseuri directive pass same parameters as in path directive, and to make it dry, extract it into a var. But, ideally, path itself could do this work, or make the work easier by e.g. extracting baseUri itself and making it available in request context, or making path handled available in request context.

Was I clear enough? Does this request make sense?

Kind regards,
Stevo Slavic.

Jim Newsham

unread,
Feb 26, 2015, 1:29:30 PM2/26/15
to spray...@googlegroups.com

To add to the "use case" discussion, I needed to compose multiple routes in a way that each route is not aware of its absolute uri path.  For example, consider a REST service with multiple versions:

/v1/resource1
/v2/resource1

There is a top-level route which delegates to a "v1" route and a "v2" route, so that the v1/v2 routes don't have any awareness of the top-level route.  However the resources under v1 (for example) need to link back to other resources under the v1 tree.  So they need prepend the base uri ("http://thehost.com/v1") to the relative resource path ("/resource2") in order to construct an absolute uri that can be consumed by the caller.  

Jim

Mathias Doenitz

unread,
Feb 27, 2015, 4:06:18 AM2/27/15
to spray...@googlegroups.com
Stevo,

> Mathias, here's an example. Say I have a spray service handling "segment1" and "segment2/segment3" paths. When I deploy same service in different environments it will be mapped to handle requests on different URLs. E.g. http://int.bar.foo.com/segment1 and https://ext.foo.com/bar/segment1. There are use cases where one needs to find out the base URI of service, http://int.bar.foo.com in first and https://ext.foo.com/bar in second environment.

The path directives (http://spray.io/documentation/1.2.2/spray-routing/path-directives/) only match the *path* component of the request URI.
If I understand you right, by “base URI” you mean the scheme and the authority component.
Those are not in scope of the path directives. That’s what the `scheme` and `host` directives are for.

So, you should be able to use exactly the kind of logic you want.

What exactly would you like to do and what do you have trouble with?

> My current solution is to have this provided as configuration to the service, …

You current solution to what exactly?
I still don’t quite understand the problem that you are facing.

Cheers,
Mathias

---
mat...@spray.io
http://spray.io

> To view this discussion on the web visit https://groups.google.com/d/msgid/spray-user/CAAUywg8BA1B_KPsV%3Dc7KB3wr4Z5JkrLt%2Bq%2BV4quk_Cav63uPag%40mail.gmail.com.

Mathias Doenitz

unread,
Feb 27, 2015, 4:09:25 AM2/27/15
to spray...@googlegroups.com
Jim,

> There is a top-level route which delegates to a "v1" route and a "v2" route, so that the v1/v2 routes don't have any awareness of the top-level route. However the resources under v1 (for example) need to link back to other resources under the v1 tree. So they need prepend the base uri ("http://thehost.com/v1") to the relative resource path ("/resource2") in order to construct an absolute uri that can be consumed by the caller.

how about simply parameterizing the inner route with the prefix along these lines?:

pathPrefix(“v1”) {
api(“v1”)
} ~
pathPrefix(“v2”) {
api(“v2”)
}

and

def api(prefix: String): Route =
pathPrefix(“user”) {
… /*some routing needing access to the prefix */
} ~ ...

Cheers,
Mathias

---
mat...@spray.io
http://spray.io

> --
> You received this message because you are subscribed to the Google Groups "spray.io User List" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to spray-user+...@googlegroups.com.
> Visit this group at http://groups.google.com/group/spray-user.
> To view this discussion on the web visit https://groups.google.com/d/msgid/spray-user/5fee7b14-2290-4244-b346-1cc1429b3748%40googlegroups.com.

Jim Newsham

unread,
Feb 28, 2015, 2:17:56 AM2/28/15
to spray...@googlegroups.com

Mathias,

I was relying on the matched part to give me the full prefix of the uri, including scheme/host.  If I simply pass the prefix value of "v1" to my delegate path, how would I then reconstruct the entire uri (ie., "http://www.myhost.com/v1")?  I'm sure there's something simple I'm missing.

Thanks,
Jim

Mathias Doenitz

unread,
Feb 28, 2015, 3:44:23 PM2/28/15
to spray...@googlegroups.com
Jim,

have you seen the `requestUri` directive?
http://spray.io/documentation/1.2.2/spray-routing/misc-directives/requestUri/

This gives you access to the complete request URI.
This together with everything discussed before should let you do pretty much anything possible with the URIs you consume and/or produce.

Cheers,
Mathias

---
mat...@spray.io
http://spray.io

> To view this discussion on the web visit https://groups.google.com/d/msgid/spray-user/9dd60fdc-2a98-4a3a-a6e0-dc23d6110d4e%40googlegroups.com.

Jim Newsham

unread,
Mar 2, 2015, 5:12:01 PM3/2/15
to spray...@googlegroups.com

Sure, and then if the "what you want to do" is to extract the base uri in a lot of different places, then you could abstract that into a method (which requires the full uri and the unmatched path), but then you'll still have code duplication invoking the requestUri directive, the unmatchedPath directive, and then the method.  A more direct way to compose this into a (admittedly very simple but nonetheless useful) directive which does the entire operation.  Anyway, it was useful for me, and I thought it might be useful for someone else.

Regards,
Jim

Colin Bester

unread,
May 3, 2016, 11:18:22 AM5/3/16
to spray.io User List
I suspect what is being looked for is how to include the root-context path defined by application server (in my case jetty).

Problem I have is that one doesn't want to hard code the root context of application server. So ideally one would want to be able to include the full URI that would be required to access the resource returned by requestUri directive.

For example I have path finder of "/device/usage" and application server root context of "/rest", my url would then be http://somewhere.com/rest/device/usage but requestUri only returns http://somewhere.com/device/usage, it does not include the root context "rest".
Reply all
Reply to author
Forward
0 new messages