前言
EOSIO对外提供了可以访问和操作的接口,调用如下
http://123.207.175.74:8888/v1/chain/get_info
成功返回如下结果
{
"server_version": "134df9f6",
"chain_id": "d0505f66cb656524ccdbd3c6ce1b23a126b378f5d7eb8ee7246c1cc38db6730b",
"head_block_num": 15139606,
"last_irreversible_block_num": 15139605,
"last_irreversible_block_id": "00e70315b2bfd5cf47c0759ea7fbbe7d61b96116b5848ff6f7dd72cdf0e1cb87"
}
EOSIO所有的API列表可以参考如下链接:EOSIO远程HTTP接口
接口API分类
在EOSIO中,链相关接口按照功能分类(同时更可以参考代码结构),可以将接口简单分为以下几类(只简单说明最常用最核心的API)
链接口(chain_api)
- v1/chain/get_info:返回链最基本的信息,如链ID,块高,生产者等信息
- v1/chain/get_block:根据块号,或者块信息获取这个块的详细信息
- v1/chain/get_account:根据账户名,返回此账户信息
- v1/chain/get_code:获取某个账户下的合约内容
- v1/chain/get_code_hash:获取某个账户下的合约hash,用作比较
- v1/chain/get_abi:获取账户下合约关联的API
- v1/chain/get_table_rows:查询表信息
- v1/chain/get_currency_balance:获取代币余额
- v1/chain/get_currency_stats:获取所有代币统计信息
- v1/chain/get_producers:获取生产者信息
- v1/chain/abi_json_to_bin:利用ABI将一段json数据转换成二进制数据,一般用作action参数序列化
- v1/chain/abi_bin_to_json:利用ABI将一段二进制数据转换成json格式的数据
- v1/chain/get_required_keys:鉴权相关,返回可用的第一个公钥key,用于接下来的签名操作
- v1/chain/push_transaction:发送交易
- v1/chain/send_transaction:发送交易
生产接口(producer_api)
- v1/producer/pause:暂停生产
- v1/producer/resume:激活生产
- v1/producer/paused:返回链是否属于暂停状态
- v1/producer/create_snapshot:创建数据快照
网络接口(net_api)
- v1/net/connect:链接P2P到另外一个节点
- v1/net/disconnect:断一个链接
- v1/net/status:返回当前节点的网络链接状态
- v1/net/connections:返货当前节点所有的链接信息
数据接口(db_size_api)
- v1/db_size/get:返回当前节点的chainbase数据库的使用情况,主要用于运营
接口API的注册
在EOSIO中,所有的对外的http接口,都是通过依托http_plugin插件来实现的,http_plugin封装了http调用的相关操作,如接口注册,调用处理,结果返回等等
如何注册一个API
auto& _http_plugin = app().get_plugin<http_plugin>();
ro_api.set_shorten_abi_errors( !_http_plugin.verbose_errors() );
_http_plugin.add_api({
CHAIN_RO_CALL(get_info, 200, http_params_types::no_params_required)}, appbase::priority::medium_high);
调用http_plugin类的add_api方法,向插件注册了一个和url绑定的回调函数,最终保存在变量url_handlers中,函数形势如下:
using url_handler = std::function<void(string,string,url_response_callback)>;
Http API 请求处理
首先在插件启动的时候,会做如下的操作与处理
my->thread_pool.emplace( "http", my->thread_pool_size );
if(my->listen_endpoint) {
try {
my->create_server_for_endpoint(*my->listen_endpoint, my->server);
fc_ilog( logger, "start listening for http requests" );
my->server.listen(*my->listen_endpoint);
my->server.start_accept();
} catch ( ... ){
...
}
}
- 创建工作线程池thread_pool,用以处理最终的api业务信息
- 创建server服务
- 启动端口监听,开始接受外部请求
接下来核心函数的处理流程如下
// 创建server服务,并且设置相关的参数,最核心的设置请求处理函数handle_http_request
template<class T>
void create_server_for_endpoint(const tcp::endpoint& ep, websocketpp::server<detail::asio_with_stub_log<T>>& ws) {
try {
ws.clear_access_channels(websocketpp::log::alevel::all);
ws.init_asio( &thread_pool->get_executor() );
ws.set_reuse_addr(true);
ws.set_max_http_body_size(max_body_size);
// captures `this` & ws, my needs to live as long as server is handling requests
ws.set_http_handler([&](connection_hdl hdl) {
handle_http_request<detail::asio_with_stub_log<T>>(ws.get_con_from_hdl(hdl));
});
} catch ( ... ){
...
}
}
// 请求处理
void handle_http_request(detail::connection_ptr<T> con) {
try {
auto& req = con->get_request();
if(!allow_host<T>(req, con))
return;
// 解析http请求头
con->defer_http_response();
// 验证请求合法性
auto abstract_conn_ptr = make_abstract_conn_ptr<T>(con, shared_from_this());
if( !verify_max_bytes_in_flight( con ) || !verify_max_requests_in_flight( con ) ) return;
// 调用注册业务请求处理回调函数,响应API
std::string resource = con->get_uri()->get_resource();
auto handler_itr = url_handlers.find( resource );
if( handler_itr != url_handlers.end()) {
std::string body = con->get_request_body();
handler_itr->second( abstract_conn_ptr, std::move( resource ), std::move( body ), make_http_response_handler<T>(abstract_conn_ptr) );
} else {
fc_dlog( logger, "404 - not found: ${ep}", ("ep", resource) );
error_results results{
websocketpp::http::status_code::not_found,
"Not Found", error_results::error_info(fc::exception( FC_LOG_MESSAGE( error, "Unknown Endpoint" )), verbose_http_errors )};
con->set_body( fc::json::to_string( results, fc::time_point::now() + max_response_time ));
con->set_status( websocketpp::http::status_code::not_found );
con->send_http_response();
}
} catch( ... ) {
handle_exception<T>( con );
}
}
最后从上面的代码中,我们可以具体的出以下结论
- http_plugin插件只是提供了一个http请求处理的基础框架,具体的业务逻辑,依赖上层注册进来回调函数
- 如果找不到处理的API请求,自动返回404错误
- 如果在处理中发生异常,则调用handle_exception函数进行处理
总结
- EOSIO中http请求的处理还是比较简单的,基础逻辑在插件中处理,记住最核心的url_handler类型
- 如果要处理请求头相关的信息,只要修改http_plugin相关代码就可以
- 在EOSIO代码中还有其他API处理,但都不是链相关的,剩下的主要是钱包,历史状态相关接口
- 在链相关的接口中,chain_api是最基本的,如果是个单节点,并且业务量很小,只启动这个接口类就可以