![Oracle Drags Its Feet in the JavaScript Trademark Dispute](https://cdn.sanity.io/images/cgdhsj6q/production/919c3b22c24f93884c548d60cbb338e819ff2435-1024x1024.webp?w=400&fit=max&auto=format)
Security News
Oracle Drags Its Feet in the JavaScript Trademark Dispute
Oracle seeks to dismiss fraud claims in the JavaScript trademark dispute, delaying the case and avoiding questions about its right to the name.
class2api
Advanced tools
the lib for mapping your javascript class to api service. support map Class's static methods merge into graphQL resolver
Table of Contents generated with DocToc
//先全局安装class2api
$ npm i class2api -g
//从脚手架初始化新项目
$ class2api init
根据提示输入
$ class2api init
脚手架模版类型:
- base ——精简型,不带before/after拦截器
- normal ——普通型,带before/after拦截器、API缓存机制
- super ——增强型,带before/after拦截器、API缓存机制、数据库访问
- admin ——管理后台权限认证型,super的基础上附带管理身份权限校验
选择以上哪种模版?(base/normal/super/admin): super
给创建的新项目取个名字(superDemo27366): class2api_scaffold_super
远程提取模版文件 ...
请配置数据库链接:
数据库IP(127.0.0.1):
数据库端口(3306):
数据库访问用户(root):
数据库密码(默认为空):
创建新数据库的名称/存在则忽略(class2api_demo): class2api_oooooo
数据库编码(utf8_general_ci):
√ 数据库配置成功!
√ 项目创建成功!
开始体验:
$ cd class2api_scaffold_super && npm install && npm start
最终:
//curl请求
$ curl -d 'name=huangyong' 'http://127.0.0.1:3002/a/hello'
//运行单元测试,需全局安装mocha: $ npm i mocha -g
$ mocha test/test.run.js
```javascript import {GKSUCCESS} from 'class2api' class ClassA { constructor() { throw '静态业务功能类无法实例化' } static async hello({name}) { return {message: `this is a message from Api: got name [${name}]`} } static async deleteArticle({name}) { //TODO:...删除文章的操作 return GKSUCCESS() } } export default ClassA ```
createServer({
config:{
apiroot:'/', //[可选],挂载微服务的根路径,如:apiroot设置为'apiv1",则业务类ClassA的method1方法对应的访问入口为: http://yourdomain/apiv1/classa/method1
redis, //[可选],内置API方法缓存所使用的redis实例配置参数,使用clearCache、cacheAble修饰器时必须
cros:true, //[可选],是否允许跨域访问
cros_headers:[], //[可选],允许跨域访问时的headers白名单
cros_origin:[], //[可选],允许跨域访问时的授权源配置,默认为*。当传入有效的cros_origin参数时,以cros_origin中指定的为准
frontpage_default: '' //[可选],放在API方法内部获取前端的域名,与从前端请求传过来header中的frontpage合并,优先获取客户端的,其实采用此默认值。最后封装为标准url对象并绑定到API方法回调的params对象的___frontpageURL属性上
},
modelClasses, //必需,映射的业务类列表,如:[ClassA, {model:XXX,as:'abc'}],建议以mode-as的方式修改业务类暴露的访问路径,以隐藏实际的类命名(建议)
beforeCall, //[可选],API接口请求之前的拦截事件,可以修改、监听post信息,以及身份的验证判断
afterCall, //[可选],API接口请求完成后的拦截事件,可以修改、监听返回结果,典型场景就是记录请求的日志
custom:(expressInstence)=>{
return expressInstence
}, //[可选],对expressInstence实例进行自定义扩展,注:微服务通常是无状态的,所以不建议增加session机制
method404 //[可选],自定义的express的路由中间件,在404场景时,可输出自己定制的返回值
})
class2api对所有请求的返回数据结构,统一为:
{
err,
result
}
其中err代表内部异常或错误,凡是GKErrors和GKErrorWrap result抛出的错误都会捕捉err ,而result保存的是业务类静态方法的执行返回结果,且约定为必须为对象结构,不能是数字、字符串、布尔值等简单数据类型
*特殊情况:如果你的系统有特殊原因,需要接口返回自定义的数据结构,可以使用以下方式来控制反转:
class ClassB {
constructor() {
throw '静态业务功能类无法实例化'
}
static async customResponseResultStruck() {
//TODO:.....
//class2api内部约定,如API方法返回的是Function,则框架会调用函数并把其运行结果返回给客户端,以实现自定义特殊的response结构
return () => {
return {data: {name: 'huangyong'}, errorCode: 123}
}
}
}
class ClassB {
constructor() {
throw '静态业务功能类无法实例化'
}
static async customResponseResultStruck() {
//TODO:.....res.write(fileStream)
//class2api内部约定,如API方法返回__customResp标记,则框架终止res操作,由方法内部自行控制res操作
return {__customResp:true}
}
}
//创建微服务对象
createServer({
config:{
apiroot: '/api_v2',//如需要时,可以指定API服务的起始路径,特别是在映射路径的方式中
},
//...
})
//创建微服务对象
createServer({
config:{
cros:true,
cros_headers:['customHeader'],
cros_origin:['http://web.domain.com'],
},
//...
})
//init.js
import {setting_redisConfig} from 'class2api'
setting_redisConfig({
host: "127.0.0.1",
port: 6379,
cache_prefx:'dev_class2api_',//必须的参数,且针对每个应用不同配置,以避免各应用之间发生key碰撞与覆盖
defaultExpireSecond:10*60 //可缺省,class2api内部的默认混存时长为1分钟,可自定义
})
let cache = getRedisClient()
cache.set('keyABC','this is message!', (err,data)=>{})
await cache.setAsync('keyABC','this is message!')
return {
_gankao: 1,//固定标志位,以区别普通的error对象
code: errCode,//错误码,内置错误类型为负数,通过GKErrorWrap自定义的code请务必为正数
message: `...`,//错误信息
more: ''//详细的错误信息
}
import {parseAdminAccountFromJWToken} from "class2api/rulehelper";
@modelSetting({
__Auth:async ({req})=>{
//后台的用户验证,解析header中的jwtoken信息,调用class2api/rulehelper的解析,注意与非后台常规用户验证的区别
let jwtoken = req.header('jwtoken') || ''
return await parseAdminAccountFromJWToken({jwtoken})
},
__ruleCategory:{
name:"文章管理",
desc:"对文章进行新建、编辑、删除等操作"
}
})
class ArticleManager {
constructor() {
throw '静态业务功能类无法实例化'
}
}
class GKModelA {
@cacheAble({
cacheKeyGene: ({name}) => {
return `getArticle-${name}`
}
})
static async getArticle({uID, name}) {
return {message: `getArticle.${name},from user. ${uID}`}
}
}
Q:如何判断某次API请求的结果是命中了缓存? A:命中了缓存的调用,在其请求的返回结果中,带有__fromCache=1属性,如:
{
err:null,
result:{
name:'huangyong',
__fromCache:1 //额外的输出标记
}
}
Q:如果需要在某次调用接口时强制禁用(绕过)API缓存机制? A:传入__nocache=1参数,组件内部即会判断并绕过缓存,示例如:
let options = {
uri: remote_api,
body: {
fID:123,
__nocache:1
},
json: true,
}
let {body} = await request.postAsync(options)
//常规的控制方式
class GKModelA {
@clearCache({
cacheKeyGene: (args) => {
let {aID} = args[0]
return `article-${aID}`
}
})
static async deleteArticle({aID}) {
//...
return GKSUCCESS()
}
}
//需要清除多个关联key时,可使用反转控制机制
class GKModelA {
@clearCache({
cacheKeyGene: (args) => {
return ''
}
})
static async deleteArticle({aID, __cacheManage}) {
//...
if(__cacheManage){
await __cacheManage.delete(`article-${aID}`) //删除文章缓存
await __cacheManage.delete(`articleCategory-1`) //删除文章类别的缓存
}
return GKSUCCESS()
}
}
import {GKErrors} from 'class2api/gkerrors'
GKErrors._TOKEN_PARSE_FAIL //token解析失败
GKErrors._RULE_VALIDATE_ERROR //权限认证过程中发生异常
GKErrors._TOKEN_LOGIN_INVALID //请先登录
GKErrors._NOT_ACCESS_PERMISSION //无访问权限
GKErrors._NOT_SERVICE //功能即将实现
GKErrors._PARAMS_VALUE_EXPECT//参数不符合预期
GKErrors._NO_RESULT //无匹配结果
GKErrors._SERVER_ERROR //服务发生异常
GKErrors._NOT_PARAMS //缺少参数
//DemoUser.js
import {TableSetting} from 'class2api/dbhelper';
export default (sequelize, DataTypes)=> {
const User = sequelize.define('demouser', {
name: {type: DataTypes.STRING, allowNull: false, defaultValue: '', comment: `用户的姓名`},
birthday: {type: DataTypes.DATE},
...TableSetting.extendDateTimeVirtualFields(DataTypes, ['birthday'])
}, {
...TableSetting.tabelOption,
classMethods: {
associate: function (DataModel, ass) {
//User.belongsTo(DataModel.Student)
}
},
comment: '学生信息'
});
return User;
}
import {DBModelLoader} from 'class2api/dbhelper'
import _config from './config'
import DemoUser from './tables/DemoUser'
//模型定义,在aloader.init内部会动态加载指定的定位文件,替换为真实的object的value值
export const DataModel = {
DemoUser: DBModelLoader.define(DemoUser),
}
//绑定模型关系时,可能需要定义的别名
export const ass = {
subComment: "subComment",
replyToUser: "replyToUser"
}
(async()=>{
await DBModelLoader.INIT(_config.mysql, {model:DataModel, ass})
})();
fn:Function
sequezeli的聚合函数引用
col:Function
sequezeli的列函数引用
literal:Function
sql语句字面量包装函数,确保sequelize不解析此SQL字符串
where:Function
sequezeli的where函数的引用
createTransaction:Function 创建一个sequezeli事务
在 class2api/rulehelper 下,适用于后台管理系统的身份检验、权限校验的辅助函数库
exports.config = {
name: 'courseService',
admin_rule_center: {
auth:"http://127.0.0.1:3002/gkrulemanager/auth", //管理用户的身份验证接口
validator: "http://127.0.0.1:3002/gkrulemanager/validate", //管理用户对指定权限的验证接口
register: "http://127.0.0.1:3002/gkrulemanager/register" //权限配置表的上传注册接口(待完善),在IDE环境下使用
}
}
修饰器向权限中心的权限校验接口(config.admin_rule_center.validator)提交以下参数信息: 1、jwtoken:jsonwebtoken版的管理员身份签名,从req参数中提取,并在header中发送,权限中心会识别解密jwtoken,从而分辨身份并校验权限 2、categoryName:权限组名称,从modelSetting修饰器的配置信息中获得 3、categoryDesc:权限组的描述信息,从modelSetting修饰器的配置信息中获得 4、ruleName:权限名称,从accessRule修饰器函数的参数中获得 5、ruleDesc:权限的描述信息,从accessRule修饰器函数的参数中获得 6、codePath:代码路径,格式如:ClassA.methodA,在框架内部获得 7、sysName:调用方系统名称,在class2api.config.js中配置
class GKModelA {
constructor() {
throw '静态业务功能类无法实例化'
}
@accessRule({ruleName: '删除文章', ruleDesc: '对文章进行删除'})
static async deleteArticle({aID}) {
//...
return GKSUCCESS()
}
}
在class2api/testhelper中,测试辅助函数库,为了接近真实环境,以及确保接口调用的全流程,class2api的单元测试原则上都以http调用的方式执行,而避免用类调用。 注:当需要测试业务功能类本身时,还是可以在业务类层面直接调用,以避开微服务框架以及http通讯的干扰
//testhelper的最小化案例
import {ApiDesc, WebInvokeHepler, setApiRoot, save2Doc} from 'class2api/testhelper'
let _run = {
accounts: {
user1: {
token: 'token-111'
}
}
};
//通过setApiRoot,除了本地开发环境,还可以调用测试环境、正式环境的接口,这会带来极大的线上排查与校验的便利性
const remote_api = process.env.ONLINE==='1'? `https://comment_api_test.gankao.com`
:(process.env.ONLINE==='2'? `https://comment_api.gankao.com`
:`http://127.0.0.1:3002`);
//配置远程请求endpoint
setApiRoot(remote_api);
describe('接口服务', function () {
after(function () {
save2Doc({save2File:'api.MD'})
});
it('/a/hello', async () => {
let response = await WebInvokeHepler(_run.accounts.user1)('/a/hello',
{name: "haungyong"},
ApiDesc(`hello测试方法`)
)
let {err, result} = response
let {message} = result
message.lastIndexOf('haungyong').should.be.above(-1)
})
})
FAQs
the lib for mapping your javascript class to api service. support map Class's static methods merge into graphQL resolver
We found that class2api demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 0 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Oracle seeks to dismiss fraud claims in the JavaScript trademark dispute, delaying the case and avoiding questions about its right to the name.
Security News
The Linux Foundation is warning open source developers that compliance with global sanctions is mandatory, highlighting legal risks and restrictions on contributions.
Security News
Maven Central now validates Sigstore signatures, making it easier for developers to verify the provenance of Java packages.