4.3. RESTful应用开发规范

hetao在hetao_socgi层上又增加了hetao_rest层,便于直接开发RESTful风格应用。开发接口头文件在hetao_rest.h中,示例test/test_socgi_rest_hello/test_socgi_rest_hello.c演示了简单开发。

  1. #include "hetao_rest.h"
  2. #include "LOGC.h"
  3.  
  4. funcRestServiceEntry GET_hello ;
  5. int GET_hello( struct RestServiceContext *ctx )
  6. {
  7. char http_body[ 1024 ] = "" ;
  8. int http_body_len = 0 ;
  9.  
  10. int nret = 0 ;
  11.  
  12. InfoLog( __FILE__ , __LINE__ , "GET_hello" );
  13.  
  14. BUFSTRCAT( http_body , http_body_len , "hetao_rest : hello world\n" ) ;
  15. nret = RESTFormatHttpResponse( ctx , http_body , http_body_len , NULL ) ;
  16. if( nret )
  17. {
  18. ErrorLog( __FILE__ , __LINE__ , "RESTFormatHttpResponse failed[%d]" , nret );
  19. return nret;
  20. }
  21. else
  22. {
  23. InfoLog( __FILE__ , __LINE__ , "RESTFormatHttpResponse ok" );
  24. return 0;
  25. }
  26. }
  27.  
  28. static struct RestServiceConfig g_RSC_ARRAY[] =
  29. {
  30. { HTTP_METHOD_GET , "/hello.do" , GET_hello } ,
  31. { "" , "" , NULL }
  32. } ;
  33.  
  34. INITHTTPAPPLICATION InitHttpApplication ;
  35. int InitHttpApplication( struct HttpApplicationContext *ctx )
  36. {
  37. struct RestServiceControler *ctl = NULL ;
  38.  
  39. InfoLog( __FILE__ , __LINE__ , "InitHttpApplication" );
  40.  
  41. ctl = RESTCreateRestServiceControler( g_RSC_ARRAY ) ;
  42. if( ctl == NULL )
  43. {
  44. ErrorLog( __FILE__ , __LINE__ , "RESTCreateRestServiceControler failed" );
  45. return REST_FATAL_CREATE_RESTSERVICECONTROLER;
  46. }
  47. else
  48. {
  49. DebugLog( __FILE__ , __LINE__ , "RESTCreateRestServiceControler ok" );
  50. }
  51.  
  52. SOCGISetUserData( ctx , ctl );
  53.  
  54. return 0;
  55. }
  56.  
  57. CALLHTTPAPPLICATION CallHttpApplication ;
  58. int CallHttpApplication( struct HttpApplicationContext *ctx )
  59. {
  60. struct RestServiceControler *ctl = NULL ;
  61.  
  62. int nret = 0 ;
  63.  
  64. InfoLog( __FILE__ , __LINE__ , "CallHttpApplication" );
  65.  
  66. ctl = SOCGIGetUserData( ctx ) ;
  67. if( ctl == NULL )
  68. {
  69. ErrorLog( __FILE__ , __LINE__ , "SOCGIGetUserData failed" );
  70. return REST_FATAL_GET_RESTSERVICECONTROLER;
  71. }
  72. else
  73. {
  74. DebugLog( __FILE__ , __LINE__ , "SOCGIGetUserData ok" );
  75. }
  76.  
  77. nret = RESTDispatchRestServiceControler( ctl , SOCGIGetHttpEnv(ctx) ) ;
  78. if( nret )
  79. {
  80. ErrorLog( __FILE__ , __LINE__ , "RESTDispatchRestServiceControler failed[%d]" , nret );
  81. return nret;
  82. }
  83. else
  84. {
  85. DebugLog( __FILE__ , __LINE__ , "RESTDispatchRestServiceControler ok" );
  86. }
  87.  
  88. return 0;
  89. }
  90.  
  91. CLEANHTTPAPPLICATION CleanHttpApplication ;
  92. int CleanHttpApplication( struct HttpApplicationContext *ctx )
  93. {
  94. struct RestServiceControler *ctl = NULL ;
  95.  
  96. InfoLog( __FILE__ , __LINE__ , "CleanHttpApplication" );
  97.  
  98. ctl = SOCGIGetUserData( ctx ) ;
  99. if( ctl == NULL )
  100. {
  101. ErrorLog( __FILE__ , __LINE__ , "SOCGIGetUserData failed" );
  102. return REST_FATAL_GET_RESTSERVICECONTROLER;
  103. }
  104. else
  105. {
  106. DebugLog( __FILE__ , __LINE__ , "SOCGIGetUserData ok" );
  107. }
  108.  
  109. RESTDestroyRestServiceControler( ctl );
  110.  
  111. return 0;
  112. }

