salvo-rs/salvo

Feature: Consider supporting handler macro for structures' methods

wildonion opened this issue · 3 comments

I hope you guys are doing well, thanks for this great fremework, the routing system is a great feature simplifies handling api logics a lot, however I have couple of ideas in the mind want to share with you, wondering if we could implement them in future versions. one of them is writing structure based conrollers, currently as I'm designing the apis there is this pattern of #[handler] that can be added to the impl of structure but there is a lock of supporting of adding it on top of structure methods like if we could implement the following logic or similar to that would be a great option:

struct HelloController;

#[handler]
impl HelloController{
    
    async fn check_health(req: &mut Request, res: &mut Response){ // GET probably!

    }

    async fn send_hello(req: &mut Request, res: &mut Response){ // POST probably!

    }

    async fn take_back_health(req: &mut Request, res: &mut Response){ // DELETE 

    }
}

or

#[probably_a_controller_registration_macro]
impl HelloController{

    #[handler]
    async fn check_health(req: &mut Request, res: &mut Response){ // GET probably!

    }

    #[handler]
    async fn send_hello(req: &mut Request, res: &mut Response){ // POST probably!

    }

    #[handler]
    async fn take_back_hello(req: &mut Request, res: &mut Response){ // DELETE 

    }
}

and the registeration would be something like passing the structure to a method like register():

let hello_router = Router::with_path("/v1/hello/").register(HelloController);
let user_router = Router::with_path("/v1/user/").register(UserController);
let apis = Router::with_path("/api").push(hello_router).push(user_router);

actually this pattern helps a lot in designing api and adds more flexibility to the current router system, in fact

Although there are concepts similar to Controller in many web frameworks, I don't think it's necessary to introduce such a concept.

  • I think Controller is just doing the same function as namespace in most cases. There's no need to introduce two concepts for one thing.

  • This design seems to save some code in some cases, but in fact, it makes things more complicated. For example, what if the function name is inconsistent with the route path to be registered? How to add middleware? It makes things inflexible and introduces more complex problems while trying to simplify the code.

@chrislearn
thanks for your response, yes you're right although there are solutions to forementioned problems like if we could add a custom attribute like #[handler(path="/user/get/<id>/")] and extend the method name in macro handler, the inconsistency problem can be solved or for the middleware issue, a proc macro like #[hoop] or #[middleware] can be used for that then when we're registering the controller the parser will separate each of them to push them into the main router to build the router tree, but as you've mentioned indeed more complex logics behind the framework needs to get handled.

As mentioned above, the fact that you want to create multiple complex macros requires a learning cost and no flexibility, but, what benefits do you get? Nothing.