MyServerless 3.0 发布,在前端 JS/HTML 里直接写 Java 和 SQL

来源: 投稿
作者: yong9981
2021-07-19

MyServerless(原GoSqlGo)简介 | Description

天下武功,唯快不破,MyServerless能让前端直接在HTML或Javascript里写业务代码和SQL,不需要后端程序员参与,从而实现快速开发。
MyServerless主页:https://gitee.com/drinkjava2/myserverless

本次更新内容:

1.项目原名为GoSqlGo,现从3.0即本版起更名为MyServerless。
2.myserverless.properties文件中新增web_files选项,可以指定在哪些前端文件中允许直接书写远程Java脚本和SQL,默认是html,htm,js,jsp,php这几种。
3.myserverless.properties文件中新增api_export_file选项,可以指定一个输出文件名,当运行go-server.bat进行打包布署时,可以按指定的文件名自动将页面所有Java和SQL生成一个API汇总文档。
4.原项目中出现.gsg调用的地方统一更改为myserverless.do调用。
5.升级它的DAO工具jSqlBox到5.0.6.jre8版,这个版本添加了一个camelHandler,用于将JDBC查询结果Map/Array/List对象中的下划线格式的键值名称转为驼峰式,如下面这行SQL调用:
  List<Map<String, Object>> result = DB.qryMapList("select user_name from users where age>", que(10), " order by id ", pagin(1, 10), new CamelHander());
 在添加了CamelHander之后返回结果中的键名user_name将转换为userName。
6.修正了jSqlBox中jDialects子项目达梦数据库支持以及从数据库生成Java源码的bug。

MyServerless项目原名为GoSqlGo,但这个命名不太贴切,因为它不光是可以直接在前端写SQL,还可以直接写Java源码,考虑到它的这个开发模式有点类似于serverless,后端不参与具体业务开发,只提供硬件平台和基础的API供调用,开发都可以远程进行,而且都是以方法作为最小且唯一的功能单元,所以从3.0版起项目更名为MyServerless,原项目GoSqlGo不再维护。

MyServerless与通常大厂的Serverless服务相比,主要区别是:
1.使用免费。通常大厂的Serverless服务是按调用来按需收费,而MyServerless是用户自己布署的,调用不收费,还是按传统租用空间方式收费。
2.可定制,无依赖。开发者拥有全部的MyServerless服务端源码,可以自行定制后端服务,如选择不同的签权方式、DAO工具等,不与具体的云服务商绑定。
3.技术极简。大厂的Serverless服务通常较复杂,很难上手。而MyServerless核心源码只有几千行,采用标准Java脚本语言和SQL,极易上手,通常前端只需要了学会SQL即尝试进行开发。
4.功能极度简化。MyServerless的定位是用最少的代价开通前端访问数据库能力。相比与大厂的Serverless服务,它在功能上有缺失:
1)目前不提供源码管理和IDE插件,后端源码就直接保存在前端。
2)目前不提供高可用性、后端自动缩扩容这些功能。
3)目前只整合了一个DAO工具,专注于数据库访问,不支持一些特殊功能如文件上传等。
4)大厂Serverless服务通常不区分开发期和布署期,而MyServerless因为安全原因,分为开发和布署两个阶段,开发期源码写在前端,源码远程发送到服务器执行,布署期必须利用打包工具将前端的源码和SQL抽取到后端。

适用场合 | Applications

MyServerless适用于原型、快速、简单业务开发,以及前后端都是同一个人开发的场合,因为MyServerless是没有API的,也就不存在前后端沟通问题,所以开发效率高。

不适用场合 | Not Applicable

MyServerless不适用于复杂业务开发(比如脚本源码超过50行),原因是因为目前不具备IDE插件可以调试存放在前端的Java脚本,调试效率低,如果业务复杂时,在调试上花的时间还不如直接让传统后端程序员提供API和文档。

通常网站的API是由绝大多数的简单CRUD和少部分的复杂业务API组成,大项目可以考虑用MyServerless结合传统API方式来开发,开启一个MyServerless服务支撑简单的CRUD功能,剩下的复杂业务依然由传统后端程序员提供API和文档,这样可以节省后端程序员大部分的开发工作量。

使用 | Usage

用实例来说明MyServerless的使用,以下示例直接在前端写SQL和Java脚本,实测通过,文件位于这里