首先应用开发服务接口GET_hello(在示例中只是简单的组织了HTTP响应报文),然后配置到RESTful服务列表配置g_RSC_ARRAY中,其中第一列为HTTP方法,第二列为URI,可以使用“{}”通配任意数据,三入口函数代码基本不用修改直接拿来用,函数InitHttpApplication根据RESTful服务列表配置创建RESTful服务控制器,设置进HTTP应用上下文中,当符合URI扩展名的HTTP请求到来时hetao会调用函数CallHttpApplication,从HTTP应用上下文中取出RESTful服务控制器,调用函数RESTDispatchRestServiceControler分派到对应的服务函数中,函数CleanHttpApplication用于销毁RESTful服务控制器。

函数CallHttpApplication的参数上下文和服务函数中的参数上下文是两个结构体,前者是socgi规范定义的HTTP应用上下文,后者是REST服务上下文。

在服务函数中可使用hetao_rest.h中的函数访问HTTP信息,函数RESTGetHttpMethodPtr用于查询HTTP请求方法,函数RESTGetHttpUriPtr用于查询HTTP请求URI,函数RESTGetHttpUriPathsCount用于查询URI分解后的目录数量,函数RESTGetHttpUriPathPtr用于查询每一段的目录名,函数RESTGetHttpUriQueriesCount用于ChaunceyURI分解后的参数数量,函数RESTGetHttpUriQueryKeyPtr和RESTGetHttpUriQueryValuePtr用于查询参数键值,函数RESTGetHttpRequestBodyPtr用于获取HTTP请求体,函数RESTFormatHttpResponse用于组织HTTP响应报文。

一个比较复杂的示例在test/test_socgi_rest_full/test_socgi_rest_full.c,其RESTful服务配置表供使用参考

  1. static struct RestServiceConfig g_RSC_ARRAY[] =
  2. {
  3. { HTTP_METHOD_GET , "/" , GET_ } , /* curl "http://localhost/" */
  4. { HTTP_METHOD_GET , "/path1" , GET_path1 } , /* curl "http://localhost/path1" */
  5. { HTTP_METHOD_GET , "/path1/" , GET_path1_ } , /* curl "http://localhost/path1/" */
  6. { HTTP_METHOD_GET , "/path1/path2" , GET_path1_path2 } , /* curl "http://localhost/path1/path2" */
  7. { HTTP_METHOD_GET , "/path1/path2/" , GET_path1_path2_ } , /* curl "http://localhost/path1/path2/" */
  8. { HTTP_METHOD_GET , "/path1/path2/file" , GET_path1_path2_file } , /* curl "http://localhost/path1/path2/file" */
  9. { HTTP_METHOD_GET , "/path1/{}/file" , GET_path1_n_file } , /* curl "http://localhost/path1/123/file1" */
  10. { HTTP_METHOD_GET , "/path1/path2/file1" , GET_path1_path2_file1__key1_value1 } , /* curl "http://localhost/path1/path2/file1?key1=value1" */
  11. { HTTP_METHOD_GET , "/path1/path2/file2" , GET_path1_path2_file2__key1_value1__key2_value2 } , /* curl "http://localhost/path1/path2/file2?key1=value1&key2=value2" */
  12. { HTTP_METHOD_GET , "/path1/path2/file3" , GET_path1_path2_file3__ } , /* curl "http://localhost/path1/path2/file3?" */
  13. { HTTP_METHOD_GET , "/path1/path2/file4" , GET_path1_path2_file4__key1 } , /* curl "http://localhost/path1/path2/file4?key1" */
  14. { HTTP_METHOD_GET , "/path1/path2/file5" , GET_path1_path2_file5__key1_ } , /* curl "http://localhost/path1/path2/file5?key1=" */
  15. { HTTP_METHOD_GET , "/path1/path2/file6" , GET_path1_path2_file6__key1__ } , /* curl "http://localhost/path1/path2/file6?key1&" */
  16. { HTTP_METHOD_GET , "/path1/path2/file7" , GET_path1_path2_file7__key1___ } , /* curl "http://localhost/path1/path2/file7?key1=&" */
  17. { HTTP_METHOD_POST , "/path1/file" , POST_path1_file } , /* curl -X POST -d "this is a POST test for restserver" "http://localhost/path1/file" */
  18. { HTTP_METHOD_PUT , "/path1/file" , PUT_path1_file } , /* curl -X PUT -d "this is a PUT test for restserver" "http://localhost/path1/file" */
  19. { HTTP_METHOD_DELETE , "/path1/file" , DELETE_path1_file } , /* curl -X DELETE -d "this is a DELETE test for restserver" "http://localhost/path1/file" */
  20. { "" , "" , NULL }
  21. } ;