2019/08/14:这篇文章已经过时了。Webcc 已经演化成了一个比较完备的纯 HTTP 程序库。
详见:Webcc: 轻量级 C++ HTTP 程序库
Webcc 是我自己写的一个 C++ Web Service 程序库,网络通信基于 Boost Asio,跨平台,轻量、高效。它并非只是一个玩具,目前正应用于我们实际的项目,而且还在不断更新和完善。
Webcc 同时支持客户端和服务端,你可以用它调用已经存在的 Web Service,也可以用它实现自己的 Web Service,且同时支持 REST 和 SOAP 两种方式。
考虑到用 SOAP 的人越来越少,webcc 对 SOAP 的支持可以通过宏 WEBCC_ENABLE_SOAP
在编译时完全剔除掉。
Webcc 内部对 SOAP 消息的包装和解析依赖于 PugiXml。如果禁掉 SOAP,PugiXml 自然也会一并禁掉;如果启用 SOAP,希望你能承担对 PugiXml 的这一份小小依赖。
对 SOAP 部分的介绍就这么多,具体可见去年的这篇文章:C++ 调用 SOAP Web Service。
REST Server
假定你要提供一个关于图书的 REST Server,提供下面这些操作或接口:
- 查询图书(基于某些特定的条件);
- 添加一本新图书;
- 获取一本图书的详细信息;
- 更新一本图书的信息;
- 删除一本图书。
前面两个操作可以通过继承 webcc::RestListService
来实现:
class BookListService : public webcc::RestListService {
protected:
// 查询图书(基于某些特定的条件)
// GET /books?<query>
bool Get(const webcc::UrlQuery& query,
std::string* response_content) override;
// 添加一本新图书
// POST /books
// |request_content| 包含了新图书的数据,为 JSON 格式的字符串
bool Post(const std::string& request_content,
std::string* response_content) override;
};
其他几个操作,则需要继承自 webcc::RestDetailService
:
class BookDetailService : public webcc::RestDetailService {
protected:
// 获取一本图书的详细信息
bool Get(const std::vector<std::string>& url_sub_matches,
const webcc::UrlQuery& query,
std::string* response_content) override;
// 更新一本图书的信息
bool Put(const std::vector<std::string>& url_sub_matches,
const std::string& request_content,
std::string* response_content) override;
// 删除一本图书
bool Delete(const std::vector<std::string>& url_sub_matches) override;
};
在 Python 的 Django 框架中,有几种不同的视图(View),其中 XxxListView
对应于一列对象,而 XxxDetailView
对应于单个对象。Webcc 的 RestListService
和 RestDetailService
就是参考了 Django 的这一设计。
对于 RestListService
,URL 一般为复数形式,比如 /books
。对于 GET 请求,就是查询对象,URL 一般会带 query,比如 /books?author=Barnes
;对于 POST 请求,就是创建新对象,信息由 HTTP 请求体携带,一般为 JSON 格式。
对于 RestDetailService
,URL 一般为单数,比如 /book
,然后还要指定对象的 ID,最终的 URL 格式为 /book/{BookID}
,这个 ID 最终可以通过 url_sub_matches
这个参数拿到。RestDetailService
支持 GET、PUT、PATCH 和 DELETE 几种请求,分别对应于获取、更新、部分更新和删除。
下面看一下 BookDetailService::Get()
的实现:
bool BookDetailService::Get(const std::vector<std::string>& url_sub_matches,
const webcc::UrlQuery& query,
std::string* response_content) {
if (url_sub_matches.size() != 1) {
return false;
}
const std::string& book_id = url_sub_matches[0];
// 接下来:
// - 通过 book_id 从数据库拿到这个图书,
// - 然后把图书信息转换成 JSON 格式的字符串,
// - 最后把 JSON 字符串赋值给输出参数 response_content。
}
Service 自己并不能运行,需要把它们绑定到 RestServer
,下面是 main()
里的部分代码:
webcc::RestServer server(8080, 2);
server.Bind(std::make_shared<BookListService>(), "/books", false);
server.Bind(std::make_shared<BookDetailService>(), "/book/(\\d+)", true);
server.Run();
先创建一个 RestServer
对象,端口号为 8080
,后台的工作线程数为 2
。
然后分别创建 BookListService
和 BookDetailService
,都用 std::shared_ptr
来自动管理对象生命期。
最后绑定 services 到特定的 URL,其中 BookDetailService
的 URL 是一个正则表达式,(\\d+)
最后匹配下来的部分就存在参数 url_sub_matches
中。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。