33. 服务器管理Part 1: 添加RESTful APIs

iFun引擎提供了可以在game server内轻松实现RESTful API的功能。 使用该功能,可通过HTTP协议轻松与不同外部系统进行联动。

33.1. 注册REST API URL和处理器

可运用以下函数注册URL、URL Pattern,以及对其进行处理的处理器。

static void ApiService::RegisterHandler(const http::Method &method,
                                        const boost::regex &path_pattern,
                                        const Handler &handler)

static void ApiService::RegisterHandler2(const http::Method &method,
                                         const boost::regex &path_pattern,
                                         const Handler2 &handler)

static void ApiService::RegisterHandler3 (const http::Method &method,
                                          const boost::regex &path_pattern,
                                          const Handler3 &handler)
public static void ApiService.RegisterHandler (http.Method method,
                                               string path,
                                               Handler handler)
public static void ApiService.RegisterHandler (http.Method method,
                                               string path,
                                               AsyncHandler handler)

public static void ApiService.RegisterHandler (http.Method method,
                                               Regex path_pattern,
                                               HandlerForRegexPath handler)
public static void ApiService.RegisterHandler (http.Method method,
                                               Regex path_pattern,
                                               AsyncHandlerForRegexPath handler)
  • method: http::kGet, http::kPut, http::kPost, http::kDelete, http::kHead.

  • path: 要处理的URL字符串。指定某个特定URL时使用。

  • path_pattern: 要处理的URL pattern。以 (?<NAME>pattern) 的形式将parameter包含在URL中,可在代码中通过 NAME 读取。(参照以下示例)

  • handler: 处理相应URL的handler函数。

typedef boost::function<
    void(const Ptr<http::Response> &)>
         ResponseWriter ASSERT_NO_ROLLBACK;

typedef boost::function<
    void(Ptr<http::Response> ret,
         const http::Request &request,
         const MatchResult &params)> Handler;

typedef boost::function<
    void(Ptr<http::Response> ret,
         const http::Request2 &request,
         const MatchResult &params)> Handler2;

typedef boost::function<
    void(const http::Request2 &request,
         const MatchResult &params,
         const ResponseWriter &finisher)> Handler3;
public delegate void ResponseWriter(http.Response response);

public delegate http.Response Handler (http.Request request);
public delegate void AsyncHandler (http.Request request,
                                   ResponseWriter response_writer);

public delegate http.Response HandlerForRegexPath (http.Request request,
                                                   MatchCollection matches);
public delegate void AsyncHandlerForRegexPath (http.Request request,
                                               MatchCollection matches,
                                               ResponseWriter response_writer);

Important

만약 HTTP 응답을 바로 보내지 않고 비동기적으로 처리해야 한다면 (다른 처리를 비동기로 진행한 후에 HTTP 응답을 보내고 싶다면) 아래 함수로 핸들러를 등록하시면 비동기로 처리 할 수 있습니다.

static void ApiService::RegisterHandler3 (const http::Method &method,
                                          const boost::regex &path_pattern,
                                          const Handler3 &handler)
public static void ApiService.RegisterHandler (http.Method method,
                                               string path,
                                               AsyncHandler handler)

public static void ApiService.RegisterHandler (http.Method method,
                                               Regex path_pattern,
                                               AsyncHandlerForRegexPath handler)

33.2. 编写处理器

