后台服务对象(Service)使用参考

后台服务对象(Service)简介

后台业务服务对象(Service)用来受理客户端请求,并对请求数据进行校验和处理,并返回处理结果。所有业务服务对象必须继承系统Service类,并且必须实现process(req, res)方法。

任擎服务器接收并响应客户端请求的整个过程如下图所示:

详细过程说明如下:

1、客户端发起服务请求,并提供要请求的服务路径和请求参数;

2、任擎服务器对请求参数进行解析,然后将这些参数封装到一个名为req的对象中;

3、任擎服务器根据客户端请求的服务路径查找并加载该业务服务;如果没有找到对应的服务,则会向客户端返回错误;

4、任擎服务器判断该服务是否需要进行登录校验(根据服务的checkLogin属性值判断),如果需要,则根据客户端请求参数中发送的会话信息进行登录校验,如果校验失败,则向客户端返回错误信息,并结束服务;如果校验成功,则将当前会话的相关信息保存到req的session属性中;

5、如果该服务需要登录校验,并通过了校验,则继续判断该服务是否需要校验操作权限(根据服务的checkAuthority属性值判断),如果当前用户没有该服务的操作权限,则向客户端返回错误信息,并结束服务的执行;

6、创建一个名为res的服务器端响应对象,默认code=0;

7、然后调用服务的process方法,并将客户端请求对象req和服务器端响应对象res传入process方法;

8、在服务的process方法内进行相关的业务处理,并将响应数据保存到res对象中,最后调用服务的end方法结束服务的处理;

9、任擎服务器将服务器端响应对象res返回给客户端;

代码示例

业务服务类的示例代码如下:

var util = require('util');
var Service = require('Service');
var Dao = require('Dao');
var Delete = function() {
    Service.call(this);
};
util.inherits(Delete, Service);
module.exports = Delete;
Delete.prototype.process = function(req, res) {
    if(!req.id) {
        this.onLogicError(1, '请提供信息编号!');
        return;
    }
    var self = this;
    var dao = new Dao(this);
    dao.delete('id='+req.id, function() {
        self.end(res);
    });
};

代码说明如下:

第1行引用util,后面需要用util的inherits方法实现继承。

第2行引用Service类,所有业务服务类都需要继承该类。

第3行引用数据访问类。

第4-6行为当前业务服务类的构造方法,在该方法内调用父类Service的call方法进行初始化。

第7行调用util.inherits方法实现当前类对Service类的继承。

第8行设置module.exports,导出当前类,以便在其他文件中访问。

第9行往后的代码为当前类定义了一个名为process的方法,这个方法是所有业务服务类都需要实现的方法,这个方法有两个参数,第一个req是客户端的请求对象,存储了客户端传递的请求参数,第二个是res对象,是服务器端向客户端返回的响应对象,在里面可以存储需要传递给客户端的各种响应参数。

在process方法内,可以实现各种业务代码。其中,从req对象中获得请求参数用“req.参数名”的形式即可获得,向客户端返回响应参数时,直接用“res.参数名 = 参数值”的形式即可实现。

进行业务校验时如果出现业务服务错误,使用this.onLogicError方法可以向客户端返回错误信息,该方法的第一个参数是错误代码,第二个参数是错误描述。

需要进行数据库的相关操作时,创建数据访问类的实例对象,然后调用相应的增删改查方法即可,数据访问对象的这些方法都需要传递回调函数,在执行完数据库的相关操作后会执行这些回调函数。在回调函数内,如果还需要使用当前业务对象的相关属性或方法,就不能再用this关键字,而需要在调用数据访问方法之前先定义一个变量转存当前业务服务对象,例如用“var self = this” 这种方式定义了一个self变量,在回调函数内使用这个变量就可以访问当前业务服务对象。

在所有业务服务执行完毕后,必须调用业务服务类的end方法结束服务,否则该服务会一直运行,不会向客户端返回任何信息,直到出现超时错误。

传递给数据访问对象(Dao)相关方法的回调函数是异步执行的,如果业务服务类中用到了数据访问对象(Dao)的相关方法,则需要在回调函数内调用end方法结束服务,不能在回调函数外面结束服务,否则可能出现数据库操作还未完成时服务已经结束,造成意外错误。

属性列表

业务服务类具有以下属性:

