web framework and web server for Node.js,support session,mysql database,file upload,build-in static file server


Keywords
web, context, framework, http, server, middleware, node, node.js, application-server, nodejs, nodejs-framework, nodejs-server, web-server, webframework
License
MIT
Install
npm install webcontext@1.0.19

Documentation

安装

  1. 安装node 8.0以上版本
  2. npm install --save webcontext

简介

webcontext不仅是一个nodejs web开发框架,它还是一个轻量的应用服务器,类似于IIS,tomcat,nginx, 它能提供像php、jsp一样的页面开发体验,集成了静态文件服务,页面路由,反向代理,数据库访问,session存取,文件管理,日志读写等web服务器必备的功能,提供一站式服务,让开发者专注于业务开发,不需要关注node.js底层的技术细节,不需要去选型express,koa各种中间件的用法, 能够让你编写最少的代码快速实现业务。特性如下:

  • URL请求自动映射到相应的js文件,类似于php,jsp的页面机制,不需要额外定义路由
  • 支持RestFul,支持url重写,无需修改任何代码即可实现对搜索引擎友好的页面地址。
  • 支持反向代理内网和外网的http接口(包括https)
  • 支持热更新,service目录下的文件修改后直接生效,无需重启node
  • 自动解析请求表单、JSON报文直接映射到request.data对象中
  • 内置文件上传,自动解析到request.files对象中
  • 支持 CORS 跨域,一行配置即可开启
  • 默认支持https(http 2.0)
  • 内置静态文件服务器,默认支持gzip压缩
  • 内置日志功能(基于log4js),站点请求自动写入access.log,可通过this.logger获取日志访问器
  • 内置模板引擎(基于ejs),模板文件与同名文件自动关联
  • 内置数据库访问(基于mysql),对CURD操作进行了封装,内置了分页查询和批量更新。
  • 实现了轻量的数据库 ORM映射,类似于hibernate, 可以通过this.models["表名"] 直接获取实体对象进行操作
  • 内置Session(使用进程内存实现,如果需要跨进程使用需要自行使用redis实现)
  • 可配置,所有运行参数都在web.config.json中定义,可通过this.config获取
  • 不需要中间件,所有功能都已内置,依赖极少,一键运行

快速开始

app.js

const {Application} = require('webcontext');
const app = new Application();
 
  1. 在app.js中引入webcontext,并实例化。
  2. 运行node app.js启动。

默认监听80端口,如果要改变监听端口,请在根目录的web.config.json文件中修改port属性(首次运行自动创建)

web.config.json

{
    "port":"80",  //http 端口号
    "index":"/index"  //默认页地址
}

hello,world示例页面代码如下:

/service/index.js

module.exports= {  
    onLoad() {   
        this.response.body="hello,world";
    }
}

也可以使用es6 class语法,可以获得语法智能感知提示。

const {Context}=require("webcontext")
module.exports=class Page extends Context{  
  onLoad() {  
     
      this.render("hello,world");
  }

}

可使用数据绑定渲染同名的ejs模板页,示例页面代码如下:

./service/index.js

module.exports= {  
    onLoad() {       
        var data=[
            {id:1,title:"javascript"},
            {id:2,title:"node.js"},
            {id:3,title:"mysql"}
        ];
        this.render({list:data});
    }
}

./service/index.ejs

<ul>
<% for(var i=0;i<list.length;i++){ %>
<li><%=list[i].title%></li>
<%}%>
</ul>
  1. 在项目目录service目录下建立index.js和index.ejs,index.js文件将自动处理/index路径的请求,代码如上所示
  2. 在onLoad方法中,调用this.render方法,传入一个对象做为数据源,将自动加载同名的扩展名为ejs的模板文件,渲染结果会直接输出到reponse.body中。也可以传入一个模板字符串而不使用实体的模板文件。
  3. 编写ejs模板,代码如上所示

URL映射

URL映射就是一个URL请求由哪块代码(类、函数)来处理,webcontext根据js文件路径自动处理URL映射,类似于jsp和php的页面机制,文件必须存放在/service目录下,支持多级子目录.

请求的映射文件示例:

http://localhost/index ----> /service/index.js

http://localhost/todo/list ----> /service/todo/list.js

http://localhost/todo/add ----> /service/todo/add.js

js文件必须使用exports导出一个对象,该对象必须实现onLoad方法。

也可以在application对象的onRequest添加全局的URL映射,支持正则表达式,下面的代码是在每个http请求的响应头中添加server字段:

const WebApp = require('webcontext');
const app = new WebApp();
app.onRequest(/.*/,function (ctx){
    ctx.response.headers["server"]="webcontext";

});

请求处理

Get请求

request.query 获取get参数

