2013-02-01 12 views
18

Nie znalazłem solidnego przykładu lub struktury do podziału tras Spray.io na wiele plików. Zauważyłem, że obecna struktura moich tras stanie się bardzo kłopotliwa i byłoby miło rozrysować je na różne "Kontrolery" dla bardzo prostej aplikacji REST API.Czy trasy Spray.io można podzielić na wiele "Kontrolerów"?

Docs nie wydają się zbyt wiele pomóc: http://spray.io/documentation/spray-routing/key-concepts/directives/#directives

Oto co mam do tej pory:

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

Dzięki za pomoc w tej sprawie!

Odpowiedz

14

Możesz łączyć trasy z różnych "Kontrolerów" za pomocą ~ kombinatora.

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

Wygląda na to, że to wystarczy. Zastanawiam się, czy mogę skomponować coś w rodzaju niejawnego, które może łączyć je automatycznie zamiast ręcznie pisać service1 ~ service2 ~ service3. Dzięki! – crockpotveggies

+0

Hmmm odznaczono go, ponieważ wygląda na to, że stwarza jakiś problem związany z dziedziczeniem. 'argumenty typu [com.threetlogic.AccountServ ice.AccountServiceActor] nie są zgodne z kryteriami dotyczącymi typu metody zastosowania [T <: akka.actor.Actor]' – crockpotveggies

+0

Ok poczyniła pewne postępy z 'klasą przypadku Base (actorRefFactory: ActorRefFactory) extends HttpService {'Problemem są teraz żądania HTTP z powodu następujących:' Nie można wysłać HttpResponse jako odpowiedzi (części) dla żądania GET do '/ ', ponieważ bieżącym stanem odpowiedzi jest "Completed", ale powinno być "Uncompleted'' – crockpotveggies

33

Osobiście korzystam z tego dla dużych 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 
    } 
} 

Polecam oddanie najczęstsze trasy pierwszy i używać pathPrefix tak szybko, jak można w podrzędnych dróg, dzięki czemu można zmniejszyć liczba testów, które Spray uruchamia dla każdego przychodzącego żądania.

Musisz znaleźć poniżej trasie, która moim zdaniem jest zoptymalizowany:

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

Co type to 'inviteUserToGroup' r eturn? 'RequestContext => Unit'? – EdgeCaseBerg

+0

@EdgeCaseBerg 'inviteUserToGroup' jest typu' (Long, Long) ⇒ Route' :) –

+0

Hi Adrien, może będziesz wiedzieć, czy ten typ "konkatenacji" jest nadal poprawny? Napotykam w tym problemie http://stackoverflow.com/questions/35614708/unexpected-behaviour-on-spray-can-operator-with-http-two-methods-on-the-same-p using spray 1.3.3 . –

1

Próbowałem w ten sposób z powyższym fragmencie kodu, formatu podstawowego oraz robót.

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) 

} 
Powiązane problemy