handler函数可按如下所示编写。

  • 通过HTTP GET传过来的参数中不允许有重复的key时

    void MyHandler(
      Ptr<http::Response> response,
      const http::Request &request,
      const ApiService::MatchResult &params) {
    
      response->status_code = http::kOk;
      response->body = ...
    }
    
  • 通过HTTP GET传过来的参数中允许有重复的key时

    void MyHandler2(
      Ptr<http::Response> response,
      const http::Request2 &request,
      const ApiService::MatchResult &params) {
    
      response->status_code = http::kOk;
      response->body = ...
    }
    
  • 非同步生成Response body时(HTTP GET的参数允许重复的key)

    void MyHandler3(
      const http::Request2 &request,
      const ApiService::MatchResult &params,
      const ApiService::ResponseWriter &finisher) {
    
      some_async_func(finisher);
    }
    
    void some_async_func(const ApiService::ResponseWriter &finisher) {
      Ptr<http::Response> response(new http::Response);
      finisher(response);
    }
    
  • 同步处理单一URL

    funapi.http.Response MySyncHandler1(funapi.http.Request request)
    {
      funapi.http.Response response = new funapi.http.Response ();
      response.status_code = funapi.http.StatusCode.kOk;
      response.body = ...
      return response;
    }
    
  • URL已通过pattern指定时,同步处理

    funapi.http.Response MySyncHandler2(funapi.http.Request request, MatchCollection matches)
    {
      funapi.http.Response response = new funapi.http.Response ();
      response.status_code = funapi.http.StatusCode.kOk;
      response.body = ...
      return response;
    }
    
  • 非同步处理单一URL

    void MyAsyncHandler1(funapi.http.Request request, ResponseWriter finisher)
    {
      some_async_func(finisher)
    }
    
    void some_async_func(ResponseWriter finisher)
    {
      funapi.http.Response response = new funapi.http.Response ();
      response.status_code = funapi.http.StatusCode.kOk;
      response.body = ...
      finisher(response);
    }
    
  • URL已通过pattern指定时,非同步处理

    void MyAsyncHandler2(funapi.http.Request request, MatchCollection matches, ResponseWriter finisher)
    {
      some_async_func(finisher)
    }
    
    void some_async_func(ResponseWriter finisher)
    {
      funapi.http.Response response = new funapi.http.Response ();
      response.status_code = funapi.http.StatusCode.kOk;
      response.body = ...
      finisher(response);
    }
    

33.3. API Service注册示例

下面是注册如下所示的2个API的示例。

GET /example/hello
GET /example/hello2/*/*
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// request: http://localhost:8014/example/hello
// response: hello!
void OnHello(Ptr<http::Response> response,
    const http::Request &request,
    const ApiService::MatchResult &params) {
  response->status_code = http::kOk;
  response->header.insert(std::make_pair("Content-Type", "text/plain"));
  response->body = "hello!";
}


// request: http://localhost:8014/example/hello2/bob/24
// response:
// {
//   "name": "bob",
//   "age": "24"
// }
void OnHello2(Ptr<http::Response> response,
    const http::Request &request,
    const ApiService::MatchResult &params) {
  Json msg;
  msg["name"] = params["name"];
  msg["age"] = params["age"];
  response->status_code = http::kOk;
  response->header.insert(std::make_pair("Content-Type", "application/json"));
  response->body = msg.ToString();
}


static bool Install(const ArgumentMap &arguments) {
  ...
  ApiService::RegisterHandler(
      http::kGet, boost::regex("/example/hello"), OnHello);

  ApiService::RegisterHandler(http::kGet,
      boost::regex("/example/hello2/(?<name>\\w+)/(?<age>\\w+)/"), OnHello2);
  ...
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
using System.Text.RegularExpressions;
using System.Collections.Specialized;
using funapi;

// request: http://localhost:8014/example/hello
// response: hello!
public static funapi.http.Response OnHello(funapi.http.Request request)
{
  funapi.http.Response response = new funapi.http.Response ();
  response.status_code = funapi.http.StatusCode.kOk;
  response.body = "hello"; // response body
  return response;
}


// request: http://localhost:8014/example/hello2/bob/24
// response:
// {
//   "name": "bob",
//   "age": "24"
// }
public static funapi.http.Response OnHello2(
    funapi.http.Request request,
    MatchCollection collection)
{
  funapi.http.Response response = new funapi.http.Response ();
  response.header = new NameValueCollection ();
  response.header.Add ("Content-Type", "application/json");
  response.status_code = funapi.http.StatusCode.kOk;

  JObject msg = new JObject ();
  GroupCollection groups = collection [0]. Groups;
  msg ["name"] = groups ["name"].Value;
  msg ["age"] = groups ["age"].Value;
  response.body = msg.ToString ();
  return response;
}


public static void Install(ArgumentMap arguments)
{
  ...
  ApiService.RegisterHandler (
      funapi.http.Method.kGet, "/example/hello", OnHello);

  ApiService.RegisterHandler (
      funapi.http.Method.kGet,
      new Regex("/example/hello2/(?<name>\\w+)/(?<age>\\w+)/"),
      OnHello2);
  ...
}

33.4. ApiService功能设置参数

  • api_service_port: 用于运行管理专用RESTful API服务的本地TCP端口编号。(type=uint64, default=8015)

  • api_service_logging_level: 管理专用RESTful API消息的日志级别。0为不保存日志。1为仅在出错时保存。2为对所有请求保存日志(type=uint64, default=2)

几乎不需要直接更改设置的参数

  • api_service_event_tags_size: 同时运行管理专用RESTful API服务的个数(type=uint64, default=1)