Sprayからのマイグレーションガイド
一般的な注意事項
akka-httpに移植されていない機能:
``overrideStatusCode``とも呼ばれる ``respondWithStatus``は、ほとんどアンチパターンとして見られているので、Akka HTTPに対応するものはありません。 詳細はこちら:https://github.com/akka/akka/issues/18626
`` respondWithMediaType``はスプレーのアンチパターンとみなされ、Akka HTTPに移植されませんでした。 代わりに、ユーザーはAkka HTTPが実装するコンテントタイプネゴシエーションに頼るべきです。 詳細はこちら:https://github.com/akka/akka/issues/18625
:ref:`registeredCustomMediaTypes`がグローバルな状態に依存しないようにSprayから変更されています。
HttpServiceは取り除かれています
Sprayの``HttpService``は取り除かれています。これは以下のようなscalaのコード:
val service = system.actorOf(Props(new HttpServiceActor(routes)))
IO(Http)(system) ! Http.Bind(service, "0.0.0.0", port = 8080)
が以下のように変更する必要があることを意味しています:
Http().bindAndHandle(routes, "0.0.0.0", port = 8080)
マーシャリングの変更
Marshaller.ofは``Marshaller.withFixedContentType``で置き換えることができます。
置き換える前:
Marshaller.of[JsonApiObject](`application/json`) { (value, contentType, ctx) =>
ctx.marshalTo(HttpEntity(contentType, value.toJson.toString))
}
置き換えた後:
Marshaller.withFixedContentType(`application/json`) { obj =>
HttpEntity(`application/json`, obj.toJson.compactPrint)
}
Akka HTTPマーシャラはコンテントネゴシエーションをサポートしているので、他のマーシャラーから「スーパー」マーシャラーを作成するときにコンテンツタイプを指定する必要はありません。
前:
ToResponseMarshaller.oneOf(
`application/vnd.api+json`,
`application/json`
)(
jsonApiMarshaller,
jsonMarshaller
}
後:
Marshaller.oneOf(
jsonApiMarshaller,
jsonMarshaller
)
アンマーシャリングの変更
Akka Httpには、あらかじめ定義されたアンマーシャラーのセットが含まれています。 つまり、次のようなscalaのコードは:
Unmarshaller[Entity](`application/json`) {
case HttpEntity.NonEmpty(contentType, data) =>
data.asString.parseJson.convertTo[Entity]
}
が以下のように変更する必要があることを意味しています:
Unmarshaller
.stringUnmarshaller
.forContentTypes(`application/json`)
.map(_.parseJson.convertTo[Entity])
MediaTypesの変更
``MediaType.custom``は``MediaType``オブジェクトの所定のメソッドで置き換えられます。
置き換える前:
MediaType.custom("application/vnd.acme+json")
置き換えた後:
MediaType.applicationWithFixedCharset("application/vnd.acme+json", HttpCharsets.`UTF-8`)
リジェクションハンドリングの変更
``RejectionHandler``はBuilderパターンとして使われています。以下の例を参照してください:
前:
def rootRejectionHandler = RejectionHandler {
case Nil =>
requestUri { uri =>
logger.error("Route: {} does not exist.", uri)
complete((NotFound, mapErrorToRootObject(notFoundError)))
}
case AuthenticationFailedRejection(cause, challengeHeaders) :: _ => {
logger.error(s"Request is rejected with cause: $cause")
complete((Unauthorized, mapErrorToRootObject(unauthenticatedError)))
}
}
後:
RejectionHandler
.newBuilder()
.handle {
case AuthenticationFailedRejection(cause, challengeHeaders) =>
logger.error(s"Request is rejected with cause: $cause")
complete((Unauthorized, mapErrorToRootObject(unauthenticatedError)))
.handleNotFound { ctx =>
logger.error("Route: {} does not exist.", ctx.request.uri.toString())
ctx.complete((NotFound, mapErrorToRootObject(notFoundError)))
}
.result()
.withFallback(RejectionHandler.default)
HTTPクライアントの変更
Sprayクライアントパイプラインは取り除かれました。Httpの``singleRequest`が``sendReceive``の代わりに使われるべきです:
//this will not longer work
val token = Authorization(OAuth2BearerToken(accessToken))
val pipeline: HttpRequest => Future[HttpResponse] = (addHeader(token) ~> sendReceive)
val patch: HttpRequest = Patch(uri, object))
pipeline(patch).map { response ⇒
…
}
が以下のように変更する必要があることを意味しています:
val request = HttpRequest(
method = PATCH,
uri = Uri(uri),
headers = List(Authorization(OAuth2BearerToken(accessToken))),
entity = HttpEntity(MediaTypes.`application/json`, object)
)
http.singleRequest(request).map {
case … => …
}
ヘッダーの変更
すべてのHTTPヘッダーは``akka.http.scaladsl.model.headers._``パッケージに移動しました。
フォームフィールドとファイルアップロードディレクティブの変更
HTTPエンティティのストリーミングの性質上、複数のフォームフィールドにアクセスする前に厳密なHTTPエンティティを持つか、ファイルアップロードディレクティブを使用することが重要です。 1つの解決策は、フォームフィールドを扱う前にnextディレクティブを使用することです:
val toStrict: Directive0 = extractRequest flatMap { request =>
onComplete(request.entity.toStrict(5.seconds)) flatMap {
case Success(strict) =>
mapRequest( req => req.copy(entity = strict))
case _ => reject
}
}
あるいはこのようにも使うことができます:
toStrict {
formFields("name".as[String]) { name =>
...
}
}
Contents