2013-02-01 75 views
18

我還沒有找到一個可靠的示例或結構來將Spray.io路由分成多個文件。我發現我的路由的當前結構將變得非常繁瑣,並且將它們抽象爲用於非常簡單的REST API應用的不同「控制器」會很好。Spray.io路線可以拆分爲多個「控制器」嗎?

文檔似乎並沒有幫助太多:http://spray.io/documentation/spray-routing/key-concepts/directives/#directives

這是我到目前爲止有:

class AccountServiceActor extends Actor with AccountService { 

    def actorRefFactory = context 

    def receive = handleTimeouts orElse runRoute(demoRoute) 

    def handleTimeouts: Receive = { 
    case Timeout(x: HttpRequest) => 
     sender ! HttpResponse(StatusCodes.InternalServerError, "Request timed out.") 
    } 
} 


// this trait defines our service behavior independently from the service actor 
trait AccountService extends HttpService { 

    val demoRoute = { 
    get { 
     path("") { 
     respondWithMediaType(`text/html`) { // XML is marshalled to `text/xml` by default, so we simply override here 
      complete(index) 
     } 
     } ~ 
     path("ping") { 
     complete("PONG!") 
     } ~ 
     path("timeout") { ctx => 
     // we simply let the request drop to provoke a timeout 
     } ~ 
     path("crash") { ctx => 
     throw new RuntimeException("crash boom bang") 
     } ~ 
     path("fail") { 
     failWith(new RuntimeException("aaaahhh")) 
     } ~ 
     path("riaktestsetup") { 
     Test.setupTestData 
     complete("SETUP!") 
     } ~ 
     path("riaktestfetch"/Rest) { id => 
     complete(Test.read(id)) 
     } 
    } 
    } 
} 

感謝這個幫助!

回答

14

您可以使用〜combinator組合來自不同「控制器」的路由。

class AccountServiceActor extends Actor with HttpService { 

    def actorRefFactory = context 

    def receive = handleTimeouts orElse runRoute(
    new AccountService1.accountService1 ~ new AccountService2.accountService2) 

    def handleTimeouts: Receive = { 
    case Timeout(x: HttpRequest) => 
     sender ! HttpResponse(StatusCodes.InternalServerError, "Request timed out.") 
    } 
} 



class AccountService1 extends HttpService { 

    val accountService1 = { 
    get { 
     path("") { 
     respondWithMediaType(`text/html`) { // XML is marshalled to `text/xml` by default, so we simply override here 
      complete(index) 
     } 
     } 
    } 
} 


class AccountService2 extends HttpService { 

    val accountService2 = { 
    get { 
     path("someotherpath") { 
     respondWithMediaType(`text/html`) { // XML is marshalled to `text/xml` by default, so we simply override here 
      complete(index) 
     } 
     } 
    } 
} 
+0

看起來像這樣做的伎倆。我想知道我是否可以編寫某種可以自動將它們組合起來的隱式,而不是手動編寫service1〜service2〜service3。謝謝! – crockpotveggies

+0

嗯,因爲它看起來像創建某種繼承問題,取消選擇它。 '類型參數[com.threetierlogic.AccountServ ice.AccountServiceActor]不符合方法apply的類型參數範圍 [T <:akka.actor.Actor]' – crockpotveggies

+0

Ok'case class base(actorRefFactory:ActorRefFactory)擴展HttpService {'現在的問題是HTTP請求由於以下原因而失敗:'由於當前響應狀態爲'已完成'但是應該'未完成',因此無法將GET請求的響應(部分)作爲響應(部分)發送到'/ ' – crockpotveggies

33

我個人用這個大的API:

class ApiActor extends Actor with Api { 
    override val actorRefFactory: ActorRefFactory = context 

