使用Flare从0开始搭建一个HTTP聊天室-1

640 阅读3分钟

这是我参与8月更文挑战的第13天,活动详情查看:8月更文挑战

今天开始带大家使用Flare框架的,从0开始搭建一个HTTP服务的聊天室。

今天首先试着用flare的http框架,搭建起用户登陆认证的接口。

flare运行环境

flare作为一个fiber微线程框架,首先需要启动整个框架的运行环境,将后台的微线程管理等等线程启动起来,因此main函数中要通过如下的形式启动flare运行环境。

int main(int argc, char** argv) {
  return flare::Start(argc, argv, chatroom::Entry);
}

HTTP服务

flare框架提供了一整套的RPC框架,并支持基于Protobuf和HTTP的通信协议,flare中对HTTP的请求的处理通过HttpHandler完成。HttpHandler可以关联至某个URI上,后续框架会将相应的请求转发给这一HttpHandler

int Entry(int argc, char** argv) {
  flare::Server server;
  server.AddProtocol("http");
  
	server.AddHttpFilter(std::make_unique<LoginFilter>(flare::detail::UriMatcher(std::regex("^/user(Login([/\?#](.*?))?)?$"))));
  
	server.AddHttpHandler(std::regex("^/user(/.+)?$"), std::make_unique<UserHandler>());
  server.AddHttpPrefixHandler("/userLogin", std::make_unique<UserLoginHandler>());

  server.ListenOn(flare::EndpointFromIpv4("0.0.0.0", 8080));
  FLARE_CHECK(server.Start());

  while (!flare::CheckForQuitSignal()) {
    flare::this_fiber::SleepFor(1s);
  }
  server.Stop();
  server.Join();
  return 0;
}

上面这段程序中

  1. 创建了一个flare Server对象
  2. 通过AddProtocol方法设定通信协议是HTTP
  3. AddHttpFilter可以为Http服务添加过滤器
  4. AddHttpHandlerAddHttpPrefixHandler的配置,将对应的URI请求转发给绑定的Handler中
  5. ListenOn方法设定HTTP开启的IP和端口
  6. server.Start()方法启动HTTP服务
  7. 通过flare::CheckForQuitSignal()的方法循环检查程序是否收到退出的信号(ctl+c)
  8. Stop()、Join()等待框架完全停止结束

Handler

下面介绍一下用户登陆的Handler如何实现用户的登陆。首先看一下我们要实现的用户登陆的接口

/userLogin:
    get:
      tags:
        - user
      summary: Logs user into the system
      operationId: loginUser
      parameters:
        - name: username
          in: query
          description: The user name for login
          required: true
          schema:
            type: string
        - name: password
          in: query
          description: The password for login in clear text
          required: true
          schema:
            type: string
      responses:
        '200':
          description: token
        '400':
          description: Invalid username or password.

参数传入用户名和密码,验证成功之后,返回JWT

具体实现如下:


class UserLoginHandler : public flare::HttpHandler {
 public:
  void OnGet(const flare::HttpRequest& request, flare::HttpResponse* response,
             flare::HttpServerContext* context) override;
};

void UserLoginHandler::OnGet(const flare::HttpRequest& request,
                             flare::HttpResponse* response,
                             flare::HttpServerContext* context) {
  ParamParse param_parse(request.uri());
  auto username = param_parse.Get("username");
  auto password = param_parse.Get("password");
  if (!username || !password ||
      !UserManager::Instance()->Check(*username, *password)) {
    response->set_status(flare::HttpStatus::BadRequest);
    return;
  }
  auto token = TokenUtil::Instance()->GenerateToken(*username);
  response->set_body(token);
  response->set_status(flare::HttpStatus::OK);
}

Handler接口提供了OnGet、OnPost、OnPut、OnDelete等方法,我们在实现的时候按需实现就好,这里用户登陆使用的是HTTP的GET方法,因此只需要实现一个OnGet方法。

ParamParse是我自己通过正则表达式实现的一个Get方法参数解析器,将参数从uri中抽取出来,检查正确性之后,使用JWT生成工具生成一个属于这个用户的token,并返回,JWT生成方式可以参考之前的jwt-cpp的用法介绍

ParamParse

ParamParse类中提供了路径参数和get参数两种解析方式:

class ParamParse {
 public:
  using PathParam = std::vector<std::pair<std::string, std::optional<std::string>>>;
  using PathParamMap = std::unordered_map<std::string, std::optional<std::string>>;

  ParamParse(const std::string& uri) : _uri(uri) {}
  std::optional<std::string> Get(const std::string& key);
  /**
   * eg. /room/{roomId}/enter
   **/ 
  PathParamMap GetPathParam(const std::string& path);

 private:
  const std::string& _uri;
};

c++的正则表达式其实用起来挺繁琐的,返回值都包在了一个smatch里,在取结果的时候需要熟悉regex库返回值的结构,有空的时候可以详细介绍一下c++的正则表达式库。

小结

今天介绍了如何使用flare搭建一套HTTP服务,实现了用户登陆,但是flare内置的HTTP服务支持较少,因此我自己稍微写了一些HTTP相关的参数解析的程序,这里也展示给大家,将来为大家详细介绍一下ParamParse这个通过正则表达式抽取HTTP参数的短小的类。下面几天,我会带领大家尝试完成用户未登陆的拦截,用户注册等接口,为大家展示flare的HTTP框架及其提供的一些基础库。