一个简单嵌入式WEB业务应用设计
主要是针对之前产品中WEB应用的设计的重构,把业务与一些东西分离出来,方便维护,本身比较简单, 注意这个不是说写WEB服务程序的。
工具
goahead
json-c
jquery
数据交互
WEB服务与主应用交互
WEB服务进程同应用进程在同一个嵌入式系统中,两个进程的交互使用UNIX域UDP方试通讯,主要的是数据是,WEB前端的数据请求,数据的提交或控制命令,数据都用json的格式传输。
WEB服务与WEB前端交互
WEB前端的HTML请求都通过goahead处理了,在数据上面,我们用AJAX传输JSON的方式,goahead通过UDP服务转给主应用,主应用处理后返回进goahead,再回应给WEB前端。
之所以用UNIX域UDP通信,一个是速度,也可靠,JSON交换的数据也不是太大,双向通信,简单,包与包之前不用再去分隔了。
WEB服务端
Web服务使用开源的goahead这个工具,它相对于WEB前端来说是一个服务端,在与我们的主应用来说又是一个UNIX域UDP客户端。
WEB服务端的初始化
Goahead的初始化可以参见它的例子中,其实我对这个工具也不太熟悉,在它提供的例子里来改一下,它的初始化操作中有一个initWebs 的过程,这里面设置了一些必要的参数,如默认的网页,比如我的设置的是 index.html 是一个宏定义的:
#define WEBS_DEFAULT_HOME T("index.html") /* Default home page */
#define WEBS_DEFAULT_PORT 80 /* Default HTTPport */
#defineWEBS_DEFAULT_SSL_PORT 433 /* Default HTTPSport */
分别定义了默认的主页,端口。
因为要与主应用中通讯,使用的是UNIX的域UDP方式,所以在goahead里我们要初始创建一个socket的来与主应用交互。
在一个合适的地方,定义一个Web_Client_init,它的作用就是初始化创建一个UNIX的域UDP socket而已,所以在initWebs中调用过程: Web_Client_init()。
在一个合适的地方,定义一个 int Web_Client_Send(char* buf, int len, char* recvbuf, int* maxlen), 用它来把WEB前端的数据转发给主应用,同时接收主应用的回应。
在goahead里接收WEB前端过来的AJAX 请求json数据,做如下的动作:
设置一个URL请求的处理过程:
websUrlHandlerDefine(T("/ajax/json_data"),NULL, 0, ajax_json_data, 0);
这果 URL 为 http://xxxx/ajax/json_data 这样的URL过来的数据,都传给ajax_json_data处理了。如我的如下的处理,
#define RECV_BUF_LEN 2048
static charRECV_BUF[RECV_BUF_LEN];
int ajax_json_data(webs_t wp, char_t *urlPrefix, char_t *webDir, int arg,
char_t *url, char_t *path, char_t *query)
{
int ret;
char* data = websGetVar(wp,T("data"),"");
int len = strlen(data);
//ret = WebMsg_Send(data, len);
//char recvbuf[1024] = {0};
int recvlen = RECV_BUF_LEN;
RECV_BUF[0]= 0;
ret= Web_Client_Send(data, len, RECV_BUF, &recvlen);
if(ret == 0)
{
if(recvlen == 0)
{
websWrite(wp,T("{status:'failed', data:'Server nodata!'}"));
}else{
websWrite(wp,RECV_BUF);
}
}else{
websWrite(wp,T("{status:'failed'}"));
}
websDone(wp,200);
return 1;
}
一般JSON的请求数据都不大太,注意空间的分配,上面的过程就是调用 Web_Client_Send 把数据发给主应用,主应用处理完后,同样返回的是JSON格式的应答,获取主应用的应答后通过websWrite写回给WEB前端,注意 websGetVar 获取数据的方式,找的是名为”data”的,这个是在WEB网页中JS里定义的,原因是我们的JS代码是这样写的:
functionajaxActionWrap(actionData, callback)
{
$.post(URL_AJAX_ACTION,
{
data: actionData,
},
function(data, status){
if(callback)
callback(data, status)
}
);
}
这样完成后,我们的WEB服务端的事就完成了,它的作用就是负责传输数据,把它同业务分它了。
主应用
主应用的一个任务是要接收WEB服务传过来的WEB前端请求。
创建一个UNIX域UDP服务。比如我们在一个合适的地方创建一个初始化启动服务的方法: Web_Server_Init(),这样就能接收到WEB的请求了。
另一个任务就是对WEB的AJAX JSON请求的处理回应了。
不同的JSON调用,针对不同的业务,所以我们定义一个记录不同业务处理过程的结构:
比如:
typedef int (*JsonActionProc)(char* data, void* jsonobj,AjaxActionProcCallBack cb);
typedef struct _WebAjaxAction{
char* actionName;
JsonActionProc actionProc;
}WebAjaxAction;
actionName是对应处理方法的标识,如WEB前端传一个这样的JSON数据过来:
{"action":"wifi_status","data":{"p1":1,"p2":2}}
那么 wifi_status 就是对应actionName.
记录注册每一个json里的action调用过程,如:
static WebAjaxActionwebAjaxActionList[] = {
{"wifi_status",wifi_status},
{"wifi_scan",wifi_scan},
{"wifi_get_aplist",wif_get_aplist},
{"wifi_conn",wifi_conn},
{"wifi_getAplist",wifi_getAplist},
};
像wifi_status,wifi_scan… 这些都是调用方法的标识,这个要与WEB前端协商好,保持一致。
actionProc 参数就是对应的调用方法了。
上面的过程,也就是如 WEB 传一个JSON数据过来,它里面有对应的actionName,通过actionName找到对应的业务方法,JSON里也会有一些参数,把JSON里的数据解析出来,调用对应的业务方法就行了,同时把处理的过后的数据通过WEB服务端返回的WEB前端。
从上面的过程,来说,调用recvfrom接收到数据报后,调用一个过程,从webAjaxActionList 里找到对应的处理方法,调用处理方法,处理方法完成后,把要返回的数据通过sendto 再回到WEB服务端,再返回的WEB前端。整个过程就完事了。
WEB前端
完成上面的东西,WEB前端就可认方便的调用了,当HTML显示加载完成后,再通过AJAX的方式,用JSON的格式来调用数据。
如我的获取一个WIFI的连接状态,当然,交互的JSON数据需要WEB前端与主应用后端要协商好,用什么样的格式,比如我的是:
{"action":"wifi_status"}
表示调用的方法是 wifi_status
如下面的几个相关的方法:
/*获取WIFI连接状态*/
functiongetWifiStatus()
{
var data ='{"action":"wifi_status"}';
ajaxActionWrap(data,getWifiStatusCallBack);
}
functionajaxActionWrap(actionData, callback)
{
$.post(URL_AJAX_ACTION,
{
data: actionData,
},
function(data, status){
if(callback)
callback(data,status)
}
);
}
functiongetWifiStatusCallBack(data, status)
{
if(status != "success")
{
alert("get wifi statusfailed!\n");
return;
}else{
var jobj = eval(data)
if(jobj)
{
$("#wifiCurAp").html(jobj[0].wifi_status.ssid);
if(jobj[0].wifi_status.status== 1)
$("#wifiCurStatus").html("已连接");
else
$("#wifiCurStatus").html("未连接");
}else{
$("#wifiCurStatus").html("未连接");
}
}
}
在后端,它会找到wifi_status的对应处理方法,处理后返回定义好的JSON数据格式,WEB前端再显示出来。
最后
通过上面的处理方式,对于主应用来说,把业务相关的都隔离开了,在对应的处理方法中,WEB服务端只负责传输数据,WEB前端通过AJAX的方式传输JSON数据和接收,通过JS脚本控制。
在添加新的业务时,也只要修改WEB前端的JS脚本,主后端注册一下处理的方法就行。
注:对WEB应用只懂一点点,对上面的实现提一点注意的地方:
1:JS脚本里最好不要用eval转换数据生成对像,不安全,可以用jQuery里的对应工具。
2:AJAX的请求认证,一定要记得添加,像上面的应用的话,比较好的方式可以加在WEB服务端的ajax_json_data 里,在请求后端应用时,验证一下,见很多产品,AJAX请求尽然没有验证权限。