Post请求

request.body 获取原始的http body

request.data 获取post数据,同时支持json数据和表单数据两种格式。

post 表单数据 (jQuery):

$.post("/todo/add/",{id:1,title:"hello",status:0})

post json数据 (jQuery):

$.ajax({
    type : "POST",
    url:"/todo/add/",
    dataType:"json",
    data:{id:1,title:"hello",status:0}
})

/service/todo/add.js 接收post数据代码:

module.exports= {  
    onLoad() {  
        var data=this.request.data;
        this.response.body=JSON.stringify(data);      //{id:1,title:"hello"}
    }
}

响应处理

方式一:this.response.body

this.response.body="hello,world"

方式二:this.render(string)

this.render("hello,world")

方式三:this.render(templateString,data)

this.render("hello,<%=message%>",{message:"world"})

方式四:this.render(data)

调用render方法,传入object对象,将使用与当前文件同名的扩展名为.ejs的文件做为模板进行渲染。

service/hello.js

this.render("hello,<%=message%>",{message:"world"})

service/hello.ejs

<html>
hello,<%=message%>
</html>

反向代理其它服务器

可以轻松实现类似于nginx的反向代理,透传其它服务器的接口,可用于制作爬虫、mock服务器等应用。支持https接口,支持替换cookie domain,支持cors跨域,支持添加referer,在onProxy回调函数中可以修改header和body响应正文,注:为了保证性能,onProxy回调函数中得到的body是Buffer类型,如果目标接口使用gzip压缩,需要自行调用zlib解压缩报文。

app.proxy({
    "/myapp": {
        target: "https://www.domain.com", 
        referer:"https://www.domain.com",
        rewrite:  "/",
        onProxy:function(result) {

            if(result.path.indexOf("/login")>=0){ //匹配url规则
                result.code=200; //修改状态码
                result.headers["content-type"]="text/json" //修改http头
                var bodyStr=Buffer.toString(result.body) //解析body
                result.body=Buffer.from("追加内容"+bodyStr) //修改body     

            }

            result.headers["Access-Control-Allow-Origin"]="*" //添加cors跨域
            
        }
    }
})

多种数据库支持

支持mysql与sqlite两种数据库,在配置文件中type字段定义数据库类型

mysql 数据库配置如下:
{
    port:"8080",
    database:{ 
        type:"mysql",
        host:'127.0.0.1',//db server ip
        port:'3306',// db server port
        user:'root', //account
        password:'123456', //password
        database:'todo_db'  //database name or schema's name
    }
}

sqlite配置如下:

{
    port:"8080",
    database:{ 
        type:"sqlite",
        database:'./todo.db'  //database file name
    }
}

同一个应用程序支持多个数据库

只需要将配置中的database定义为数组即可

 "database":[
    { 
        "type":"sqlite",
        "database":"./data/album.db"
    },
    { 
        "type":"sqlite",
        "database":"./data/words.db"
    }],

在应用程序中可通过this.databaseList获取所有的数据库连接对象,将database属性修改一下就可以切换不同的数据库连接:this.database=this.databaseList[1];

数据库CURD操作

webcontext内置支持mysql数据库,可以非常方便的进行CURD操作。

数据库连接字符串在web.config.json中配置,配置好后,在程序启动时将自动连接数据库。

使用this.database获取数据库操作对象,该对象提供select,insert,update,delete,query几个方法对数据库进行操作。

注:MySQL8.0以上版本密码认证协议发生了改变,需要用mysql workbench执行如下代码: ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '你的密码';

web.config.json

{
    port:"8080",
    database:{ 
        host:'127.0.0.1',//db server ip
        port:'3306',// db server port
        user:'root', //account
        password:'123456', //password
        database:'todo_db'  //database name or schema's name
    }
}

insert

调用database.insert(tableName,columns)插入数据库,tableName参数为表名称,columns为插入的数据,例如{id:1,title:"hello"}

/service/todo/add.js 代码:

module.exports= {  
    onLoad() {  
        var data=this.request.data;     //{id:1,title:"hello"}
        this.database.insert("todo",data);  
    }
}

replace

与insert用法相同,将生成replace into ...的sql 语句执行。前提条件是数据表相应的字段必须设置unique约束。

update

用法:database.update(tableName,rows,where),rows可以是一个对象,也可以是一个数组,如果是数组,则批量更新多行记录 如果rows中定义了id参数,则不需要传入where 条件,自动使用id字段做where条件,否则必须传为where条件约束更新的记录

module.exports= {
    onLoad() {
        this.database.update("todo",{
            id:this.request.data["id"],
            title:this.request.data["title"],
            status:0
        }).then((result)=>{
            this.render(JSON.stringify({msg:"update ok!!!"}))
        })
        
        
    }
}