<html>
<head>
<meta charset="utf-8">
<script src="/js/jquery-1.11.3.min.js"></script>
<script src="/js/jquery-ajax-ext.js"></script>
<script src="/js/myserverless-3.0.js"></script>
</head>
<body>
      
    <script>document.write($java(`return new WebBox("/WEB-INF/menu.html");`)); </script>
    <h2>Transaction demo, use jQuery</h2>
    
    <script> 
	  function getUserListHtml(){
		  var users=$qryMapList(`select * from account where amount>=? order by id`,0);
		  var html="User List:<br/>";
		  for(var i=0;i<users.length;i++) 
			  html+="User ID:" +  users[i].ID+", AMOUNT:"+ users[i].AMOUNT+"<br/>"; 
	      return html;		   
	  }
	</script>
	   
	<div id="msgid" class="msg"></div> 
	<p id="Users">
	    <script>document.write(getUserListHtml());</script>   
	</p>
	
	<section>
		<header>Account A</header>
		<div id="A" class="amount">
			<script>
				document.write($qryObject(`select amount from account where id=? and amount>=?`, 'A',0));
			</script>
		</div>
	</section>
	<section>
		<header>Account B</header>
		<div id="B" class="amount">
			<script>
			    account=$qryEntity(`com.demo.entity.Account, select * from account where id=?`, 'B');
				document.write(account.amount);
			</script>
		</div>
	</section>
	<script>
	  function transfer(from, to, money){ 
			var rst = $$javaTx(`#AccountTransfer         
					//参数说明   $1:账号A的id  $2:账号B的id $3:要转账的金额
					int money = Integer.parseInt($3);
					if (money <= 0)
					     return new JsonResult(0, "Error: Illegal input.");
					Account a = DB.entityLoadById(Account.class, $1);
					if (a.getAmount() < money)
					     return new JsonResult(0, "Error:No enough balance!");
					a.setAmount(a.getAmount()-money).update();
					DB.exe("update account set amount=amount+?",par(money)," where id=?",par($2));
				    return new JsonResult(200, "Transfer success!");
			        `, from,to,money); 
		  $("#msgid").text(rst.msg);	
		  if(rst.code==200) 
	 	      $("#msgid").css("background", "#dfb");
		  else 
                     $("#msgid").css("background", "#ffbeb8");
	      $("#"+from).text($qryObject(`select amount from account where id=?`,'A'));
 	      $("#"+to).text($qryObject(`select amount from account where id=?`,'B'));
 	      $("#Users").html(getUserListHtml());
		}
	</script>
	<section>
		<header>Transfer</header>
		<form onsubmit="return false" action="##" method="post">
			<input id="amount" name="amount" value="100" class="amount">
			<button name="btnA2B" value="true" onclick="transfer('A','B', $('#amount').val())">From account A to account B</button>
			<button name="btnB2A" value="true" onclick="transfer('B','A',$('#amount').val())">From account B to account A</button>
		</form>
	</section>
</body>
</html>

另外还有两个演示:
演示2:MyServerless结合Vue的使用。
演示3: 演示直接在前端进行表单的输入检查并保存到数据库

运行 | Dependency and Run

MyServerless分为server和core两个目录,server目录是一个示范项目,使用时只需要将server项目作一些修改,如更改数据库连接和重写签权逻辑,即可以用于实际开发 。core目录是内核源码,除非要定制后端,用户一般不需要关心。
在windows下点击server目录下的run_server.bat批处理,即可进入上例的演示界面,使用用户名demo、密码123登录。
演示项目是把Web和后端做在一起,实际开发时前端可以在单独的项目里远程开发,所有改动即时生效,不需重启后端。

方法说明 | Methods

在前端引入myserverless-3.0.js这个javascript库后,就可以直接在前端调用以下远程函数执行后端业务:

$java(String, Object...) 在后端执行Java脚本,第一个参数是Java脚本源码,后续参数是业务参数,在Java脚本源码里可以用$1,$2...来代表。  
$javaTx(String, Object...) 在后端执行Java脚本并开启事务,如果有异常发生,事务回滚。  
$qryObject(String, Object...) 将SQL查询结果的第一行第一列值返回,第一个参数是SQL,后面是SQL参数,下同  
$qryArray(String, Object...)  返回SQL查询的第一行数据,以Javascript数组对象格式返回  
$qryArrayList(String, Object...)  返回多行查询结果,以数组列表格式返回    
$qryTitleArrayList(String, Object...)  返回多行查询结果,以数组列表格式返回,第一行内容是各个列的标题  
$qryMap(String, Object...) 返回SQL查询的第一行数据,为Map 格式  
$qryMapList(String, Object...)  返回SQL查询的多行数据,为List<Map>格式  
$qryEntity(String, Object...)  返回第一行数据为实体对象,SQL写法是实体类名+逗号+SQL, 示例:$qryEntity(`a.b.Demo, select * from demo`); 
$qryEntityList(String, Object...)  返回多行数据为List<实体>对象,SQL写法是实体类名+逗号+SQL, 示例:$qryEntityList(`a.b.Demo, select * from demo`);   

