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