Акка-http: прием и обработка содержимого

Я пробовал Akka-http и, надеюсь, кто-то может пролить свет на следующие вопросы:

  • Как создать разные маршруты на основе заголовка accept: в запросе? Например, я хочу, чтобы один путь кода обрабатывал "json" и один обрабатывал запросы "xml" (по умолчанию "json", если отсутствует заголовок)

  • В случаях, когда я не хочу, чтобы тип contentType был выведен, как я его определяю? Например, в приведенном ниже коде я пытаюсь запустить json через compactPrint(), но это меняет его на строку, следовательно, "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
                }
              }
          }
        }
...

Ответ в этом случае является текстовым/открытым, так как compactPrint создает строку. критика очень приветствуется.;)

Ответ 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) }

Ответ 2

Потенциальный ответ на вопроС# 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.
                  }
              }
            }
    ...

Надеясь, что кто-то может обеспечить что-то более чистое.

Ответ 3

Кажется, что принятый ответ больше не работает с 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`)))
          }
      }
    }
}

Надеется, что это поможет.