delete

用法:database.delete(tableName,where) 支持批量删除,例如批量删除id为1,2,3,4的四条记录:database.delete("todo",{id:[1,2,3,4]})

select

调用database.select(tableName,where,options) 查询数据库

where 参数默认使用and关系,例如{id:1,title:'test'} 生成的sql 为where id=1 and title='test',如果不需要where 条件请将此参数置为null。

options的数据结构:

{
    columns:["id","title"], //可选参数,定义返回的列
    orderBy:"createTime", //排序,逆序为createTime desc
    pageIndex:1,  //分页查询页码
    pageSize:20  //分页查询页大小
}

返回值为promise对象,用async/await直接接收返回的结果集。也可以使用传统的then方法传入回调函数获取结果集。 如果要使用复杂的查询条件,请使用database.query(sql,params) 传入自定义的sql执行

module.exports= {
    async onLoad() {
        var result=await this.database.select("todo",{orderBy:"createTime desc "})
        this.render({list:result});    
    }
}

/service/todo/list.js 代码:

module.exports= {
    async onLoad() {
        var result=await this.database.select("todo",{
            where:{status:0},
            orderBy:"createTime desc "
        })
        this.render({list:result});    
    }
}

数据库 ORM 映射

webcontext可以非常方便的使用ORM数据实体映射,使数据库业务代码完全不依赖sql语句。 可以通过this.models["表名"] 获取实体对象,如:this.models["todo"] , 使用ORM 映射之前,需要在web.config.json中定义数据库连接,并在models字段中定义数据表的信息,每个表一个属性,子属性中必须要定义的两个字段是table和primary分别表示表名和主键名。后续的版本将计划实现自动生成ORM的配置文件,进一步简化业务开发。

 "models":{
        "todo":{
            "table":"todo_list",
            "primary":"id",
            "orderDefault":"createTime desc",
            "columns":["id","title","createTime"],
            "query":{} //自定义查询,尚未实现
        }
    }

获取数据实体对象,使用fetch

module.exports= {
    async onLoad() {
        var ToDo=this.models["todo"];
        var todo=await ToDo.fetch(5);
        this.render({list:todo});    
    }
}

更新数据实体对象,使用save

module.exports= {
    async onLoad() {
        var ToDo=this.models["todo"];
        var todo=new ToDo(1); //获取主键值为1的记录
        await todo.save();
        this.render({code:"success"});    
    }
}

删除数据,使用delete

     var todo=new ToDo({id:5});
     todo.delete();

Session存取

为了简化使用,session存储在当前进程内存,不支持跨进程或分布式访问。

Session 写入

module.exports= {
    async onLoad() {    
        this.session["userName"]="windy";   
 
        
         
    }
}

Session 读取

module.exports= {
    onLoad() {       
        console.log(this.session["userName"] )
    }
}

其它常用操作

重定向

response.redirect("/page1.htm")

文件下载

response.writeFile(localPath)

或者 response.writeStream(fs.createReadStream(fileName))

文件上传

request.files

请求头读取

request.headers["fieldName"]

响应头写入

response.headers["fieldName"]="fieldValue"

cookie读取

request.cookies["userName"];

cookie写入

response.cookies["userName"]="windyfany" //会话cookie

response.cookies["userName"]={ value:"windy", domain:'localhost', path:'/', maxAge:10006060*1, expires:new Date(), httpOnly:false }]

静态文件服务器

/www目录中存储静态文件,如html,css,图片,js等。 例如: 访问http://localhost/css/style.css时对应访问的文件路径是/www/css/style.css 访问http://localhost/images/logo.jpg时对应访问的文件路径是/www/images/logo.jpg

目录结构

service目录存放url映射处理类,该目录存放的js文件实现onLoad方法。 www是静态文件服务器的根目录,该目录存放前端的静态资源文件如css,图片,html等。 web.config.json 是配置文件,用于配置web服务端口号,数据库连接字符串,上传文件存放目录等

|-- service                          
|   ┠-index.js      //auto handle the http path:/index
|   ┗-index.ejs     //html template file of index.js       
|-- www         //static htm,js,css,image files
|   ┠-images
|   ┠-css
|   ┗-js
|-- web.config.json          // config file                   

配置文件

配置文件存放于项目根目录下web.config.json文件中,首次运行会自动生成,可配置web服务端口号,数据库连接字符串,上传文件存放目录等

{
    port:"3000",
    index:"/index",
    sessionKey:"my_session_id",
    uploadDir:"./upload",
    database:{ 
        host:'127.0.0.1',
        port:'3306',
        user:'root',
        password:'windyfancy123',
        database:'todo_db'
    }
}