HomeiOS DevelopmentHummingbird routing and requests - The.Swift.Dev.

Hummingbird routing and requests – The.Swift.Dev.



Routing on the server aspect means the server goes to ship a response primarily based on the URL path that the shopper known as when firing up the HTTP request. After all the server can verify extra parameters and headers to construct the ultimate response, however once we speak about routing usually, we normally discuss with the trail elements. Hummingbird makes use of a trie-based router, which is a quick and environment friendly manner of trying up routes. It is fairly easy to answer HTTP request utilizing the built-in router, you possibly can merely add your primary route handlers like this:


 
router.on("foo", methodology: .HEAD) { _ -> HTTPResponseStatus in .okay }
router.on("foo", methodology: .GET) { _ -> HTTPResponseStatus in .okay }
router.on("foo", methodology: .POST) { _ -> HTTPResponseStatus in .okay }
router.on("foo", methodology: .PUT) { _ -> HTTPResponseStatus in .okay }
router.on("foo", methodology: .PATCH) { _ -> HTTPResponseStatus in .okay }
router.on("foo", methodology: .DELETE) { _ -> HTTPResponseStatus in .okay }


router.head("foo") { _ -> HTTPResponseStatus in .okay }
router.get("foo") { _ -> HTTPResponseStatus in .okay }
router.put("foo") { _ -> HTTPResponseStatus in .okay }
router.publish("foo") { _ -> HTTPResponseStatus in .okay }
router.patch("foo") { _ -> HTTPResponseStatus in .okay }
router.delete("foo") { _ -> HTTPResponseStatus in .okay }


In Hummingbird it is usually potential to register use a operate as an alternative of a block. Handler features may be async and throwing too, so you possibly can mark the blocks with these key phrases or use asynchronous Swift features when registering route handlers. In case you do not present the primary parameter, the trail as a string, the route handler goes to be connected to the bottom group. 👍


You too can prefix a path part with a colon, it will flip that part right into a dynamic route parameter. The parameter goes to be named after the trail part, by merely dropping the colon prefix. You may entry parameters inside your route handler via the req.parameters property. Additionally it is potential to register a number of elements utilizing a / character.


public extension HBApplication {
    
    func configure() throws {

        router.get { _ async throws in "Howdy, world!" }

        router.get("hiya/:identify") { req throws in
            guard let identify = req.parameters.get("identify") else {
                throw HBHTTPError(
                    .badRequest,
                    message: "Invalid identify parameter."
                )
            }
            return "Howdy, (identify)!"
        }

        let group = router.group("todos")
        group.get(use: checklist)
        group.publish(use: create)
        
        let idGroup = group.group(":todoId")
        idGroup.head(use: verify)
        idGroup.get(use: fetch)
        idGroup.put(use: replace)
        idGroup.patch(use: patch)
        idGroup.delete(use: delete)

        
        router.group("todos")
            .get(use: checklist)
            .publish(use: create)
            .group(":todoId")
                .head(use: verify)
                .get(use: fetch)
                .put(use: replace)
                .patch(use: patch)
                .delete(use: delete)

    }

    func checklist(_ req: HBRequest) async throws -> HTTPResponseStatus { .okay }
    func verify(_ req: HBRequest) async throws -> HTTPResponseStatus { .okay }
    func fetch(_ req: HBRequest) async throws -> HTTPResponseStatus { .okay }
    func create(_ req: HBRequest) async throws -> HTTPResponseStatus { .okay }
    func replace(_ req: HBRequest) async throws -> HTTPResponseStatus { .okay }
    func patch(_ req: HBRequest) async throws -> HTTPResponseStatus { .okay }
    func delete(_ req: HBRequest) async throws -> HTTPResponseStatus { .okay }
}


It’s potential to make use of a wildcard character when detecting path elements, however there is no such thing as a catch-all sample simply but, so if you wish to deal with a number of degree of elements, you must register a route handler like this (e.g. /foo, /foo/bar, /foo/bar/baz will work, however /a/b/c/d will not 😢):


import Hummingbird
import HummingbirdFoundation

public extension HBApplication {
    
    func configure() throws {
        router.get("*", use: catchAll)
        router.get("*/*", use: catchAll)
        router.get("*/*/*", use: catchAll)
    }
    
