2013-02-01 20 views
18

Tôi chưa tìm thấy ví dụ hoặc cấu trúc vững chắc để chia nhỏ các tuyến Spray.io thành nhiều tệp. Tôi thấy rằng cấu trúc hiện tại của các tuyến đường của tôi sẽ trở nên rất cồng kềnh, và sẽ tốt hơn nếu trừu tượng chúng thành các "Bộ điều khiển" khác nhau cho một ứng dụng REST API rất đơn giản.Tuyến đường Spray.io có thể được chia thành nhiều "Bộ điều khiển" không?

Documents dường như không giúp quá nhiều: http://spray.io/documentation/spray-routing/key-concepts/directives/#directives

Dưới đây là những gì tôi có cho đến nay:

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

Nhờ sự giúp đỡ về vấn đề này!

Trả lời

14

Bạn có thể kết hợp các tuyến từ "Bộ điều khiển" khác nhau bằng cách sử dụng ~ bộ phối hợp.

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

Có vẻ như điều đó thật khó. Tôi tự hỏi nếu tôi có thể soạn một số loại tiềm ẩn có thể kết hợp chúng tự động thay vì viết bằng tay service1 ~ service2 ~ service3. Cảm ơn! – crockpotveggies

+0

Hmmm bỏ chọn nó vì có vẻ như nó tạo ra một số loại vấn đề thừa kế. 'các đối số kiểu [com.threetierlogic.AccountServ ice.AccountServiceActor] không phù hợp với tham số kiểu áp dụng của phương thức áp dụng [T <: akka.actor.Actor]' – crockpotveggies

+0

Ok đã thực hiện một số tiến trình với 'trường hợp lớp cơ sở (actorRefFactory: ActorRefFactory) mở rộng HttpService {'Bây giờ vấn đề là các yêu cầu HTTP thất bại vì những điều sau đây:' Không thể gửi HttpResponse như là phản hồi (một phần) cho yêu cầu GET đến '/ ' vì trạng thái phản hồi hiện tại là 'Đã hoàn thành' nhưng phải là 'Chưa hoàn thành'' – crockpotveggies

33

Cá nhân tôi sử dụng này cho các API lớn:

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

tôi sẽ khuyên bạn nên đặt các tuyến đường phổ biến nhất đầu tiên, và sử dụng pathPrefix càng sớm càng tốt trong các tiểu đường, do đó bạn giảm số lần kiểm tra mà Phun chạy cho mỗi yêu cầu đến.

Bạn sẽ tìm thấy bên dưới một con đường mà tôi tin được tối ưu hóa:

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

Điều gì loại 'inviteUserToGroup' r eturn? 'RequestContext => Unit'? – EdgeCaseBerg

+0

@EdgeCaseBerg 'inviteUserToGroup' thuộc loại' (Long, Long) ⇒ Route' :) –

+0

Xin chào Adrien, có thể bạn sẽ biết liệu kiểu kết nối đó có đúng không? Tôi gặp phải vấn đề đó http://stackoverflow.com/questions/35614708/unexpected-behaviour-on-spray-can-operator-with-http-two-methods-on-the-same-p sử dụng phun 1.3.3 . –

1

tôi đã cố gắng theo cách này từ đoạn mã trên, định dạng cơ bản và các công trình.

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) 

} 
Các vấn đề liên quan