繁体   English   中英

如何在 Akka HTTP 中将 `text/plain` 解组为 JSON

[英]How to unmarshall `text/plain` as JSON in Akka HTTP

我正在使用旧的 HTTP API(我无法更改),它在正文中使用 JSON 进行响应,但提供了Content-Type: text/plain; charset=utf-8 Content-Type: text/plain; charset=utf-8标头。

我试图将该 HTTP 正文解组为 JSON,但出现以下异常: akka.http.scaladsl.unmarshalling.Unmarshaller$UnsupportedContentTypeException: Unsupported Content-Type, supported: application/json

我的代码如下所示:

import spray.json.DefaultJsonProtocol
import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport._
import akka.http.scaladsl.unmarshalling._

case class ResponseBody(status: String, error_msg: String)

object ResponseBodyJsonProtocol extends DefaultJsonProtocol {
  implicit val responseBodyFormat = jsonFormat2(ResponseBody)
}

def parse(entity: HttpEntity): Future[ResponseBody] = {
  implicit val materializer: Materializer = ActorMaterializer()
  import ResponseBodyJsonProtocol._
  Unmarshal[HttpEntity](entity).to[ResponseBody]
}

示例 HTTP 响应如下所示:

HTTP/1.1 200 OK
Cache-Control: private
Content-Encoding: gzip
Content-Length: 161
Content-Type: text/plain; charset=utf-8
Date: Wed, 16 Dec 2015 18:15:14 GMT
Server: Microsoft-IIS/7.5
Vary: Accept-Encoding
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET

{"status":"1","error_msg":"Missing parameter"}

如何忽略 HTTP 响应中的Content-Type并解析为 JSON?

我发现的一种解决方法是在解HttpEntity之前手动设置HttpEntity上的Content-Type

def parse(entity: HttpEntity): Future[ResponseBody] = {
  implicit val materializer: Materializer = ActorMaterializer()
  import ResponseBodyJsonProtocol._
  Unmarshal[HttpEntity](entity.withContentType(ContentTypes.`application/json`)).to[ResponseBody]
}

似乎工作正常,但我对其他想法持开放态度......

我会使用map...指令。 它看起来又短又优雅。

val routes = (decodeRequest & encodeResponse) {
  mapResponseEntity(_.withContentType(ContentTypes.`application/json`)) {
    nakedRoutes ~ authenticatedRoutes
  }
}

另一种解决方案:创建一个接受任何内容类型(或任何内容类型列表)的自定义解组器:

import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport.sprayJsValueByteStringUnmarshaller
import akka.http.scaladsl.model.ContentTypeRange
import akka.http.scaladsl.unmarshalling.{FromEntityUnmarshaller, Unmarshaller}
import spray.json.{RootJsonReader, jsonReader}

// Add this to scope, INSTEAD OF importing SprayJsonSupport._
// It returns an Unmarshaller identical to SprayJsonSupport.sprayJsonUnmarshaller, but with a custom validation on content type
implicit def lenientJsonUnmarshaller[T](implicit reader: RootJsonReader[T]): FromEntityUnmarshaller[T] =
  Unmarshaller.byteStringUnmarshaller
    .forContentTypes(ContentTypeRange.*) // or any range you'd like
    .andThen(sprayJsValueByteStringUnmarshaller)
    .map(jsonReader[T].read)

然后 - 在范围内,继续像你一样:

def parse(entity: HttpEntity): Future[ResponseBody] = {
  implicit val materializer: Materializer = ActorMaterializer()
  import ResponseBodyJsonProtocol._
  Unmarshal[HttpEntity](entity).to[ResponseBody] // this implicitly uses the custom unmarshaller
}

好处 - 很容易重用这个隐式解组器 - 编写一次,只需在需要的地方导入它,而不必在实体上设置内容类型。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM