SSI技术的前端动态实现(AJAX+SSI,适用于任何系统,包括搭载嵌入式RTOS的STM32系列开发板)
前言
嵌入式开发常会使用到RTOS系统,该系统因简洁和贴切现实时间而闻名,但其特性有时会使得习惯于Windows WEB开发或Linux 嵌入式WEB开发的从业人员而感到困惑。本文笔者为Web前端(嵌入式)开发者,将结合笔者在开发时遇到的问题及解决方案做一个简单的描述,文末会附上部分源代码,方便各位遇到相同困难的开发者进行调试和更改。欢迎转载、收藏,如有侵权,请及时告知笔者删除。
RTOS简述及开发困难所在
RTOS(Real Time Opreation System),实时操作系统。该系统内部十分简洁,因此运行时的开销极小,其设计者由此保证其相比Windows、Linux、Unix等更能贴近真实的时间。
由于其内部并无文件系统,所有的开发资料均以二进制格式存储于其中,并调用已编译好的C文件执行,从某种角度来说,并不能实现前端严格意义上分区,也就无法使用诸如Apache这类常规意义上需要依靠文件系统运行的服务器。为此,STM32系列为用户提供了LWIP(一个简单的server协议栈,可将其视为一个简化版的Apache)的指导建设服务,使得嵌入式Web前端可以简单的形式在该系统上运行,并且使用SSI和CGI技术为前后端的分离和链接进行一定程度上的动态操作。笔者在此使用lwip1.4.1进行web server的部署。当然,这并不是重点,所以本文不会详细描述如何部署LWIP以及如何使用makefsdata和keil5工具将前端页面编译的过程,但考虑到嵌入式开发的需求,下文将会提供开发者在Windows&Linux及RTOS上分别关联的开发环境。
开发环境
Windows&Linux下(64位):
服务器:Apache Tomcat-9.0.54
开发工具:NetBeans 12.3
目录结构:多级目录结构
文件主要构成:html、shtml
RTOS下:
开发板:STM32-4XXZ系列(其它搭载RTOS的开发板也适合)
服务协议:lwip 1.4.1
开发工具:keil5、makefsdata(页面本身的开发可直接在Win或Linux下完成,需要后者转码,前者编译载入)
目录结构:无(RTOS无文件系统,不存在web server的目录结构)
转码的主文件:fsdata.c 、fsdata.h及一系列的http配置文件
(注:有关tomcat配置及其下CGI、SSI的配置在本文中不做提及,lwip的配置也不做提及,如有需要,请自行百度)
详细的解决方案
由于本文着重于SSI的动态实现而非RTOS相关内容,本节将以Windows或Linux系统下的开发过程为例,着重叙述开发过程中发现的问题,对于部分非本文核心内容不做提及,有需要者自行百度,如有更好解决方案,也可联系笔者。
SSI初加载
在我们参照各网站的教程配置好上述开发环境中任何一个以后,我们就可以运行自己的web server了,这里需要告诉各位读者的是SSI有编码限制,默认为GBK,因此要及时去更改配置,否则将出现中文乱码,另外如需检测html中的SSI tag,也要参照教程提前配置,如果是和笔者一样使用的tomcat作为web服务器的读者,一定要注意放开conf.xml中的SSI过滤器字段,否则shtml将不会被tomcat识别和编译。
我们写一个简易的shtml:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<div>
<h2>Date = <!--#echo var = "date_local"--></h2>
</div>
<script src="js/jquery.min.js"></script>
</body>
</html>
在笔者的工作背景下,客户提出想要动态地在RTOS上实现一定的前端操作,比如动态地显示某些数据,此处我们以时间显示为例,来叙述这个动态的过程。SSI提供了一些简易的标签供用户使用,而这些tag在shtml中看起来就像是html的注释。
运行演示样例.shtml,我们会得到如下的结果(马赛克宝石水花保佑我不泄密~):
基础的动态操作
现在我们希望这个时间可以动态刷新,就像一个正常的时钟一样,一秒钟跳动一次,于是我们添加一小段脚本,无须担心,即使是RTOS也能识别Javascript(普通的js动态加载方法多种多样,笔者这里只采取其中一种,其他方法可以上网自己查询,不要问我其他方法,我不记得那么多,难道你会记得自己吃了多少片面包么?):
<script type="text/javascript">
setTimeout(location.reload(),1000);
</script>
那么我么将会得到如下图的这种情况:
可以看到,在我们的网页里,这个时钟的确按照我们的预想开始动态加载了,但是效果并不好,因为刷新按钮一直在跳动,并且页面上端的ICO一直处于一个正在刷新的状态。如果你是一个大学生,像这样马马虎虎地交了作业当然可以,但我们作为从业者在为客户开发软件时必须考虑到用户的体验,这种东西真的能送给客户去使用嘛?当然不可以,用术语来讲,这种动态近似于真动态,页面在实时刷新,而不是通过后台的更新来使得前台进行数据更迭。简而言之,目前的情况是:前端为主,后端为辅(注:此处的后端说的是“前台的后端”,并非是真正的数据处理的无界面后端,但因为脚本的操作形式,可将其视作一个“后端”,下同)。是前端在刷新页面希望后端来进行更新。那么,有没有一种操作方式来进行局部的刷新呢?答案是有。
伪动态更新(?)
为什么会打一个问号?因为接下来实施的操作存在一定的错误。在这里,我相信读者会对笔者接下来的操作赶到疑惑,有人说:“你为什么现在不用AJAX?”,事实上,ajax当然是可以进行局部刷新操作的,只不过现在这个简单的示例网页没有东西让我们去请求。如果你有真正的后端(windows里基于C的后端)倒是可以直接使用ajax去请求SSI字段的程序,而实质上,SSI的tag自己其实就是一种请求,它会把tag里的东西传给处理程序,然后返回处理的的结果。而在RTOS中是没有文件结构的,甚至于你的LWIP项目都不会和主程序绑定在一起,是后端在给你进行操作和处理,那么这种情况下,我们如何去更新SSI呢?
有的小朋友会好奇,按照HTML和JS的思路,为什么不能只更新当前SSI tag所在的div进行一个伪动态呢?当然可以,让笔者先贴出DIV局部刷新的JS代码(此处会用到jquery,没有的自行下载一个,免费!免费!免费!不要吝啬自己可以接触新工具的机会,但不建议你去深入学习jquery,现在去学他,等于四九年入*军,懂我意思吧?):
// setTimeout(location.reload(),1000);
$(function () {
setInterval(function () {
$("#aaa").load(location.href + " #aaa>*", "");/*注意后面DIV的ID前面的空格跟
id 后的>*,很重要!*/
}, 1000);//1秒自动刷新
});
同时,让我们给div加上一个id:
<div id="aaa">
<h2>Date = <!--#echo var = "date_local"--></h2>
</div>
现在,让我们刷新网页来看看:
可以看到,div刷新了么?废话,他当然在刷新,不过细心的小伙伴应该能看到,虽然页面在局部刷新,意图接受后端SSI程序为我们传递过来的值,可时钟就跟喝了催逝员的毒鸡汤一样 什么变化也没有。我们明确一点,还是以主仆关系类比:此时,前端页面变成了servant,而后端变成了master,master给servant下了令咒,结果servant是个单独行动的Archer,而且对魔力EX,直接无视了这个变化。我们应当清楚,这个时候的动态其实一个伪动态,因为页面本身没有更新,是通过更新div来重新展示后台的返回值(前提是你有真后端的话),由此我们可以得出一个结论,即SSI本身的请求并不支持伪动态,只支持真动态,那么,有没有一种方法,可以让SSI本身既能是真动态响应,而页面看起来又是伪动态呢?答案是有的。
AJAX+SSI实现动态调用(划重点,要考的!)
前面卖了这么多关子(阔累哇试炼哒!),现在终于来到了核心。通过上文我们已经知道SSI只能响应页面的真动态,除非你刷新,不然它是不会变的。伪动态使得前后端的主从关系发生了一定的变更,也许我们应该让他们回到真动态。好好想想吉尔伽美什和远坂时臣的关系(都是时臣的错!),我们可以利用这一点完善我们的逻辑,因此,结合ajax可以调用任意文件和程序的功能,笔者想到了虚虚实实实实虚虚的操作逻辑:
- 把SSI写在另一个页面里,让ajax去调用它;
- 将被调用的页面写入主页面;
- 让ajax定时调用,保证页面可以实时更新。
于是,我们便得到了如下的完整代码:
- Blog.shtml
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body onload="req()"><!--加载时调用请求函数,保证其自动化执行-->
<div id="aaa"></div>
<script src="js/jquery.min.js"></script>
<script type="text/javascript">
$.ajax({cache: false}); //设置全局不缓存,否则会调用缓存的页面
function req() {
$.ajax({
url: "SSI.shtml",
type: "post", // 请求方式需要设置为post,这样才能动态刷新
datatype: "text", // 最好以文本格式返回
/* 开始定义请求成功的函数 */
success: function (data) {
/* 函数自带参数data,为请求返回的内容,这里当然就是页面内容了 */
$("#aaa").text(data); // 直接给他写进这个div里面
},
error: function () {
/* 这里是请求失败函数,可以不写,测试阶段需要的话可以写几句 */
console.log("请求失败了……但不要停下来,只要不停下来,请求就在前方!所以!不要停下来啊!");
},
final: function () {
/* 这里是请求最终函数,可以不写,无论是否成功都会执行它,测试阶段需要的话可以写几句 */
console.log("请求已执行,SSI只是装饰,客户是不会懂的……");
}
});
setTimeout("req()", 1000); // 让这个请求函数递归调用自己,实现定时更新
}
</script>
</body>
</html>
- SSI.shtml(没错,就是这么简单,一个tag就足够了,不需要其他的修饰,修饰在只要在主页面更改样式就好)
<!--#echo var = "DATE_LOCAL" -->
然后我们看到的页面时怎么样一个效果呢?
是的,可以看到网络请求在不断进行,而且更新的速度也能跟上,在看主页面元素部分,这里确实在进行伪动态刷新,然而因为post形式的请求格式,请求目标SSI.shtml实际上在不断的重新载入,这个页面去世在不断的进行真动态刷新,然后我们把页面处理好的结果当成插入栓放入初号机 主页面,可以看到它确实是一个看起来是伪动态但实际上是真动态的结果页面。由此,AJAX+SSI的动态调用就算是结束了,你甚至可以RTOS这个简单且没有文件系统的系统里调用它!
结语
没什么多的需要说的了,大家看看就好,毕竟笔者也不是什么魔鬼,倒是开发RTOS的人,为了那么一点实时性的苛求而荒废文件系统,才真的像一个魔鬼……(干点正事儿吧!巴巴托斯!)
欢迎点赞、评论、收藏、转发,如需转载,请注明出处,本文为笔者原创,版权所有,侵权必究。