属性名称 数据类型 说明
checkLogin 布尔 是否需要校验登录,默认为true,如果服务不需要登录就能访问,则在该服务的构造函数内将checkLogin属性设置为false
checkAuthority 布尔 是否检查操作权限,默认为true,如果该服务所有人都能访问,则在该服务的构造函数内将checkAuthority属性设置为false
connection 对象 当前服务对象的数据库连接对象
prevService 对象 使用pipe方法调用当前服务对象的上一级服务对象
sqlInject 对象 防SQL注入设置,包含两个属性:
check:bool型,当前业务服务是否要进行SQL注入检查,默认为true;
exclude:数组,用来设置当前业务服务的哪些请求参数不需要SQL注入检查,该参数只有在上面的check==true时才有效,默认为空数组,表示当前服务的所有请求参数都会检查。
任擎默认会检查所有请求参数,如果发现有可疑内容,会报错,包括一些特殊字符、sql关键字等,如果有参数确实需要包含这些内容,则可以通过设置服务对象的sqlInject参数禁用防SQL注入检查。

方法列表

end 结束服务

功能说明:

终止执行当前服务,在服务的业务执行完毕后,必须通过该方法结束服务,否则该服务会一直处于执行状态,不会向客户端返回处理结果。

如果业务服务内有各种判断和异步方法,一定要保证所有情况和路径执行完毕后都能结束服务,否则就会造成该服务的相关资源一直保持占用,包括网络和数据库连接等,时间长了后,大量未正常关闭的服务会造成任擎服务器异常崩溃。

方法原型:end(res)

输入参数:

参数名称 数据类型 可为空 说明
res JSON对象 返回给客户端的响应数据。

返回值:

示例代码:

this.end(res);

onError 抛出错误信息

功能说明:

抛出意外错误信息,同时结束当前服务。

方法原型:onError(err)

输入参数:

参数名称 数据类型 可为空 说明
err 错误对象或字符串 错误对象或错误描述字符串

返回值:

示例代码:

var id;
try {
  id = parseInt(req.id);
} catch(e) {
  this.onError(e);
  return;
}

onLogicError 抛出业务逻辑错误

功能说明:

抛出业务逻辑错误,同时结束当前服务。

方法原型:onLogicError(code, msg, req, res, cb)

输入参数:

参数名称 数据类型 可为空 说明
code 整数 错误代码,客户端根据代码判断具体是什么错误,以便进行相关处理。
msg 字符串 错误描述字符串。
req 对象 客户端请求对象。
res 对象 返回给客户端的服务器响应信息。

返回值:

示例代码:

if(!req.code) {
  this.onLogicError(1, '请提供用户编码!');
  return;
}

pipe 调用其他业务服务

方法原型:pipe(nextServiceClass, req, res, cb)

输入参数:

参数名称 数据类型 可为空 说明
nextServiceClass 对象 要调用的业务服务类
req 对象 客户端请求对象
res 对象 服务器响应对象
cb 函数 调用成功后需要执行的回调函数

返回值:

功能说明:

可以使用该方法调用其他业务服务,并将当前业务服务内的客户端请求对象req、服务器响应对象res和数据库连接等相关参数传给该服务。

多个服务可以使用pipe方法串联起来执行,在最后一个pipe的服务执行结束后,任擎会自动结束前面的服务。

示例代码:

req.appCode = 'task';
req.appDataId = taskId;
req.appDataType = 'send';
req.content = req.name;
req.message = '['+req.session.user.name+'] '+req.name;
this.pipe(server.loadModule('/app/sys/service/sendAppMsg'), req, res, function(receiveService, receiverId) {
  var receiveDao2 = new ReceiveDao(receiveService);
  receiveDao2.update({status:1, receive_time: new Date()},
    {where:'status=0 and task_id='+taskId+' and receiver_id='+receiverId});
});

process 执行业务逻辑

方法原型:process(req, res)

输入参数:

参数名称 数据类型 可为空 说明
req 对象 客户端请求对象
res 对象 返回给客户端的响应数据。

返回值:

功能说明:

执行后台服务的具体业务逻辑,所有业务服务类都要实现该方法。

示例代码:

Delete.prototype.process = function(req, res) {
    if(!req.id) {
        this.onLogicError(1, '请提供信息编号!');
        return;
    }
    var self = this;
    var dao = new Dao(this);
    dao.delete('id='+req.id, function() {
        self.end(res);
    });
};