喷涂路线未按预期工作。 GET请求同时执行DELETE

Spray route not working as expected. GET request also executes DELETE

部分路由自动执行。我认为这与 Api.scala

中的代码有关

RegistrationsRoute.scala

package com.admcore.api

import scala.concurrent.ExecutionContext
import spray.util.LoggingContext
import spray.json.DefaultJsonProtocol
import spray.routing._
import spray.json._
import spray.http.StatusCodes
import spray.httpx.marshalling._
import spray.httpx.SprayJsonSupport._
import akka.actor.ActorRef
import akka.pattern.ask
import scala.util.{Failure, Success}
import akka.util.Timeout
import scala.concurrent.duration._
import scala.util.Success
import scala.util.Failure
import scala.language.postfixOps
import com.admcore.service.RegistrationsService.{GetRegistrationsMessage, GetRegistrationMessage, PostRegistrationMessage, PutRegistrationMessage, DeleteRegistrationMessage} 
import com.admcore.model.{Registration, RegistrationJsonProtocol}
import com.admcore.model.RegistrationJsonProtocol._
import com.admcore.Services

class RegistrationsRoutes(services: Services)(implicit ec: ExecutionContext, log: LoggingContext) extends ApiRoute(services) {

  import com.admcore.api.ApiRoute._
  import ApiRouteProtocol._
  import com.admcore.model.RegistrationJsonProtocol._

  implicit val timeout = Timeout(10 seconds)

  val route: Route = {
    pathPrefix("adsregistrations") {
      pathEnd {
        post {
          entity(as[Registration]) { registration =>
            withService("adsRegistrations") { service =>
              val future = (service ? PostRegistrationMessage(registration)).mapTo[Registration]

              onComplete(future) {
                case Success(result) =>
                  complete(result.toString)

                case Failure(e) =>
                  log.error(s"Error: ${e.toString}")
                  complete(StatusCodes.InternalServerError, Message(ApiMessages.UnknownException))
              }
            }
          }
        } ~
        put {
          entity(as[Registration]) { registration =>
            withService("adsRegistrations") { service =>
              val future = (service ? PutRegistrationMessage(registration)).mapTo[Registration]

              onComplete(future) {
                case Success(result) =>
                  complete(result.toString)

                case Failure(e) =>
                  log.error(s"Error: ${e.toString}")
                  complete(StatusCodes.InternalServerError, Message(ApiMessages.UnknownException))
              }
            }
          }
        } ~
        get {
          withService("adsRegistrations") { service =>
            val future = (service ? GetRegistrationsMessage()).mapTo[Vector[Registration]]

            onComplete(future) {
              case Success(result) =>
                complete(Message(result.toString))

              case Failure(e) =>
                log.error(s"Error: ${e.toString}")
                complete(StatusCodes.InternalServerError, Message(ApiMessages.UnknownException))
            }
          }
        }
      } ~
      pathPrefix(Segment) { (registrationId) =>
        pathEnd {
          delete {
            withService("adsRegistrations") { service =>
              val future = (service ? DeleteRegistrationMessage(registrationId)).mapTo[Registration]

              onComplete(future) {
                case Success(result) =>
                  complete(Message(result.toString))

                case Failure(e) =>
                  log.error(s"Error: ${e.toString}")
                  complete(StatusCodes.InternalServerError, Message(ApiMessages.UnknownException))
              }
            }
          } ~
          get {
            withService("adsRegistrations") { service =>
              val future = (service ? GetRegistrationMessage(registrationId)).mapTo[Registration]

              onComplete(future) {
                case Success(result) =>
                  complete(Message(result.toString))

                case Failure(e) =>
                  log.error(s"Error: ${e.toString}")
                  complete(StatusCodes.InternalServerError, Message(ApiMessages.UnknownException))
              }
            }
          }
        }
      }
    }
  }
}

Registration.scala

package model

import akka.actor.{Actor, ActorLogging}
import spray.json._
import spray.json.DefaultJsonProtocol._
import spray.httpx.SprayJsonSupport
import com.mongodb.casbah.Imports._
import org.bson.types.ObjectId
import com.mongodb.DBObject
import com.mongodb.casbah.commons.{MongoDBList, MongoDBObject}

case class Registration(
  system: String, 
  identity: String, 
  id: Option[String] = None)

object RegistrationJsonProtocol extends DefaultJsonProtocol {
  implicit val adsRegistrationFormat = jsonFormat3(Registration)
}

RegistrationsService.scala

package com.admcore.service

import akka.actor.{Props, ActorLogging, Actor}
import spray.json._
import com.mongodb.util.JSON
import com.mongodb.casbah.Imports._
import com.admcore.DBConfiguration
import com.admcore.MongoContext
import com.admcore.model.{Registration, RegistrationDAO}


object RegistrationsService {
  case class PostRegistrationMessage(registration: Registration)
  case class PutRegistrationMessage(registration: Registration)
  case class DeleteRegistrationMessage(registrationId: String)
  case class GetRegistrationMessage(registrationId: String)
  case class GetRegistrationsMessage()