注意以上远程函数调用的第一个参数是Java源码或SQL文本,要用键盘ESC下方的单引号括起来,这是Javascript的特殊单引号,支持多行文本。
以上方法都是自定义的,用户也可以自定义自己的方法。以上方法还可以用$$开头返回JSON对象。JSON对象有{code, msg, data, debugInfo} 4个字段,但debugInfo字段仅当服务端配置为debug_info=true时才有值。
MyServerless方法可以添加以下两类特殊语句:

  1. #xxxxx 形式的ID,用来自定义方法ID,如没有这个ID,方法缺省ID为"Default"。这个方法ID的命名很重要,在用户的签权类里,要根据这个ID来判断用户是否有权限执行这个方法
  2. import开头的语句,这个等同于标准的Java包引入语法
    例如下面这个方法调用定义了一个名为ReadUserAmount的方法ID,并引入了一个名为abc.DemoUser的Java包:
$java('#ReadUserAmount import abc.DemoUser; return new DemoUser().loadById($1).getAmount();', 'u1');   

开发和布署 | Develop & Deploy

在类根目录(项目的resources目录)下,有一个名为myserverless.properties的配置文件,可以进行配置,例如配置deploy目录、设定开发/生产阶段、设定develop_token和debug_inifo、打包时是否生成API等,详见它的注释。

开发阶段:MyServerless示范项目在服务端运行,它自带一个动态Java脚本编译功能,前端发来的SQL和Java脚本,被动态编译为实际的Java类,并执行这个Java类,最后返回JSON对象。

如果javascript方法前是两个$符号,如$$java,则返回一个JSON对象,它的data字段保存了返回结果。如果javascript方法前只有一个$符号,如$java,则返回的值直接就是Json的data字段。  

布署阶段:双击server目录下的批处理文件go-server.bat,即可将前端所有的SQL和原生Java片段抽取到服务端去,转变为Java源文件,原有前端的SQl和JAVA代码在转换后将成为类似于$callDeployed('Xxxx_C9GK90J27','A');之类的通过ID进行的调用,以防止非法访问, 实现安全性。
server目录下还有一个文件名为go-front.bat,这个是逆操作,可以将后端的Java代码移回到前端。

安全 | Security

在项目的myserverless.properties文件里,有以下关于安全的设计:
1.当stage设定为product阶段时,不再动态编译前端传来的SQL和Java片段,以实现运行期的安全。
2.当stage设定为develop时,deveop_token必须设定一个密码,在开发阶段会 执行前端传来的任意SQL和Java代码,后端会检查前端传来的develop_token密码与服务器的设定是否一致,否则拒绝访问,这样可以排除开发期的非法访问。
3.token_security设定,这是用来登记用户自定义的登录检查类,使用详见示范项目,MyServerless并没有集成第三方权限框架,所以需要用户根据自已的项目实现自定义的登录和token验证类。通常是根据token和方法ID来判定是否允许执行MyServerless远程方法。

常见问题 | FAQ

  • 安全上有没有问题?
    架构上没有安全问题,MyServerless通过token和方法ID,结合自定义签权方法来检查每一个方法ID的执行权限。当然用户写的签权方法或Java脚本中有可能出现安全漏洞,但这与本项目的架构无关。

  • 为什么采用Java作为业务脚本语言而不是Javascript/C#/Go等语言? 因为作者只对Java熟悉,没有精力象大厂的Serverless服务一样提供多种语言实现,以前做过Node.js的尝试,但最终还是决定放弃。用户如果对其它语言熟悉,也可以仿照MyServerless的思路编写自己的serverless服务,原理不复杂,无非就是源码保存在远程动态编译执行,布署时再抽取出来以保证安全。

  • 为什么默认server项目采用jSqlBox这么小众的DAO工具?
    因为jSqlBox是本人写的DAO工具,打广告用的,它的SQL写法很多。如果前端对jSqlBox不感冒,可以仿照示例改造成使用不同的DAO工具如MyBatis等。MyServerless重点在于提供了一个动态编译执行远程Java源码的框架和打包工具,并不拘泥于具体的某个Java或SQL技术。

  • (小鹏提出)Java写在HTML/Javascript里没有错误检查、语法提示,及重构功能,不利于复杂业务开发。
    这个将来可以通过开发IDE插件解决。但目前的解决办法是只能运行go-server.bat批处理将Sql/Java抽取成Java源码类,在Eclipse/Idea等IDE里找错、更正后再用go-front.bat批处理塞回到HTML里去,也可以干脆就不塞回去了,后者就对应传统的前后端分离API开发了。

  • 业务有变动,前端代码或SQL需要修改怎么办?
    开发期直接在前端修改Java代码或SQL即可,即改即生效,不需要重启后端服务器。布署时由运维布署并重启后端服务器。

  • 前端业务代码需要复用(如多处调用或测试)怎么办?
    需要复用的业务代码和SQL写在公共JavaScript库里,前端其它地方调用这些公共库里的方法。

  • 与GraphQL或XXX-API等项目的区别?
    GraphQL等项目着重于API及文档的创建。而MyServerless是直接在前端写Java脚本和SQL,参数和业务注释直接写在源码即可,根本就不创建API, 也不需要写文档。个人认为文档的作用应该是用来学习而不是沟通。
    如果一定要MyServerless生成API文档,可以在配置里加入api_export_file=xxx.html即可汇总所有前端源码和SQL成一个API文档,但这个文档仅用于复核,并不是开发必不可缺的文档。

