Scala 如何获得正确的参数验证错误

Scala 如何获得正确的参数验证错误,scala,akka,query-parameters,akka-http,Scala,Akka,Query Parameters,Akka Http,我有一个简单的路线,其中参数应提取到案例类中: val myRoute: Route = get { path("resource") { parameters('foo, 'x.as[Int]).as(FooParams) { params => ... } ~ parameters('bar, 'x.as[Int]).as(BarParams) { params => ...

我有一个简单的路线,其中参数应提取到案例类中:

val myRoute: Route =
    get {
      path("resource") {
        parameters('foo, 'x.as[Int]).as(FooParams) { params =>
        ...
        } ~
        parameters('bar, 'x.as[Int]).as(BarParams) { params =>
          ...
        }
    }
}

case class FooParams(foo: String, x: Int) {
require(x > 1 && x < 10, "x for foos must be between 2 and 9")
}

case class BarParams(bar: String, x: Int) {
require(x > 10 && x < 20, "x for bars must be between 11 and 19")
}

我不会使用两个不同的
参数
指令,而是建议使用其中一个并将参数作为选项,即
参数('foo?,'bar?,'x.as[Int])
。此指令将提取您需要的数据,稍后您可以匹配这些数据并将其转换为您需要的案例,如下所示:

(get & path("...")) {
  parameters('foo?, 'bar?, x.as[Int]) {
    case (None, Some(bar), x) => BarParams(bar, x)
    case (Some(foo), None, x) => FooParams(foo, x)
    /**
     * Here comes you Rejection (you can make your own)
     */
    case _ => reject(MalfromedParametersRejection)
  }
}
val validationWins = mapRejections{ rej =>
  val mapped = rej.filter(_.isInstanceOf[ValidationRejection])
  if (mapped.isEmpty) rej else mapped
}

val myRoute = 
  get {
    path("resource") {
      validationWins{
        parameters('foo, 'x.as[Int]).as(FooParams) { params =>
          complete(StatusCodes.OK)
        } ~ 
        parameters('bar, 'x.as[Int]).as(BarParams) { params =>
          complete(StatusCodes.OK)
        }        
    }
  }

另一个我认为使用<代码>的拙劣实践在构造函数中需要,给定的解决方案允许您使用Edvices来处理您所描述的情况:

parameters('foo?, 'bar?, x.as[Int]) {
  case (None, Some(bar), x) if x > 1 && x < 10 => BarParams(bar, x)
  //...
}
参数('foo?、'bar?、x.as[Int]){
如果x>1&&x<10=>BarParams(bar,x),则情况(无,部分(bar),x)
//...
}

我不会使用两个不同的
参数
指令,而是建议使用其中一个,并将您的参数作为选项,即
参数('foo?,'bar?,'x.as[Int])
。此指令将提取您需要的数据,稍后您可以匹配这些数据并将其转换为您需要的案例,如下所示:

(get & path("...")) {
  parameters('foo?, 'bar?, x.as[Int]) {
    case (None, Some(bar), x) => BarParams(bar, x)
    case (Some(foo), None, x) => FooParams(foo, x)
    /**
     * Here comes you Rejection (you can make your own)
     */
    case _ => reject(MalfromedParametersRejection)
  }
}
val validationWins = mapRejections{ rej =>
  val mapped = rej.filter(_.isInstanceOf[ValidationRejection])
  if (mapped.isEmpty) rej else mapped
}

val myRoute = 
  get {
    path("resource") {
      validationWins{
        parameters('foo, 'x.as[Int]).as(FooParams) { params =>
          complete(StatusCodes.OK)
        } ~ 
        parameters('bar, 'x.as[Int]).as(BarParams) { params =>
          complete(StatusCodes.OK)
        }        
    }
  }

另一个我认为使用<代码>的拙劣实践在构造函数中需要,给定的解决方案允许您使用Edvices来处理您所描述的情况:

parameters('foo?, 'bar?, x.as[Int]) {
  case (None, Some(bar), x) if x > 1 && x < 10 => BarParams(bar, x)
  //...
}
参数('foo?、'bar?、x.as[Int]){
如果x>1&&x<10=>BarParams(bar,x),则情况(无,部分(bar),x)
//...
}

我认为你看到的问题的主要部分来自于你的路线是如何定义的。通过在路径“resource”下定义这两个可能的参数集,当它在
foo
param上未命中时,您会在拒绝列表的开头出现一个
MissingParemeterRejection
。一个
ValidationRejection
也会在那里结束,但是默认的拒绝处理程序在决定要传递给调用方的状态代码和消息时必须更喜欢
MissingParameterRejection
。如果您只是像这样重新定义了路线:

val myRoute: Route =
  get {
    path("resource") {
      parameters('foo, 'x.as[Int]).as(FooParams) { params =>
      ...
      } ~
    }
    path("resource2"){
      parameters('bar, 'x.as[Int]).as(BarParams) { params =>
        ...
      }
    }
  }
然后一切按预期进行。在这种情况下,在接受根路径之前,它甚至不会尝试计算参数。而且,由于每个根路径都有不同的参数集,因此不可能在列表的开头获得不必要的缺失参数异常

现在,如果这不是一个可接受的替代方案,那么您可以使用类似于
mapRejections
的内容包装该路由,以删除不必要的缺失参数拒绝(如果它包含验证拒绝)。大概是这样的:

(get & path("...")) {
  parameters('foo?, 'bar?, x.as[Int]) {
    case (None, Some(bar), x) => BarParams(bar, x)
    case (Some(foo), None, x) => FooParams(foo, x)
    /**
     * Here comes you Rejection (you can make your own)
     */
    case _ => reject(MalfromedParametersRejection)
  }
}
val validationWins = mapRejections{ rej =>
  val mapped = rej.filter(_.isInstanceOf[ValidationRejection])
  if (mapped.isEmpty) rej else mapped
}

val myRoute = 
  get {
    path("resource") {
      validationWins{
        parameters('foo, 'x.as[Int]).as(FooParams) { params =>
          complete(StatusCodes.OK)
        } ~ 
        parameters('bar, 'x.as[Int]).as(BarParams) { params =>
          complete(StatusCodes.OK)
        }        
    }
  }

理想情况下,我更喜欢在我的路线树中使用
cancelRejections
来删除那些与路线树中的未来无关的东西,但是没有一个干净的地方可以这样做,所以我使用
mapRejections
来代替它。

我认为您看到的问题的主要部分来自于路线的定义。通过在路径“resource”下定义这两个可能的参数集,当它在
foo
param上未命中时,您会在拒绝列表的开头出现一个
MissingParemeterRejection
。一个
ValidationRejection
也会在那里结束,但是默认的拒绝处理程序在决定要传递给调用方的状态代码和消息时必须更喜欢
MissingParameterRejection
。如果您只是像这样重新定义了路线:

val myRoute: Route =
  get {
    path("resource") {
      parameters('foo, 'x.as[Int]).as(FooParams) { params =>
      ...
      } ~
    }
    path("resource2"){
      parameters('bar, 'x.as[Int]).as(BarParams) { params =>
        ...
      }
    }
  }
然后一切按预期进行。在这种情况下,在接受根路径之前,它甚至不会尝试计算参数。而且,由于每个根路径都有不同的参数集,因此不可能在列表的开头获得不必要的缺失参数异常

现在,如果这不是一个可接受的替代方案,那么您可以使用类似于
mapRejections
的内容包装该路由,以删除不必要的缺失参数拒绝(如果它包含验证拒绝)。大概是这样的:

(get & path("...")) {
  parameters('foo?, 'bar?, x.as[Int]) {
    case (None, Some(bar), x) => BarParams(bar, x)
    case (Some(foo), None, x) => FooParams(foo, x)
    /**
     * Here comes you Rejection (you can make your own)
     */
    case _ => reject(MalfromedParametersRejection)
  }
}
val validationWins = mapRejections{ rej =>
  val mapped = rej.filter(_.isInstanceOf[ValidationRejection])
  if (mapped.isEmpty) rej else mapped
}

val myRoute = 
  get {
    path("resource") {
      validationWins{
        parameters('foo, 'x.as[Int]).as(FooParams) { params =>
          complete(StatusCodes.OK)
        } ~ 
        parameters('bar, 'x.as[Int]).as(BarParams) { params =>
          complete(StatusCodes.OK)
        }        
    }
  }

理想情况下,我更喜欢在我的路径树中使用
取消拒绝
来删除树中不重要的东西,但是没有干净的地方可以这样做,所以我使用
映射拒绝

谢谢,4lex1v,正如广告所宣传的那样。你能详细说明一下在课堂上使用
require
是一种不好的做法吗?在@kostja中建议使用它没有什么问题,我只是更喜欢将异常具体化为一个值,而不是使用外部处理程序来处理它。谢谢,4lex1v,正如广告中所宣传的那样。你能详细说明一下在课堂上使用
require
是一种不好的做法吗?在@kostja中建议这样做没有错,我只是更喜欢将异常具体化为一个值,而不是使用外部处理程序来处理它。我不会在这里改变你的方法。我认为你正在做的很好。我使用路由测试工具包运行了您的代码,当参数不满足要求时,我得到了相应的拒绝(ValidationRejection)。我还没有在运行中的服务器上进行测试,但是您所做的符合文档,所以这里肯定发生了其他事情。我不会在这里改变您的方法。我认为你正在做的很好。我使用路由测试工具包运行了您的代码,当参数不满足要求时,我得到了相应的拒绝(ValidationRejection)。我还没有在运行的服务器中进行测试,但是您所做的符合文档的要求,所以这里肯定发生了其他事情。