32. Server management part 1: Adding RESTful APIs

iFun Engine provides a function to easily add RESTful APIs inside the game server. By using this function, you can easily connect to various xternal systems using the HTTP protocol.

32.1. Registering handlers and REST API URLs

You can use the following function to register a particular URL or URL pattern and the handler to process it.

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 text string to handle. Used when calling only one particular URL.
  • path_pattern: URL pattern to handle. Parameters can be included in the URL in (?<NAME>pattern) form and read as NAME in the code. (See example below)
  • handler: Handler function to handle that URL.
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)

32.2. Creating handlers

You can create a handler function as follows.

  • If duplicate keys are not allowed during parameter transfer through HTTP GET

    void MyHandler(
      Ptr<http::Response> response,
      const http::Request &request,
      const ApiService::MatchResult &params) {
    
      response->status_code = http::kOk;
      response->body = ...
    }
    
  • If duplicate keys are allowed during parameter transfer through HTTP GET

    void MyHandler2(
      Ptr<http::Response> response,
      const http::Request2 &request,
      const ApiService::MatchResult &params) {
    
      response->status_code = http::kOk;
      response->body = ...
    }
    
  • If the response body is asynchronously generated (duplicate keys permitted for HTTP GET parameters)

    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);
    }
    
  • Handling a single URL synchronously

    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;
    }
    
  • Handling synchronously when the URL is given as a 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;
    }
    
  • Handling a single URL asynchronously

    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);
    }
    
  • Handling asynchronously when the URL is given as a 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);
    }
    

32.3. Example of API service registration

The following example shows registration of two APIs.

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);
  ...
}

32.4. ApiService parameters

  • api_service_port: Local TCP port number to switch to administrative RESTful API service. (type=uint64, default=8015)
  • api_service_logging_level: Log level for administrative RESTful API messages. If 0, no logs are kept. If 1, only errors are logged. If 2, all requests are logged (type=uint64, default=2)

Parameters with configurations that are almost never changed manually

  • api_service_event_tags_size: Number of administrative RESTful API services switching simultaneously (type=uint64, default=1)