  def props(property: String) = Props(classOf[RegistrationsService], property)
}

class RegistrationsService(property: String) extends Actor with ActorLogging with DBConfiguration {
  import RegistrationsService._

  def receive = {    
    case PostRegistrationMessage(registration) => {
      log.debug(s"POST ${registration}")

      val dao = new RegistrationDAO(mongoContext.adsRegistrations)
      val result = dao.insert(registration)
      log.debug(s"result: $result")

      sender() ! result.get
    }

    case PutRegistrationMessage(registration) => {
      log.debug(s"PUT ${registration}")

      val dao = new RegistrationDAO(mongoContext.adsRegistrations)
      val result = dao.update(registration)
      log.debug(s"result: $result")

      sender() ! result.get
    }

    case DeleteRegistrationMessage(registrationId) => {
      log.debug(s"DELETE ${registrationId}")

      val dao = new RegistrationDAO(mongoContext.adsRegistrations)
      val result = dao.delete(registrationId)
      log.debug(s"result: $result")

      sender() ! result.get
    }

    case GetRegistrationMessage(registrationId) => {
      log.debug(s"GET ${registrationId}")

      val dao = new RegistrationDAO(mongoContext.adsRegistrations)
      val result = dao.findOne(new ObjectId(registrationId))
      log.debug(s"result: $result")

      sender() ! result.get
    }

    case GetRegistrationsMessage() => {
      log.debug(s"GET all ADS registration")

      val dao = new RegistrationDAO(mongoContext.adsRegistrations)
      val result = dao.find()
      log.debug(s"result: $result")

      sender() ! result
    }
  }
}

Api.scala

package com.admcore.api

import spray.routing._
import akka.actor.{ActorRef, ActorLogging, Props}
import akka.io.IO
import scala.concurrent.ExecutionContext.Implicits.global
import spray.can.Http
import spray.json.DefaultJsonProtocol
import spray.util.LoggingContext
import spray.httpx.SprayJsonSupport
import spray.http.HttpHeaders.{`Access-Control-Allow-Origin`, `Access-Control-Allow-Credentials`, `Access-Control-Allow-Headers`, `Access-Control-Allow-Methods`}
import spray.http.HttpMethods._
import spray.http.{StatusCodes, HttpOrigin, SomeOrigins}
import com.admcore.util.ConfigHolder
import com.admcore.{Services, Core, CoreActors}

trait CORSSupport extends Directives {
  private val CORSHeaders = List(
    `Access-Control-Allow-Methods`(GET, POST, PUT, DELETE, OPTIONS),
    `Access-Control-Allow-Headers`("Origin, X-Requested-With, Content-Type, Accept, Accept-Encoding, Accept-Language, Host, Referer, User-Agent"),
    `Access-Control-Allow-Credentials`(true)
  )

  def respondWithCORS(origin: String)(routes: => Route) = {
    val originHeader = `Access-Control-Allow-Origin`(SomeOrigins(Seq(HttpOrigin(origin))))

    respondWithHeaders(originHeader :: CORSHeaders) {
      routes ~ options { complete(StatusCodes.OK) }
    }
  }
}

trait Api extends Directives with RouteConcatenation with CORSSupport with ConfigHolder {
  this: CoreActors with Core =>

  val routes =
    respondWithCORS(config.getString("origin.domain")) {
      pathPrefix("api" / "v1") {
        new RegistrationsRoutes(services).route
      }
    }

  val rootService = system.actorOf(ApiService.props(config.getString("hostname"), config.getInt("port"), routes))
}

object ApiService {
  def props(hostname: String, port: Int, routes: Route) = Props(classOf[ApiService], hostname, port, routes)
}

class ApiService(hostname: String, port: Int, routes: Route) extends HttpServiceActor with ActorLogging {
  IO(Http)(context.system) ! Http.Bind(self, hostname, port)

  def receive: Receive = runRoute(routes)
}

object ApiRoute {
  case class Message(message: String)

  object ApiRouteProtocol extends DefaultJsonProtocol {
    implicit val messageFormat = jsonFormat1(Message)
  }

  object ApiMessages {
    val UnknownException = "Unknown exception"
    val UnsupportedService = "Sorry, provided service is not supported."
  }
}

abstract class ApiRoute(services: Services = Services.empty)(implicit log: LoggingContext) extends Directives with SprayJsonSupport {

  import com.admcore.api.ApiRoute.{ApiMessages, Message}
  import com.admcore.api.ApiRoute.ApiRouteProtocol._

  def withService(id: String)(action: ActorRef => Route) = {
    services.get(id) match {
      case Some(provider) =>
        action(provider)

      case None =>
        log.error("Unsupported service: $id")
        complete(StatusCodes.BadRequest, Message(ApiMessages.UnsupportedService))
    }
  }
}

