Как создать пользовательскую директиву для повторного использования маршрутов?

У меня есть фрагмент маршрута, который я хочу повторно использовать в нескольких сценариях:

val dirSegment = "licenses"
path( dirSegment ~ PathEnd ) {
  redirect( dirSegment + "/", StatusCodes.MovedPermanently ) 
} ~ 
pathPrefix(dirSegment) { 
  path("") {
    /* do something */
  }
}

Я хотел бы превратить это в директиву (или параметризируемый маршрут?), где я могу указать значение dirSegment val и произвольную дальнейшую маршрутизацию/код вместо path("") { /* do something */ } white, сохраняя поведение перенаправления, выглядя примерно так:

directoryPath("licenses") {
  path("") {
    /* do something */
  }
} ~ 
directoryPath("about") {
  path("") {
    /* do somthing else */
  }
}

Принимая во внимание, что это было бы эквивалентно следующему поведению без всех повторений:

val dirSegment = "licenses"
val anotherDir = "About"

path( dirSegment ~ PathEnd ) {
  redirect(dirSegment + "/", StatusCodes.MovedPermanently ) 
} ~ 
pathPrefix(dirSegment) { 
  path("") {
    /* do something */
  }
} ~
path( anotherDir ~ PathEnd ) {
  redirect(anotherDir + "/", StatusCodes.MovedPermanently ) 
} ~ 
pathPrefix(anotherDir) { 
  path("") {
    /* do something else */
  }
}

Обратите внимание, что этот вопрос был вдохновлен некоторыми обсуждениями в Как автоматически добавить косую черту в конец URL-адреса в маршрутизации спрея?


person Connie Dobbs    schedule 25.10.2013    source источник


Ответы (1)


Для этого вам нужно написать пользовательскую директиву.

// additional imports you may need
import shapeless.HNil
import spray.http.StatusCodes
import spray.routing.Directive0
import spray.routing.PathMatcher

Теперь, когда это не так:

/**
 * Spray's PathEnd matches trailing optional slashes... we can't have that
 * otherwise it will cause a redirect loop.
 */
object PathEndNoSlash extends PathMatcher[HNil] {
  def apply(path: Path) = path match {
    case Path.Empty ⇒ PathMatcher.Matched.Empty
    case _          ⇒ PathMatcher.Unmatched
  }
}

/**
 * Custom directive that uses a redirect to add a trailing slashe to segment
 * if the slash isn't present.
 */
def directoryPath(segment: String) = new Directive0 {
  def happly(f: HNil ⇒ Route) =
    // first, the redirect
    pathPrefix(segment ~ PathEndNoSlash) {
      redirect("/" + segment + "/", StatusCodes.MovedPermanently) } ~
    // delegate actual work to nested directives
    pathPrefix(segment).happly(f)
}

Использование:

directoryPath("about") {
  path("a") {
    complete {
      "this is /about/a"
    }
  } ~ path("b") {
    complete {
      "this is /about/b"
    }
  } ~ path(PathEnd) {
    complete {
      "this is /about/"
    }
  }
}

Если пользователь обращается к /about, он будет переадресован на /about/ и увидит "это /о/". Вложенные пути a и b работают как положено (то есть без собственных перенаправлений).

Примечание. Это решение предназначено для Spray 1.2.

person Andy    schedule 25.10.2013
comment
Мы немного изменили поведение PathMatcher в RC1 и, вероятно, сделаем это снова перед финальным релизом. Начиная с RC1 есть rawPathPrefix, который точно соответствует без каких-либо дополнительных косых черт в конце. После этого вы сможете написать rawPathPrefix(Slash ~ segment ~ PathEnd) для соответствия без косой черты. В противном случае это выглядит хорошо. - person jrudolph; 26.10.2013
comment
@jrudolph, если PathEnd не изменился, не будет ли он по-прежнему соответствовать необязательной косой черте в конце? Я помню, как пробовал различные методы (возможно, rawPathPrefix) и имел проблемы с петлями перенаправления, пока не написал свой собственный PathEndNoSlash. - person Andy; 27.10.2013
comment
Вот PR для руководства по миграции, которое я написал сегодня днем, надеюсь, оно объясняет новое поведение: github .com/spray/spray/pull/659/files - person jrudolph; 31.10.2013
comment
@ Энди, я провел быстрый тест - вы также можете избавиться от объекта PathEndNoSlash, (!) не вводя косую черту. pathPrefix(segment ~ !Slash) - person Ramon; 22.04.2014