Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multiple Handlers #51

Open
gotev opened this issue Dec 9, 2022 · 1 comment
Open

Multiple Handlers #51

gotev opened this issue Dec 9, 2022 · 1 comment

Comments

@gotev
Copy link

gotev commented Dec 9, 2022

When multiple endpoints are defined, one ends up with:

await server.appendRoute(HTTPRoute(.GET, "/cat"), to: MyCatGetHandler())
await server.appendRoute(HTTPRoute(.GET, "/dog"), to: MyDogGetHandler())
await server.appendRoute(HTTPRoute(.GET, "/fish"), to: MyFishGetHandler())

and then implementations contains only the handler:

struct MyCatGetHandler : HTTPHandler {
    func handleRequest(_ request: FlyingFox.HTTPRequest) async throws -> FlyingFox.HTTPResponse { }
}

struct MyDogGetHandler : HTTPHandler {
    func handleRequest(_ request: FlyingFox.HTTPRequest) async throws -> FlyingFox.HTTPResponse { }
}

struct MyFishGetHandler : HTTPHandler {
    func handleRequest(_ request: FlyingFox.HTTPRequest) async throws -> FlyingFox.HTTPResponse { }
}

Maybe it's my personal taste, but I find it better to have both the route and the handler in the same file, so by adding those extensions:

protocol RouteHandler : HTTPHandler {
    func route() -> HTTPRoute
}

extension HTTPServer {
    func appendRoutes(_ routeHandlers: RouteHandler...) {
        routeHandlers.forEach {
            appendRoute($0.route(), to: $0)
        }
    }
}

I'm able to write each handler like this (which I find convenient, specially when the route has placeholders, so I can write a single common function and define shared constants between the route and the request handler):

struct MyCatGetHandler : RouteHandler {

    func route() -> HTTPRoute {
        HTTPRoute(method: .GET, path: "/cat")
    }

    func handleRequest(_ request: FlyingFox.HTTPRequest) async throws -> FlyingFox.HTTPResponse {
        // implementation
    }
}

and also be able to add them all like this:

await server.appendRoutes(
    MyCatGetHandler(),
    MyDogGetHandler(),
    MyFishGetHandler()
)

If you think it's a valuable addition, feel free to add it in the library 🍻

This is my third proposal so far (#50 #49), and the fact I was able to write everything I needed as an extension denotes really good design choices on your side, so kudos for the great work! 💯

@swhitty
Copy link
Owner

swhitty commented Oct 29, 2023

Hi @gotev I've been thinking a lot about your suggestion think there is a nice declarative solution SE-0389 Attached Macros available in Swift 5.9 and later.

I'm interested in your feedback, the implementation is currently available on the branch preview/macro where handlers can annotate functions with routes:

@HTTPHandler
struct MyHandler {

  @HTTPRoute("/cat")
  func getCat(_ request: HTTPRequest) -> HTTPResponse { ... }

  @HTTPRoute("/dog")
  func getDog(_ request: HTTPRequest) -> HTTPResponse { ... }

  @HTTPRoute("/fish")
  func getFish(_ request: HTTPRequest) -> HTTPResponse { ... }
}

let server = HTTPServer(port: 80, handler: MyHandler())
try await server.start()

The macro synthesises conformance to HTTPHandler delegating handling to the first matching route.
Read more here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants