【问题标题】:Akka-http: Accept and Content-type handlingAkka-http:接受和内容类型处理
【发布时间】:2015-11-18 04:54:44
【问题描述】:

我正在试用 Akka-http,希望有人能阐明以下问题:

  1. 如何根据请求中的 accept: 标头创建不同的路由?例如,我想要一个处理“json”的代码路径和一个处理“xml”请求的代码路径(如果缺少标头,则默认为“json”)

  2. 如果我不想推断 contentType,我该如何指定它?例如,在下面的代码中,我尝试通过 compactPrint() 运行 json,但这会将其更改为字符串,因此是“text/plain”。我想覆盖它并告诉客户它仍然是 json。

我的代码是这样的;

...
path("api") {
          get {
              complete {
                getStuff.map[ToResponseMarshallable] {
                  case Right(r) if r.isEmpty => List[String]().toJson.compactPrint
                  case Right(r) => r.toJson.compactPrint
                  case Left(e) => BadRequest -> e
                }
              }
          }
        }
...

这种情况下的响应是 text/plain,因为 compactPrint 创建了一个字符串。 非常欢迎批评。 ;)

【问题讨论】:

标签: scala akka akka-http


【解决方案1】:

您可以如下定义内容类型,

complete {
           HttpResponse(entity = HttpEntity(ContentType(MediaTypes.`application/json`), """{"id":"1"}"""))
         }

您可以将自定义指令创建为,

  def handleReq(json: String) = {
    (get & extract(_.request.acceptedMediaRanges)) {
      r =>
        val encoding: MediaRange =
          r.intersect(myEncodings).headOption
            .getOrElse(MediaTypes.`application/json`)
        complete {
          // check conditions here
         // HttpResponse(entity = HttpEntity(encoding.specimen, json)) //
        }
    }
  }

并在路由中使用指令作为

val route = path("api"){ handleReq(json) }

【讨论】:

  • 谢谢。我只是希望有一个指令根据标头进行拒绝,而不是包含实际处理。但无论如何,我最终实现了自动处理序列化为 XML 或 JSON 的编组器,所以我不再需要这种方法了。但是感谢您的帮助,它帮助我克服了挫败感,这是实现事物的一种方式。所以我会接受答案。
  • 这里最好使用 ContentTypes.application/json 而不是 ContentType(MediaTypes.application/json)
【解决方案2】:

似乎接受的答案不再适用于 akka-http v10.0.3。

这可行:

// the encodings I want, in the order of preference
val myEncodings = Seq(MediaRange(`application/xml`),MediaRange( `application/json`))

...
path("api") {
          (get & extract(_.request.headers)){ requestHeaders =>
              val mediaTypeNegotiator = new MediaTypeNegotiator(requestHeaders)
              val encoding = mediaTypeNegotiator
                     .acceptedMediaRanges
                     .intersect(myEncodings)
                     .headOption
                     .getOrElse(MediaRange(`application/json`))
              complete {
                     // check "encoding" here and make decision.
              }
          }
        }
...

你也可以这样做

val myEncodings = Seq(MediaRange(`application/xml`),MediaRange( `application/json`))

path("api") {
      (get & extract(_.request.headers)){ requestHeaders =>
        complete {
          val mediaTypeNegotiator = new MediaTypeNegotiator(requestHeaders)
          if(mediaTypeNegotiator.accept(MediaTypes.`application/xml`)) {
            // respond with xml
          } else if(mediaTypeNegotiator.accept(MediaTypes.`application/json`)) {
            // respond with json
          } else {
            // respond with json by default or reject properly :
            reject(UnsupportedRequestContentTypeRejection(Set(MediaTypes.`application/xml`, MediaTypes.`application/json`)))
          }
      }
    }
}

希望这会有所帮助。

【讨论】:

    【解决方案3】:

    问题 #1 的潜在答案似乎是这样,但我想通过自定义指令或更优雅的方式来解决。不幸的是,似乎缺少 Akka-Http 自定义指令的文档。

    // the encodings I want, in the order of preference
    val myEncodings = Seq(MediaRange(`application/xml`),MediaRange( `application/json`))
    
        ...
        path("api") {
                  (get & extract(_.request.acceptedMediaRanges)){  
                      r => 
                        val encoding = 
                          r.intersect(myEncodings).headOption
                             .getOrElse(MediaRange(`application/json`))
                      complete {
                             // check "encoding" here and make decision.
                      }
                  }
                }
        ...
    

    希望有人可以提供更清洁的东西。

    【讨论】:

    • 我更新了答案,你可以看看,是不是你想要的。
    猜你喜欢
    • 2015-04-07
    • 1970-01-01
    • 2015-08-26
    • 2017-08-28
    • 2020-02-07
    • 1970-01-01
    • 2013-04-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多