喷涂路线不匹配 [英] Spray Routing Doesn't match anything

查看:80
本文介绍了喷涂路线不匹配的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经尝试了很多事情,但是无论我在测试中做了什么(只是发送一个PUT请求以创建用户),日志都不会输入任何pathPrefix,而只是结束了而无法匹配任何内容。

I have tried many things, but no matter what I do in my tests (which simply sends a PUT request to "create a user") the logs do not enter any of the pathPrefix and just go to the end and fail to match anything.

有人可以提供见解吗?以下是我编写的课程以及简单的测试(甚至还没有检查任何内容)

Can anyone offer insight? Below is the class I have written as well as the simple test (which doesn't even check anything yet)

我知道总体而言,这很初级,我没有做很棒,但这只是我共同努力的一点,我觉得我可以对路由做一些快速测试

I know overall it is rather rudimentary and I am not doing things great, but it's just something I threw together to the point where I felt i could do a few quick tests on the routing

package system

import akka.actor.{Props, ActorRef, ActorLogging}
import akka.util.Timeout
import spray.http.HttpHeader
import spray.routing.directives.OnCompleteFutureMagnet
import spray.routing.{RequestContext, HttpServiceActor, Route}
import spray.http.StatusCodes._

import scala.concurrent.duration._

import akka.pattern.ask

import system.F_BackBone._

import scala.util.{Failure, Success}

import language.postfixOps

class F_Listener(backbone: ActorRef) extends HttpServiceActor with ActorLogging {
  import context.dispatcher

  implicit val timeout = Timeout(5 seconds)

  log.debug("beginning user log\n")

  //TODO write the main listener spawner that acts as the main spray server and binds these listeners to connections
  //TODO must watch the spray side http server actor and die with it

  val route: Route = { uri =>
    log.debug("request received " + uri.unmatchedPath)

    pathPrefix("data") { req =>
      log.debug("data path detected " + req.unmatchedPath + "\n")
      get {
        genericGet(req, GetImage) //URIs for actual data like pictures
      } ~
        path("uploadimage") {
          put { req =>
            genericPut(PutImage(req.request.entity))
          }
        }
    } ~
    pathPrefix("user") { req =>
      log.debug("user path detected" + req.unmatchedPath + "\n")
      get {
        genericGet(req, GetUserInfo)
      } ~
        post {
          genericPost(req, req.request.headers, UpdateUserData)
        } ~
        delete {
          genericDelete(req, DeleteUser)
        }
      pathPrefix("newuser") {
        put { req =>
          genericPut(CreateUser(req.request))
        }
      }
    } ~
    pathPrefix("page") { req =>
      log.debug("page path detected" + "\n")
      get {
        genericGet(req, GetPageInfo)
      } ~
        post {
          genericPost(req, req.request.headers, UpdatePageData)
        } ~
        delete {
          genericDelete(req, DeletePage)
        }
      path("newpage") {
        put { req =>
          genericPut(CreatePage(req.request))
        }
      }
    } ~
    pathPrefix("profile") { req =>
      log.debug("profile path detected" + "\n")
      get {
        genericGet(req, GetProfileInfo)
      } ~ //no need for "createprofile" they are created with the user and can be accessed through the JSON returned with that creation
        post {
          genericPost(req, req.request.headers, UpdateProfileData)
        } //no need to delete profile, deleted with user
    } ~
    pathPrefix("picture") { req =>
      log.debug("picture path detected" + "\n")
      get {
        genericGet(req, GetPictureInfo)
      } ~ //same as for profile, when you upload an image the picture JSON is created
        post {
          genericPost(req, req.request.headers, UpdateImageData)
        } ~
        delete {
          genericDelete(req, DeletePicture)
        }
    } ~
    pathPrefix("album") { req =>
      log.debug("album path detected" + "\n")
      get {
        genericGet(req, GetAlbumInfo)
      } ~
        post {
          genericPost(req, req.request.headers, UpdateAlbumData)
        } ~
        delete {
          genericDelete(req, DeleteAlbum)
        }
      path("createalbum") {
        put { req =>
          genericPut(CreateAlbum(req.request))
        }
      }
    }
    log.error("no path matched" + "\n")
    complete(NotFound, "That resource does not exist")
  }

  /**
   * goes inside of a spray routing "get" and completes the passed message to the backbone given the id
   * it internally converts the remaining request path to a bigint, if this fails it completes with a failure
   * @param req the reques who contains the string to be converted to bigint as an ID
   * @param messageConstructor the case class message (constructor but can be passed with sugar) to compose the bigint into
   * @return returns a route for thed spray routing to go through and side effects the complete needed
   */
  def genericGet(req: RequestContext, messageConstructor: (BigInt) => GetInfo): Route = {
    val id = req.unmatchedPath.toString

    if (!id.contains("/")) { //if this is the last element only
      try {
        val idBig = BigInt(id)
        onComplete(OnCompleteFutureMagnet((backbone ? messageConstructor.apply(idBig)).mapTo[String])) {
          case Success(entityJson) =>
            log.info("get completed successfully: " + messageConstructor + " " + "for " + idBig)
            complete(entityJson)
          case Failure(ex) =>
            log.error(ex, "get failed: " + messageConstructor + " for " + idBig)
            complete(InternalServerError, "Request could not be completed: \n" + ex.getMessage)
        }
      } catch {
        case e: NumberFormatException =>
          log.info("illegally formatted id requested: " + id)
          complete(BadRequest, "Numbers are formatted incorrectly")
      }
    }
    else reject
  }

  /**
   * same as above but for posts, I treid to write a more generic function to repeat rewriting code but it ended up just not being worth the thought
   * @param req request who contains id to parse to bigint
   * @param args arguments to change
   * @param messageConstructor the message to send
   * @return
   */
  def genericPost(req: RequestContext, args: List[HttpHeader], messageConstructor: (BigInt, List[HttpHeader]) => PostInfo) = {
    val id = req.unmatchedPath.toString

    if (!id.contains("/")) {
      try {
        val idBig = BigInt(id)
        onComplete(OnCompleteFutureMagnet((backbone ? messageConstructor.apply(idBig, args)).mapTo[String])) {
          case Success(newEntityJson) =>
            log.info("post completed successfully: " + messageConstructor + " for " + idBig)
            complete(newEntityJson)
          case Failure(ex) =>
            log.error(ex, "get failed: " + messageConstructor + " for " + idBig)
            complete(InternalServerError, "Request could not be completed: \n" + ex.getMessage)
        }
      } catch {
        case e: NumberFormatException =>
          log.info("illegally formatted id requested: " + id)
          complete(BadRequest, "Numbers are formatted incorrectly")
      }
    }
    else reject
  }

  /**
   * same as above except no need to parse a special message since the path tells all and this is for putting, so the fully constructed message gets passed here
   * @param message constructed message to send to the backbone for handling
   * @return
   */
  def genericPut(message: PutInfo) = {
    pathEnd {
      onComplete(OnCompleteFutureMagnet((backbone ? message).mapTo[String])) {
        case Success(newEntityJson) =>
          log.info("put completed successfully: " + message)
          complete(newEntityJson)
        case Failure(ex) =>
          log.error(ex, "put failed: " + message)
          complete(InternalServerError, "Error putting entity: " + ex.getMessage)
      }
    }
  }

  /**
   * almost identical to get, should probobly only be one function
   * @param req identical
   * @param messageConstructor identical
   * @return route
   */
  def genericDelete(req: RequestContext, messageConstructor: (BigInt) => DeleteInfo) = {
    val id = req.unmatchedPath.toString

    if (!id.contains("/")) {
      try {
        val idBig = BigInt(id)
        onComplete(OnCompleteFutureMagnet((backbone ? messageConstructor.apply(idBig)).mapTo[String])) {
          case Success(newEntityJson) =>
            log.info("delete completed successfully: " + messageConstructor + " for " + idBig)
            complete(newEntityJson)
          case Failure(ex) =>
            log.error(ex, "get failed: " + messageConstructor + " for " + idBig)
            complete(InternalServerError, "Request could not be completed: \n" + ex.getMessage)
        }
      } catch {
        case e: NumberFormatException =>
          log.info("illegally formatted id requested: " + id)
          complete(BadRequest, "Numbers are formatted incorrectly")
      }
    }
    else reject
  }

  override def receive = runRoute(route)
}

object F_Listener {
  def props(backbone: ActorRef) = Props(new F_Listener(backbone))
}


/*ideas for speed improvements:
parse out arguments before passing to backbone (might help with scaling to distributed system)
genericDelete, Post, Put, and Get are all pretty similar, some functional composition is probobly possible, esp for delete and get
 */

import akka.testkit.TestActorRef
import org.scalatest.{WordSpec}
import spray.http.HttpHeaders
import spray.testkit.ScalatestRouteTest
import system.F_Listener

class FakebookRoutingTests extends WordSpec with ScalatestRouteTest {
  val route = TestActorRef(new F_Listener(null)).underlyingActor.route
  //implicit val host = DefaultHostInfo(HttpHeaders.Host("fake.fakebook.com"), false)

  "the route" should {

    "succeed on path to create a user" in {
      Put("/user/newuser") ~> route ~> check {

      }
    }
  }
}


推荐答案

删除 req => 会有所帮助。如果我没记错的话,获取发布等。不要为指令提供任何参数。 路径也是如此。可能还需要删除 uri =>

Removing req => helps. If I remember correctly, put, get, post etc. don't provide any arguments for directives. Same goes for path. Removing uri => may also be needed.

更新:

如果要保留 req => ,则应在该对象上调用所有完成请求的调用。因此,例如,顶部应该是 req.complete(entityJson),而不是 complete(entityJson)大多数上下文对象。

If req => is to stay, then all calls finalizing the request should be called on that object. So, for example, instead of complete(entityJson) there should be req.complete(entityJson) on the top-most context object.

对于给定的代码,具有上下文的最上面的块是第一个包含 uri 的对象,因此该代码应该调用 uri.complete

For given code top-most block with context is the first one containing uri so somewhere in the code there should be a call to uri.complete.

这篇关于喷涂路线不匹配的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