    def receive = runRoute(route) 
} 

/** 
* API endpoints 
* 
* Individual APIs are created in traits that are mixed here 
*/ 
trait Api extends ApiService 
    with AccountApi with SessionApi 
    with ContactsApi with GroupsApi 
    with GroupMessagesApi with OneToOneMessagesApi 
    with PresenceApi 
    with EventsApi 
    with IosApi 
    with TelephonyApi 
    with TestsApi { 
    val route = { 
    presenceApiRouting ~ 
    oneToOneMessagesApiRouting ~ 
    groupMessagesApiRouting ~ 
    eventsApiRouting ~ 
    accountApiRouting ~ 
    groupsApiRouting ~ 
    sessionApiRouting ~ 
    contactsApiRouting ~ 
    iosApiRouting ~ 
    telephonyApiRouting ~ 
    testsApiRouting 
    } 
} 

我建議把最常見的途徑第一,並在子路由,只要你可以使用pathPrefix,讓你減少Spray爲每個傳入請求運行的測試數量。

你會發現,我相信這是優化的路線如下:

val groupsApiRouting = { 
    pathPrefix("v3"/"groups") { 
     pathEnd { 
     get { 
      traceName("GROUPS - Get joined groups list") { listJoinedGroups } 
     } ~ 
     post { 
      traceName("GROUPS - Create group") { createGroup } 
     } 
     } ~ 
     pathPrefix(LongNumber) { groupId => 
     pathEnd { 
      get { 
      traceName("GROUPS - Get by ID") { getGroupInformation(groupId) } 
      } ~ 
      put { 
      traceName("GROUPS - Edit by ID") { editGroup(groupId) } 
      } ~ 
      delete { 
      traceName("GROUPS - Delete by ID") { deleteGroup(groupId) } 
      } 
     } ~ 
     post { 
      path("invitations"/LongNumber) { invitedUserId => 
      traceName("GROUPS - Invite user to group") { inviteUserToGroup(groupId, invitedUserId) } 
      } ~ 
      path("invitations") { 
      traceName("GROUPS - Invite multiple users") { inviteUsersToGroup(groupId) } 
      } 
     } ~ 
     pathPrefix("members") { 
      pathEnd { 
      get { 
       traceName("GROUPS - Get group members list") { listGroupMembers(groupId) } 
      } 
      } ~ 
      path("me") { 
      post { 
       traceName("GROUPS - Join group") { joinGroup(groupId) } 
      } ~ 
      delete { 
       traceName("GROUPS - Leave group") { leaveGroup(groupId) } 
      } 
      } ~ 
      delete { 
      path(LongNumber) { removedUserId => 
       traceName("GROUPS - Remove group member") { removeGroupMember(groupId, removedUserId) } 
      } 
      } 
     } ~ 
     path("coverPhoto") { 
      get { 
      traceName("GROUPS - Request a new cover photo upload") { getGroupCoverPhotoUploadUrl(groupId) } 
      } ~ 
      put { 
      traceName("GROUPS - Confirm a cover photo upload") { confirmCoverPhotoUpload(groupId) } 
      } 
     } ~ 
     get { 
      path("attachments"/"new") { 
      traceName("GROUPS - Request attachment upload") { getGroupAttachmentUploadUrl(groupId) } 
      } 
     } 
     } 
    } 
    } 
+0

類型是'inviteUserToGroup' r E打開? 'RequestContext => Unit'? – EdgeCaseBerg

+0

@EdgeCaseBerg'inviteUserToGroup'是類型'(Long,Long)⇒Route' :) –

+0

嗨Adrien,也許你會知道是否該類型的「連接」仍然正確?我遇到這個問題 http://stackoverflow.com/questions/35614708/unexpected-behaviour-on-spray-can-operator-with-http-two-methods-on-the-same-p使用噴霧1.3.3 。 –

1

我試圖從上面的代碼片段,基本格式,並以這種方式工作。

import akka.actor.ActorSystem 
import akka.actor.Props 
import spray.can.Http 
import akka.io.IO 
import akka.actor.ActorRefFactory 
import spray.routing.HttpService 
import akka.actor.Actor 


/** 
* API endpoints 
* 
* Individual APIs are created in traits that are mixed here 
*/ 

trait Api extends ApiService 
with UserAccountsService 
{ 
    val route ={ 
    apiServiceRouting ~ 
    accountsServiceRouting 
    } 

} 

trait ApiService extends HttpService{ 
    val apiServiceRouting={ 
    get{ 
     path("ping") { 
     get { 
     complete { 
      <h1>pong</h1> 
     } 
     } 
     } 
    } 
    } 
} 

trait UserAccountsService extends HttpService{ 
    val accountsServiceRouting={ 
    path("getAdmin") { 
     get { 
     complete { 
      <h1>AdminUserName</h1> 
     } 
     } 
    } 
    } 
} 
class ApiActor extends Actor with Api { 
    override val actorRefFactory: ActorRefFactory = context 

    def receive = runRoute(this.route) 
} 


object MainTest extends App { 

    // we need an ActorSystem to host our application in 
    implicit val system = ActorSystem("UserInformaitonHTTPServer") 

    // the handler actor replies to incoming HttpRequests 
    val handler = system.actorOf(Props[ApiActor], name = "handler") 

    // starting the server 
    IO(Http) ! Http.Bind(handler, interface = "localhost", port = 8080) 

} 
相關問題