1 CHN 05 中间件和过滤器
Nitromelon edited this page 2024-05-11 14:18:19 +08:00
This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

English | 简体中文

中间件(middleware)和过滤器(filter)可以帮助用户提高编程效率在HttpController的例子getInfo方法在返回用户信息之前应该先校验用户是否登录我们把这个逻辑写在getInfo方法里当然是可以的但是很显然校验用户登录属于通用逻辑很多接口都将用到应该把它单独提取出来再配置到调用handler之前这就是filter的作用。

drogon的中间件采用了洋葱圈模型, 框架做完URL路径匹配后会依次调用注册到该路径上的中间件在每个中间件中用户可以选择拦截或放行请求并添加前置、后置处理逻辑。 如果有一个中间件拦截了请求该请求将不会继续深入洋葱圈内层对应的handler也不会被调用但是仍然会通过外层中间件的后置处理逻辑。

过滤器实际上是省略后置操作的中间件,经过进一步的包装后暴露给用户。中间件和过滤器可以在注册路径时混合使用。

内置中间件/过滤器

drogon内置了如下常用过滤器:

  • drogon::IntranetIpFilter:只放行内网ip发来的http请求否则返回404页面
  • drogon::LocalHostFilter:只放行本机127.0.0.1或者::1发来的http请求否则返回404页面

自定义中间件/过滤器

  • 中间件的定义

    用户可以自定义中间件,需要继承HttpMiddleware类模板,模板类型就是子类类型,比如我们想为某些路由开启跨域支持,就可以定义如下:

    class MyMiddleware : public HttpMiddleware<MyMiddleware>
    {
    public:
        MyMiddleware(){};  // do not omit constructor
    
        void invoke(const HttpRequestPtr &req,
                    MiddlewareNextCallback &&nextCb,
                    MiddlewareCallback &&mcb) override
        {
            const std::string &origin = req->getHeader("origin");
            if (origin.find("www.some-evil-place.com") != std::string::npos)
            {
                // intercept directly
                mcb(HttpResponse::newNotFoundResponse(req));
                return;
            }
            // Do something before calling the next middleware
            nextCb([mcb = std::move(mcb)](const HttpResponsePtr &resp) {
                // Do something after the next middleware returns
                resp->addHeader("Access-Control-Allow-Origin", origin);
                resp->addHeader("Access-Control-Allow-Credentials","true");
                mcb(resp);
            });
        }
    };
    

    我们需要重载父类的invoke虚函数实现中间件逻辑;

    这个虚函数有三个参数,分别是:

    • req: http请求
    • nextCb进入内层的回调函数调用该函数意味着继续深入洋葱圈内层调用下一个中间件或最终handler。 调用nextCb时接受另一个函数作为参数, 当从洋葱圈内层返回时该函数会被调用并传入内层返回的HttpResponsePtr。
    • mcb返回上层的回调函数调用该函数意味着返回洋葱圈上层。若跳过nextCb只调用mcb意味着拦截该请求直接返回上层。
  • 过滤器的定义

    用户可以自定义过滤器需要继承HttpFilter类模板模板类型就是子类类型比如我们想做一个LoginFilter就可以定义如下:

    class LoginFilter:public drogon::HttpFilter<LoginFilter>
    {
    public:
        void doFilter(const HttpRequestPtr &req,
                      FilterCallback &&fcb,
                      FilterChainCallback &&fccb) override ;
    };
    

    你可以通过 drogon_ctl 命令创建过滤器, 见 drogon_ctl.

    我们需要重载父类的doFilter虚函数实现过滤器逻辑

    这个虚函数有三个参数,分别是:

    • req: http请求
    • fcb过滤器回调函数函数类型是void (HttpResponsePtr),当过滤器判定请求不合法时,通过这个回调把特定的响应返回给浏览器;
    • fccb过滤器链回调函数函数类型是void()当过滤器判定请求合法时通过这个回调告诉drogon调用下一个过滤器或者最终的handler

    具体的实现可以参考drogon内置过滤器的实现。

  • 中间件/过滤器的注册

    中间件/过滤器总是伴随controller的注册进行前面提到的注册handler的宏(PATH_ADD,METHOD_ADD等)都可以在最后添加一个或多个中间件/过滤器名字比如我们把前面getInfo方法的注册行改为如下形式

    METHOD_ADD(User::getInfo,"/{1}/info?token={2}",Get,"LoginFilter","MyMiddleware");
    

    则在路径匹配成功后必须满足如下条件getInfo方法才会被调用

    1. 请求必须是http get请求
    2. 请求方必须已经登录;

    可以看到,中间件/过滤器的配置和注册是非常简单的注册中间件的controller文件并不需要引用中间件的头文件中间件和控制器也是充分解耦的。

    注意: 如果中间件/过滤器定义在命名空间里,注册时必须把命名空间写全

06 视图