您的路由在构建时执行,而不是在请求处理时执行:这适用于执行一次的 GET 请求以及对每个请求同时执行的 GET/DELETE。有关详细说明,请参阅 spray docs。要解决此问题,请将要评估的未来直接传递给 onComplete 指令,而不是将其分配给 val。这将推迟对未来的评估,直到实际处理相应的请求。所以而不是

get {
  withService("adsRegistrations") { service =>
    val future = (service ? GetRegistrationsMessage()).mapTo[Vector[Registration]]

    onComplete(future) {
      case Success(result) =>
        complete(Message(result.toString))

      case Failure(e) =>
        log.error(s"Error: ${e.toString}")
        complete(StatusCodes.InternalServerError, Message(ApiMessages.UnknownException))
    }
  }
}

使用

get {
  withService("adsRegistrations") { service =>    
    onComplete((service ? GetRegistrationsMessage()).mapTo[Vector[Registration]]) {
      case Success(result) =>
        complete(Message(result.toString))

      case Failure(e) =>
        log.error(s"Error: ${e.toString}")
        complete(StatusCodes.InternalServerError, Message(ApiMessages.UnknownException))
    }
  }

编辑:更新了可能修复的答案

这就是我为了其他人的利益而最终得到的。我仍然需要弄清楚如何 return 一个正确的 json 对象字符串,而不仅仅是 toString,但那是以后的事了。此代码确保不会为每个请求执行多个方法,并且在应用程序启动时没有方法 运行。

package com.admcore.api

import akka.actor.ActorRef
import akka.pattern.ask
import akka.util.Timeout
import spray.json._
import spray.routing._
import spray.http.StatusCodes
import spray.httpx.marshalling._
import spray.httpx.SprayJsonSupport._
import spray.util.LoggingContext
import scala.concurrent.ExecutionContext
import scala.concurrent.duration._
import scala.language.postfixOps
import scala.util.{Failure, Success}

import com.admcore.Services
import com.admcore.model.Registration
import com.admcore.model.RegistrationJsonProtocol._
import com.admcore.service.RegistrationsService.{
    GetRegistrationsMessage, GetRegistrationMessage, 
    PostRegistrationMessage, PutRegistrationMessage, DeleteRegistrationMessage 
} 

class RegistrationsRoutes(services: Services)(implicit ec: ExecutionContext, log: LoggingContext) extends ApiRoute(services) {

  import com.admcore.api.ApiRoute._
  import ApiRouteProtocol._

  implicit val timeout = Timeout(10 seconds)

  val getAllRegistrations = pathPrefix("adsregistrations") & pathEnd & get
  val postRegistration = pathPrefix("adsregistrations") & pathEnd & post
  val putRegistration = pathPrefix("adsregistrations" / Segment) & pathEnd & put
  val deleteRegistration = pathPrefix("adsregistrations" / Segment) & pathEnd & delete
  val getRegistration = pathPrefix("adsregistrations" / Segment) & pathEnd & get

  val route: Route = {
    withService("registrations") { service =>
      getAllRegistrations {
        onComplete((service ? GetRegistrationsMessage()).mapTo[Vector[Registration]]) {
          case Success(result) =>
            complete(Message(result.toString))

          case Failure(e) =>
            log.error(s"Error: ${e.toString}")
            complete(StatusCodes.InternalServerError, Message(ApiMessages.UnknownException))
        }
      } ~
      postRegistration {
        entity(as[Registration]) { registration =>
          val future = (service ? PostRegistrationMessage(registration)).mapTo[Registration]

          onComplete(future) {
            case Success(result) =>
              complete(result.toString)

            case Failure(e) =>
              log.error(s"Error: ${e.toString}")
              complete(StatusCodes.InternalServerError, Message(ApiMessages.UnknownException))
          }          
        }
      } ~
      putRegistration { registrationId =>
        entity(as[Registration]) { registration =>
          val future = (service ? PutRegistrationMessage(registration)).mapTo[Registration]

          onComplete(future) {
            case Success(result) =>
              complete(result.toString)

            case Failure(e) =>
              log.error(s"Error: ${e.toString}")
              complete(StatusCodes.InternalServerError, Message(ApiMessages.UnknownException))
          }
        }
      } ~
      deleteRegistration { registrationId =>
        val future = (service ? DeleteRegistrationMessage(registrationId)).mapTo[Registration]

        onComplete(future) {
          case Success(result) =>
            complete(Message(result.toString))

          case Failure(e) =>
            log.error(s"Error: ${e.toString}")
            complete(StatusCodes.InternalServerError, Message(ApiMessages.UnknownException))
        }
      } ~
      getRegistration { registrationId =>
        val future = (service ? GetRegistrationMessage(registrationId)).mapTo[Registration]

        onComplete(future) {
          case Success(result) =>
            complete(Message(result.toString))

          case Failure(e) =>
            log.error(s"Error: ${e.toString}")
            complete(StatusCodes.InternalServerError, Message(ApiMessages.UnknownException))
        }
      }
    }
  }
}