diff --git a/akka-docs/rst/scala/http/migration-from-spray.rst b/akka-docs/rst/scala/http/migration-from-spray.rst index f145bc2562..f49c0ae8d8 100644 --- a/akka-docs/rst/scala/http/migration-from-spray.rst +++ b/akka-docs/rst/scala/http/migration-from-spray.rst @@ -1,11 +1,185 @@ -Migration Guide from spray +Migration Guide from Spray ========================== -**TODO - will be written shortly.** +General notes +------------- + +Features which are not ported to the akka-http: - ``respondWithStatus`` also known as ``overrideStatusCode`` has not been forward ported to Akka HTTP, as it has been seen mostly as an anti-pattern. More information here: https://github.com/akka/akka/issues/18626 - ``respondWithMediaType`` was considered an anti-pattern in spray and is not ported to Akka HTTP. Instead users should rely on content type negotiation as Akka HTTP implements it. More information here: https://github.com/akka/akka/issues/18625 -- :ref:`registeringCustomMediaTypes` changed from Spray in order not to rely on global state. \ No newline at end of file +- :ref:`registeringCustomMediaTypes` changed from Spray in order not to rely on global state. + +Removed HttpService +------------------- + +Spray’s ``HttpService`` was removed. This means that scala code like this:: + + val service = system.actorOf(Props(new HttpServiceActor(routes))) + IO(Http)(system) ! Http.Bind(service, "0.0.0.0", port = 8080) + +needs to be changed into:: + + Http().bindAndHandle(routes, "0.0.0.0", port = 8080) + +Changes in Marshalling +---------------------- + +Marshaller.of can be replaced with ``Marshaller.withFixedContentType``. + +Was:: + + Marshaller.of[JsonApiObject](`application/json`) { (value, contentType, ctx) => + ctx.marshalTo(HttpEntity(contentType, value.toJson.toString)) + } + +Replace with:: + + Marshaller.withFixedContentType(`application/json`) { obj => + HttpEntity(`application/json`, obj.toJson.compactPrint) + } + +Akka HTTP marshallers support content negotiation, now it's not necessary to specify content type +when creating one “super” marshaller from other marshallers: + +Before:: + + ToResponseMarshaller.oneOf( + `application/vnd.api+json`, + `application/json` + )( + jsonApiMarshaller, + jsonMarshaller + } + +After:: + + Marshaller.oneOf( + jsonApiMarshaller, + jsonMarshaller + ) + +Changes in Unmarshalling +------------------------ + +Akka Http contains a set of predefined unmarshallers. This means that scala code like this:: + + Unmarshaller[Entity](`application/json`) { + case HttpEntity.NonEmpty(contentType, data) => + data.asString.parseJson.convertTo[Entity] + } + +needs to be changed into:: + + Unmarshaller + .stringUnmarshaller + .forContentTypes(`application/json`) + .map(_.parseJson.convertTo[Entity]) + +Changes in MediaTypes +--------------------- + +``MediaType.custom`` can be replaced with specific methods in ``MediaType`` object. + +Was:: + + MediaType.custom("application/vnd.acme+json") + +Replace with:: + + MediaType.applicationWithFixedCharset("application/vnd.acme+json", HttpCharsets.`UTF-8`) + +Changes in Rejection Handling +----------------------------- + +``RejectionHandler`` now uses a builder pattern – see the example: + +Before:: + + 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))) + } + } + + +After:: + + 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) + +Changes in HTTP Client +---------------------- + +The Spray-client pipeline was removed. Http’s ``singleRequest`` should be used instead of ``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 ⇒ + … + } + +needs to be changed into:: + + val request = HttpRequest( + method = PATCH, + uri = Uri(uri), + headers = List(Authorization(OAuth2BearerToken(accessToken))), + entity = HttpEntity(MediaTypes.`application/json`, object) + ) + + http.singleRequest(request).map { + case … => … + } + +Changes in Headers +------------------ + +All HTTP headers have been moved to the ``akka.http.scaladsl.model.headers._`` package. + + +Changes in form fields and file upload directives +------------------------------------------------- + +With the streaming nature of http entity, it’s important to have a strict http entity before accessing +multiple form fields or use file upload directives. +One solution might be using next directive before working with form fields:: + + val toStrict: Directive0 = extractRequest flatMap { request => + onComplete(request.entity.toStrict(5.seconds)) flatMap { + case Success(strict) => + mapRequest( req => req.copy(entity = strict)) + case _ => reject + } + } + +And one can use it like this:: + + toStrict { + formFields("name".as[String]) { name => + ... + } + } +