4.3. RESTful应用开发规范
hetao在hetao_socgi层上又增加了hetao_rest层,便于直接开发RESTful风格应用。开发接口头文件在hetao_rest.h中,示例test/test_socgi_rest_hello/test_socgi_rest_hello.c演示了简单开发。
- #include "hetao_rest.h"
- #include "LOGC.h"
- funcRestServiceEntry GET_hello ;
- int GET_hello( struct RestServiceContext *ctx )
- {
- char http_body[ 1024 ] = "" ;
- int http_body_len = 0 ;
- int nret = 0 ;
- InfoLog( __FILE__ , __LINE__ , "GET_hello" );
- BUFSTRCAT( http_body , http_body_len , "hetao_rest : hello world\n" ) ;
- nret = RESTFormatHttpResponse( ctx , http_body , http_body_len , NULL ) ;
- if( nret )
- {
- ErrorLog( __FILE__ , __LINE__ , "RESTFormatHttpResponse failed[%d]" , nret );
- return nret;
- }
- else
- {
- InfoLog( __FILE__ , __LINE__ , "RESTFormatHttpResponse ok" );
- return 0;
- }
- }
- static struct RestServiceConfig g_RSC_ARRAY[] =
- {
- { HTTP_METHOD_GET , "/hello.do" , GET_hello } ,
- { "" , "" , NULL }
- } ;
- INITHTTPAPPLICATION InitHttpApplication ;
- int InitHttpApplication( struct HttpApplicationContext *ctx )
- {
- struct RestServiceControler *ctl = NULL ;
- InfoLog( __FILE__ , __LINE__ , "InitHttpApplication" );
- ctl = RESTCreateRestServiceControler( g_RSC_ARRAY ) ;
- if( ctl == NULL )
- {
- ErrorLog( __FILE__ , __LINE__ , "RESTCreateRestServiceControler failed" );
- return REST_FATAL_CREATE_RESTSERVICECONTROLER;
- }
- else
- {
- DebugLog( __FILE__ , __LINE__ , "RESTCreateRestServiceControler ok" );
- }
- SOCGISetUserData( ctx , ctl );
- return 0;
- }
- CALLHTTPAPPLICATION CallHttpApplication ;
- int CallHttpApplication( struct HttpApplicationContext *ctx )
- {
- struct RestServiceControler *ctl = NULL ;
- int nret = 0 ;
- InfoLog( __FILE__ , __LINE__ , "CallHttpApplication" );
- ctl = SOCGIGetUserData( ctx ) ;
- if( ctl == NULL )
- {
- ErrorLog( __FILE__ , __LINE__ , "SOCGIGetUserData failed" );
- return REST_FATAL_GET_RESTSERVICECONTROLER;
- }
- else
- {
- DebugLog( __FILE__ , __LINE__ , "SOCGIGetUserData ok" );
- }
- nret = RESTDispatchRestServiceControler( ctl , SOCGIGetHttpEnv(ctx) ) ;
- if( nret )
- {
- ErrorLog( __FILE__ , __LINE__ , "RESTDispatchRestServiceControler failed[%d]" , nret );
- return nret;
- }
- else
- {
- DebugLog( __FILE__ , __LINE__ , "RESTDispatchRestServiceControler ok" );
- }
- return 0;
- }
- CLEANHTTPAPPLICATION CleanHttpApplication ;
- int CleanHttpApplication( struct HttpApplicationContext *ctx )
- {
- struct RestServiceControler *ctl = NULL ;
- InfoLog( __FILE__ , __LINE__ , "CleanHttpApplication" );
- ctl = SOCGIGetUserData( ctx ) ;
- if( ctl == NULL )
- {
- ErrorLog( __FILE__ , __LINE__ , "SOCGIGetUserData failed" );
- return REST_FATAL_GET_RESTSERVICECONTROLER;
- }
- else
- {
- DebugLog( __FILE__ , __LINE__ , "SOCGIGetUserData ok" );
- }
- RESTDestroyRestServiceControler( ctl );
- return 0;
- }
首先应用开发服务接口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服务配置表供使用参考
- static struct RestServiceConfig g_RSC_ARRAY[] =
- {
- { HTTP_METHOD_GET , "/" , GET_ } , /* curl "http://localhost/" */
- { HTTP_METHOD_GET , "/path1" , GET_path1 } , /* curl "http://localhost/path1" */
- { HTTP_METHOD_GET , "/path1/" , GET_path1_ } , /* curl "http://localhost/path1/" */
- { HTTP_METHOD_GET , "/path1/path2" , GET_path1_path2 } , /* curl "http://localhost/path1/path2" */
- { HTTP_METHOD_GET , "/path1/path2/" , GET_path1_path2_ } , /* curl "http://localhost/path1/path2/" */
- { HTTP_METHOD_GET , "/path1/path2/file" , GET_path1_path2_file } , /* curl "http://localhost/path1/path2/file" */
- { HTTP_METHOD_GET , "/path1/{}/file" , GET_path1_n_file } , /* curl "http://localhost/path1/123/file1" */
- { HTTP_METHOD_GET , "/path1/path2/file1" , GET_path1_path2_file1__key1_value1 } , /* curl "http://localhost/path1/path2/file1?key1=value1" */
- { HTTP_METHOD_GET , "/path1/path2/file2" , GET_path1_path2_file2__key1_value1__key2_value2 } , /* curl "http://localhost/path1/path2/file2?key1=value1&key2=value2" */
- { HTTP_METHOD_GET , "/path1/path2/file3" , GET_path1_path2_file3__ } , /* curl "http://localhost/path1/path2/file3?" */
- { HTTP_METHOD_GET , "/path1/path2/file4" , GET_path1_path2_file4__key1 } , /* curl "http://localhost/path1/path2/file4?key1" */
- { HTTP_METHOD_GET , "/path1/path2/file5" , GET_path1_path2_file5__key1_ } , /* curl "http://localhost/path1/path2/file5?key1=" */
- { HTTP_METHOD_GET , "/path1/path2/file6" , GET_path1_path2_file6__key1__ } , /* curl "http://localhost/path1/path2/file6?key1&" */
- { HTTP_METHOD_GET , "/path1/path2/file7" , GET_path1_path2_file7__key1___ } , /* curl "http://localhost/path1/path2/file7?key1=&" */
- { HTTP_METHOD_POST , "/path1/file" , POST_path1_file } , /* curl -X POST -d "this is a POST test for restserver" "http://localhost/path1/file" */
- { HTTP_METHOD_PUT , "/path1/file" , PUT_path1_file } , /* curl -X PUT -d "this is a PUT test for restserver" "http://localhost/path1/file" */
- { HTTP_METHOD_DELETE , "/path1/file" , DELETE_path1_file } , /* curl -X DELETE -d "this is a DELETE test for restserver" "http://localhost/path1/file" */
- { "" , "" , NULL }
- } ;