我正在使用遗留的HTTP API(我无法更改),它在正文中以JSON响应,但提供了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?
答案 0 :(得分:7)
我发现的一个解决方法是在解组之前在HttpEntity
上手动设置def parse(entity: HttpEntity): Future[ResponseBody] = {
implicit val materializer: Materializer = ActorMaterializer()
import ResponseBodyJsonProtocol._
Unmarshal[HttpEntity](entity.withContentType(ContentTypes.`application/json`)).to[ResponseBody]
}
:
for /R "%zipFilePath%" %%I in ("*.zip") do (
"C:\Program Files\7-Zip\7z.exe" x -y -o"%%~dpI" "%%~fI"
)
似乎工作正常,但我对其他想法持开放态度......
答案 1 :(得分:2)
我使用map...
指令。它看起来简短而优雅。
val routes = (decodeRequest & encodeResponse) {
mapResponseEntity(_.withContentType(ContentTypes.`application/json`)) {
nakedRoutes ~ authenticatedRoutes
}
}
答案 2 :(得分:0)
另一种解决方案:创建一个接受任何内容类型(或任何内容类型列表)的自定义解组器:
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
}
好处 - 很容易重用这个隐式解组器 - 编写一次,只需在任何需要的地方导入,而不必在实体上设置内容类型。