2015-11-20 39 views
0

我已經嘗試了很多事情,但不管我在測試中做了什麼(它只是發送PUT請求以「創建用戶」)日誌不會輸入任何pathPrefix,只是走到最後,無法匹配任何東西。噴霧路由不匹配任何東西

任何人都可以提供見解?下面是我寫的以及簡單的測試(它甚至不檢查任何東西)

我總體上知道它是相當基本的,我沒有做很好的事情,但它只是我扔在一起的東西我覺得我可以做點幾個快速測試的路線

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 { 

     } 
    } 
    } 
} 
+0

嘗試使用path(「newuser」)而不是pathPrefix(「newuser」) – Nyavro

+0

它甚至沒有輸入pathPrefix(「user」),但我會給那個鏡頭 –

回答

1

刪除req =>幫助。如果我沒有記錯,put,get,post等不提供任何指令的參數。 path也一樣。刪除uri =>也可能需要。

更新:

如果req =>是留,然後敲定請求的所有來電應在該對象上調用。因此,例如,代替complete(entityJson),最頂級的上下文對象應該有req.complete(entityJson)

對於給定的代碼,帶上下文的最上面的塊是第一個包含uri的塊,因此代碼中的某處應該調用uri.complete

+0

那些肯定會提供一個執行上下文,我使用它來傳遞給我的函數,我甚至沒有輸入路徑前綴(除非遍歷不是這種方式,並且必須匹配完整路徑),但函數過濾器可能不需要它 –

+0

如果你想要繼續使用'req =>'和上下文,那麼應該在該上下文中調用所有'complete'調用。例如,'req.complete(newEntityJson)'。 – Nebril

+0

ok ive嘗試了其他的東西,沒有任何工作,甚至沒有添加像路徑(「用戶」/「新用戶」){完整(「測試」)}直接路徑,所以我即將嘗試下一個 –