相关开源项目 | Related Projects

期望 | Futures

如对MyServerless感兴趣请点个赞,或发issue提出完善意见

版权 | License

Apache 2.0

关注我 | About Me

Github
码云

展开阅读全文
6 收藏
分享
加载中
精彩评论
应用场景太少并且极其不安全... 没什么学习意义..
2021-07-19 22:20
1
举报
真不错,这样注入就更方便了
2021-07-19 19:14
1
举报
最新评论 (17)
html写java和sql,那安全问题怎么保证啊
2021-07-20 16:30
0
回复
举报
开发期由develop_token保证远程传来的Java和sql可被动态编译执行,布署后这些java/sql代码被转移到服务端去了,html里看不到源码了。布署期由token保证用户的登录身份,结合方法ID来签权。布署期不再动态编译运行远程代码。
2021-07-20 22:29
0
回复
举报
应用场景太少并且极其不安全... 没什么学习意义..
2021-07-19 22:20
1
回复
举报
除非你能找出漏洞来,否则就不要说不安全。
2021-07-19 23:09
0
回复
举报
都写在前端代码里了... 不是很轻易就找到可利用的漏洞了... 你不会以为是加密的吧???
2021-07-22 23:24
0
回复
举报
是加密了啊, 仔细看上面介绍。布署阶段:双击server目录下的批处理文件go-server.bat,即可将前端所有的SQL和原生Java片段抽取到服务端去,转变为Java源文件,原有前端的SQl和JAVA代码在转换后将成为类似于$callDeployed('Xxxx_C9GK90J27','A');之类的通过ID进行的调用。
2021-07-23 06:32
0
回复
举报
真不错,这样注入就更方便了
2021-07-19 19:14
1
回复
举报
用wasm体系这事就好办很多吧
2021-07-19 15:07
0
回复
举报
不是一个概念。webassembly是始终运行在前端的高运行速度语言,主要解决显示问题。而这个项目是(在开发期)把Java和SQL远程发送到后端执行的。
2021-07-19 15:16
0
回复
举报
我是说wasm体系:包含wasmer/wasi
2021-07-19 15:27
0
回复
举报
另外wasm可以实现在线编译
2021-07-19 17:19
0
回复
举报
通用和在线编译只是其中一部分,还是要看生态的,wasm还不成熟。MyServerless选用Java是因为这是后端主流语言,用的人多,生态全。
2021-07-19 22:58
0
回复
举报
node.js诞生之初也是这样,npm没几年就火起来了,现在趋势是人们希望有一个更快更轻量,更快,部署更省事,且安全跨平台的工具,那么显然wasm是满足上述条件,全球开发者也在跟进实现,实现应该会很快,java太重了,与其把开销花在jvm上,不如直接冲就完事了
2021-07-28 11:25
0
回复
举报
被jsp折磨的还不够么
2021-07-19 13:58
0
回复
举报
这个和jsp不一样的,比方说下面这行
$qryEntity(`select * from account where id=?`, userId);
这个userId是javascript变量,直接传到后端去了。而JSP是不能获取当前页的javascript变量的。
2021-07-19 15:09
0
回复
举报
为什么不用php这么折磨自己?为什么不直接用nodejs直接写?
2021-07-19 12:15
0
回复
举报
这个MyServerless是个极简开源项目,核心一共也就3000行代码而已。如果真在乎语种,用对应语言把这3000行代码的思路重新实现一遍也不难,就看想不想做了,反正我最近不打算支持其它语种。
你或者可以考虑大厂的serverless比如Lambda,目前支持Java, Go, PowerShell, Node. js, C#, Python, Ruby等语种,但大厂有大厂的问题,业务容易被绑死在他们的平台上。
2021-07-19 13:09
0
回复
举报
更多评论
17 评论
6 收藏
分享
返回顶部
顶部