Hummingbird routing and requests - The.Swift.Dev. - Slsolutech Best IT Related Website google.com, pub-5682244022170090, DIRECT, f08c47fec0942fa0

Hummingbird routing and requests – The.Swift.Dev.

Spread the love



Routing on the server aspect means the server goes to ship a response primarily based on the URL path that the consumer referred to as when firing up the HTTP request. After all the server can test further parameters and headers to construct the ultimate response, however once we speak about routing usually, we normally confer with the trail parts. Hummingbird makes use of a trie-based router, which is a quick and environment friendly means of wanting up routes. It is fairly easy to reply to HTTP request utilizing the built-in router, you possibly can merely add your fundamental route handlers like this:


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


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


In Hummingbird additionally it is doable to register use a perform as a substitute of a block. Handler capabilities may be async and throwing too, so you possibly can mark the blocks with these key phrases or use asynchronous Swift capabilities when registering route handlers. In the event you do not present the primary parameter, the trail as a string, the route handler goes to be hooked up to the bottom group. 👍


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


public extension HBApplication {
    
    func configure() throws {

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

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

        let group = router.group("todos")
        group.get(use: record)
        group.submit(use: create)
        
        let idGroup = group.group(":todoId")
        idGroup.head(use: test)
        idGroup.get(use: fetch)
        idGroup.put(use: replace)
        idGroup.patch(use: patch)
        idGroup.delete(use: delete)

        
        router.group("todos")
            .get(use: record)
            .submit(use: create)
            .group(":todoId")
                .head(use: test)
                .get(use: fetch)
                .put(use: replace)
                .patch(use: patch)
                .delete(use: delete)

    }

    func record(_ req: HBRequest) async throws -> HTTPResponseStatus { .okay }
    func test(_ 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 doable to make use of a wildcard character (*) when detecting path parts and the recursive model (**) to catch every little thing. Additionally you should utilize the ${identify} syntax to catch a named request parameter even with a prefix or suffix, however you possibly can’t insert this in the midst of a path element. (e.g. "prefix-${identify}.jpg" will not work, however "${identify}.jpg" is simply tremendous) 💡


import Hummingbird
import HummingbirdFoundation

extension HBApplication {

    func configure(_ args: AppArguments) throws {

        router.get("foo-${identify}", use: catchPrefix)
        router.get("${identify}.jpg", use: catchSuffix)
        
        router.get("*", use: catchOne)
        router.get("*/*", use: catchTwo)

        router.get("**", use: catchAll)
        
    }
    
    
    func catchOne(_ req: HBRequest) async throws -> String {
        "one"
    }

    
    func catchTwo(_ req: HBRequest) async throws -> String {
        "two"
    }
    
    
    func catchAll(_ req: HBRequest) async throws -> String {
        "all: " + req.parameters.getCatchAll().joined(separator: ", ")
    }
    
    
    func catchPrefix(_ req: HBRequest) async throws -> String {
        "prefix: " + (req.parameters.get("identify") ?? "n/a")
    }
    
    
    func catchSuffix(_ req: HBRequest) async throws -> String {
        "suffix: " + (req.parameters.get("identify") ?? "n/a")
    }
}


It is usually doable to edit the auto-generated response when you specify the .editResponse possibility.


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


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


func configure() throws { 
    router.submit("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 rely: Int = 0
        for attempt await chunk in stream.sequence {
            rely += chunk.readableBytes
        }
        return String("(size) / (rely)")
    }
}


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 through the req.headers container, you must observe that this technique will return header values in a case-insensitive means. If you wish to stream bigger information, you additionally need to 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 at liberty 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.technique)
    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 = attempt? JSONDecoder().decode(Foo.self, from: buffer)
        print(foo ?? "n/a")
    }
    return "Good day, world!"
}


Anyway, there may be one further tremendous cool characteristic in Hummingbird that I might like to point out you. It’s doable to outline a route handler, this manner you possibly can encapsulate every little thing right into a single object. There’s an async model of the route handler protocol, when you do not want async, you possibly can merely drop the key phrase each from the protocol identify & the tactic. I like this method quite a bit. 😍


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 = attempt request.decode(as: Enter.self)
    }

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



The request.decode technique makes use of the built-in decoder, which you need to explicitly set for the applying, since we will talk utilizing JSON information, we are able to use the JSON encoder / decoder from Basis to mechanically rework the information.


With a purpose to make use of the customized route handler, you possibly can merely register the article kind.


import Hummingbird
import HummingbirdFoundation

public extension HBApplication {

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


You may learn extra about how the encoding and decoding works in Hummingbird, however perhaps that matter deserves its personal weblog submit. When you have questions or solutions, be at liberty to contact me. 🙈



Leave a Reply

Your email address will not be published. Required fields are marked *