【问题标题】:Spray route not working as expected. GET request also executes DELETE喷涂路线未按预期工作。 GET 请求也执行 DELETE
【发布时间】:2015-01-09 18:04:03
【问题描述】:

某些路由是自动执行的。我认为这与 Api.scala 中的代码有关

  • POST 和 PUT 工作正常。天气我执行 GET 或 DELETE,两者都被执行。只是想知道为什么?

  • 在启动期间执行 GET /api/v1/adsresgistrations

  • 有没有更好的路线组织方式?

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))
    }
  }
}

【问题讨论】:

  • 对于编组问题,您是在导入spray.httpx.marshalling._ 吗?
  • 对于#1,检查GetRegistrationMessage 中的代码。确保您没有在其中删除? # 2 你的路线在我看来不错。
  • @SoumyaSimanta - 它不在 GetRegistrationMessage 中。我现在也用该代码更新了问题。我怀疑它与 Api 特征有关。我也更新了问题。请看一看。
  • @tobym - 已导入,但仍出现错误。暂时从问题中删除了这部分,只是为了专注于路线。

标签: json scala routes spray


【解决方案1】:

您的路由在构建时执行,而不是在请求处理时执行:这适用于执行一次的 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))
    }
  }

编辑:更新答案并可能修复

【讨论】:

  • 经过更多阅读,仍然无法弄清楚如何正确执行此操作。创建了另一个问题stackoverflow.com/questions/27894780/… 仅针对部署期间的自动执行。我想如果我能做到这一点,我就会知道如何解决另一个问题。感谢您提供的任何帮助。
  • 我已经更新了答案,提供了一个可能的解决方案,适用于应该解决您的问题。
  • 那行得通。谢谢。不分配给 val 就大不相同了。
  • 很高兴听到。将 future 声明为 lazy 也可以。
【解决方案2】:

这是我最终为他人谋福利的结果。我仍然需要弄清楚如何返回对象的正确 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))
        }
      }
    }
  }
}

【讨论】:

    猜你喜欢
    • 2018-04-24
    • 2014-11-12
    • 2018-05-14
    • 2014-03-26
    • 1970-01-01
    • 2015-04-11
    • 1970-01-01
    相关资源
    最近更新 更多