    func catchAll(_ req: HBRequest) async throws -> String {
        return req.uri.path
    }
}


Additionally it is potential to edit the auto-generated response should you specify the .editResponse possibility.


router.get("foo", choices: .editResponse) { req -> String in
    req.response.standing = .okay
    req.response.headers.replaceOrAdd(
        identify: "Content material-Kind", 
        worth: "utility/json"
    )
    return #"{"foo": "bar"}"#
}


Hummingbird help for physique streaming is superb, you possibly can stream a HTTP request physique by utilizing the .streamBody possibility. The physique stream has a sequence property, which you should utilize to iterate via the incoming ByteBuffer chunks when dealing with the request. 🔄


func configure() throws { 
    router.publish("foo", choices: .streamBody) { req async throws -> String in
        guard
            let rawLength = req.headers["Content-Length"].first,
            let size = Int(rawLength),
            let stream = req.physique.stream
        else {
            throw HBHTTPError(
                .badRequest,
                message: "Lacking or invalid physique stream."
            )
        }
        var depend: Int = 0
        for strive await chunk in stream.sequence {
            depend += chunk.readableBytes
        }
        return String("(size) / (depend)")
    }
}


let app = HBApplication(
    configuration: .init(
        tackle: .hostname(hostname, port: port),
        serverName: "Hummingbird",
        maxUploadSize: 1 * 1024 * 1024 * 1024 
    )
)


As you possibly can see you possibly can simply entry all of the incoming headers by way of the req.headers container, you must word that this methodology will return header values in a case-insensitive manner. If you wish to stream bigger recordsdata, you additionally should set a customized maxUploadSize utilizing the configuration object when initializing the HBApplication occasion.


curl -X POST http://localhost:8080/foo 
    -H "Content material-Size: 3" 
    --data-raw 'foo'

curl -X POST http://localhost:8080/foo 
    -H "content-Size: 5242880" 
    -T ~/check


You may check out streaming with a easy cURL script, be happy to experiment with these.


One other factor I might like to point out you is the way to entry question parameters and different properties utilizing the request object. Right here is an all-in-one instance, which you should utilize as a cheatsheet… 😉



router.get("bar") { req async throws -> String in
            
    struct Foo: Codable {
        var a: String
    }

    print(req.methodology)
    print(req.headers)
    print(req.headers["accept"])
    print(req.uri.queryParameters.get("q") ?? "n/a")
    print(req.uri.queryParameters.get("key", as: Int.self) ?? 0)

    if let buffer = req.physique.buffer {
        let foo = strive? JSONDecoder().decode(Foo.self, from: buffer)
        print(foo ?? "n/a")
    }
    return "Howdy, world!"
}


Anyway, there may be one extra tremendous cool function in Hummingbird that I might like to point out you. It’s potential to outline a route handler, this manner you possibly can encapsulate all the pieces right into a single object. There’s an async model of the route handler protocol, should you do not want async, you possibly can merely drop the key phrase each from the protocol identify & the tactic. I like this method lots. 😍


struct MyRouteHandler: HBAsyncRouteHandler {

    struct Enter: Decodable {
        let foo: String
    }

    struct Output: HBResponseEncodable {
        let id: String
        let foo: String
    }
    
    let enter: Enter

    init(from request: HBRequest) throws {
        self.enter = strive request.decode(as: Enter.self)
    }

    func deal with(request: HBRequest) async throws -> Output {
        .init(
            id: "id-1",
            foo: enter.foo
        )
    }
}



The request.decode methodology makes use of the built-in decoder, which you must explicitly set for the applying, since we will talk utilizing JSON knowledge, we will use the JSON encoder / decoder from Basis to mechanically remodel the information.


As a way to make use of the customized route handler, you possibly can merely register the thing kind.


import Hummingbird
import HummingbirdFoundation

public extension HBApplication {

    func configure() throws {
        
        encoder = JSONEncoder()
        decoder = JSONDecoder()
                
        
        router.publish("foo", use: MyRouteHandler.self)
    }
}


You may learn extra about how the encoding and decoding works in Hummingbird, however perhaps that subject deserves its personal weblog publish. You probably have questions or recommendations, be happy to contact me. 🙈



RELATED ARTICLES

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Most Popular

Recent Comments