Skip to content
Mathias edited this page Jul 26, 2011 · 5 revisions

Path filter directives filter requests based on the unmatched part of the request URIs path component (i.e. without scheme, host and query part). They come in two flavors:

  • The path directive applies a match expression to the complete unmatched path of the request.
  • The pathPrefix directive does the same but tries to only match a path prefix.

Both directives take one argument of type [PathMatcher][] (or rather a subtype of PathMatcher), which is usually specified as a filter expression written in a mini-DSL.

The PathMatcher DSL

Just like spray routes are built from directives as building blocks more complex PathMatchers are created by combining simpler PathMatchers with operators.

By default the following things can be used as "atomic" PathMatchers:

  1. Simple Strings They simply match themselves and extract no value.

  2. Regular expressions (with zero or one capturing group) They match whatever they match and extract one String value, which is either the complete match (if the regex contains no capturing group) or the captured group (if the regex contains a capturing group).

  3. The objects IntNumber, LongNumber, HexIntNumber, HexLongNumber, DoubleNumber They match either an Int, Long or Double literal and extract its value.

  4. The Remaining object It matches all remaining, unmatched characters of the request path and extracts them as a String value.

  5. The JavaUUID object It matches the string representation of a java.util.UUID instance and extracts it.

PathMatchers can be chained with the / or ~ operators. The difference between the two is that / also matches a separating slash character whereas ~ simply connects two PathMatchers without any additional logic.

Note that each call to a path or pathPrefix directive implies a leading slash match, so you never have to start your PathMatcher argument with a slash.

Examples

The following route only responds to requests to paths beginning with "/hello" (note that you do not have to specify the initial slash / character):

pathPrefix("hello") { ... }

The following three routes all respond to requests to "/hello/bob":

path("hello/bob") { ... }

path("hello" / "bob") { ... }

pathPrefix("hello") {
  path("bob") { // the leading slash before "bob" is implied
    ...
  }
}

Regular expression matches (without capturing groups):

path("order" / "\\d+".r) { id =>
  // 'id' contains the string of matched digits
  _.complete("The order id is: " + id)
}

pathPrefix("employee" / "[^/]+".r) { employee =>
  path("salary" / "\\d\\d\\d\\d".r / "\\d\\d".r) { (year, month) =>
    ...          
  } ~
  path("tenure") {
    ...
  }
}

Regex match with capturing group:

path("order(\\d+)".r) { id =>
  ... // 'id' contains the string of matched digits    
}

Chaining several regex matches:

path("post-" ~ "\\d\\d\\d\\d".r ~ "-(\\d\\d)".r ~ "-(\\d\\d)".r) {
  (year, month, day) =>
  ... // responds for example to "/post-2011-03-23"          
}

Matching and extracting Int values:

pathPrefix("order") {
  path("i" ~ IntNumber) { intNum =>
    ...
  } ~
  path("h" ~ HexIntNumber) { intNum =>
    ...
  }
}

Custom PathMatchers

If you often have to match and extract custom objects from the request path it probably makes sense to create a custom PathMatcher for your type. In many cases the easiest way for this is to reuse a regex matcher and wrap it with a string-to-type conversion. You might want to use the implementation of the DoubleNumber matcher as a starting point:

/**
 * A PathMatcher that matches and extracts a Double value. The matched string representation is the pure decimal,
 * optionally signed form of a double value, i.e. without exponent.
 */
object DoubleNumber extends PathMatcher1[Double] {
  private val regexMatcher = new SimpleRegexMatcher("""[+-]?\d*\.?\d*""".r)

  def apply(path: String) = {
    // use 'flatMapValue' on the result of any PathMatcher1 to convert the extracted value to another type
    regexMatcher(path).flatMapValue { string =>
      try {
        Some(java.lang.Double.parseDouble(string))
      } catch {
        case _: NumberFormatException => None
      }
    }
  }
}

Clone this wiki locally