JianghuJS-协议数据结构
学习目标
- 学习jianghujs应用协议概念和规范
- 前端页面进行数据交互练习
1. 应用协议特点
- 不同端之间所传递的数据,江湖JS所做的规范与设计
- 应用协议的设计上,进行了统一和简化
- 能够做到在不同通讯通道间无缝切换
2. Resource请求
在江湖JS里,一个HTTP请求/Socket请求对应数据库_resource
表中的一行数据。
我们把请求resource 分成两类:
- sql resource: 后端根据请求组装出sql,执行并将结果返回给前端
- service resource: 后端根据请求找出
/app/service
下对应的service,执行对应的方法并将结果返回给前端
3. Resource请求 & Resource响应
- 请求:
/${appId}/resource
- 请求方式:
POST
- 请求头:
- 'Content-Type': 'application/json'
- 'Accept': 'application/json'
- 请求body
where offset limit参数只有在 sql resource场景有用
协议字段 类型 描述 packageId String 必填✅ 协议包的唯一id; 可以使用时间戳 + 随机数生成; 比如: 1622720431076_3905352 packageType String 必填✅ 协议包类型; 比如:'httpRequest', 'socketForward', 'socketRequest' appData Object 必填✅ 协议包数据 --appId String 必填✅ 应用ID; 比如: demo_xiaoapp --pageId String 必填✅ 页面ID; 比如: demoPage --actionId String 必填✅ 操作ID; 比如: selectStudentList --authToken String 必填✅ 用户令牌 --userAgent String 选填 客户端浏览器信息; 通过 window.navigator.userAgent
获取--where Object 选填 where条件; 比如: { name: '张三丰', classId: '2021-01级-02班' } --whereIn Object 选填 where in查询条件; 比如: { name: ['张三丰', '张无忌'] } --whereLike Object 选填 where 模糊查询条件; 比如: { name: '张' } --whereOptions Array 选填 where条件 ; 比如: [['name', '=', 'zhangshan'],['level', '>', 3],['name', 'like', '%zhang%']] --whereOrOptions Array 选填 where or 条件; 比如: [['name', '=', 'zhangshan'],['level', '>', 3],['a', 100]] --offset Number 选填 查询起始位置; 比如: 0 --limit Number 选填 查询条数,默认查所有; 比如: 10 --orderBy Array 选填 排序; 比如: [{ column: 'age', order: 'desc' }] --actionData Object 选填 操作数据,比如:要保存或更新的数据... { name: '张三丰', level: '03' } - 响应body
字段 类型 描述 packageId String 必返回✅,请求唯一标识。 packageType String 必返回✅,标识数据包类型; httpResponse, socketResponse status String 必返回✅,标识业务状态; success: 成功, fail: 失败 timestamp String 必返回✅,响应时间, E.g: 2021-05-24 17:56:59,408" appData Object 必返回✅, sql resource
orservice resource
执行后的结果--appId String 必返回✅ 应用ID; 比如: demo_xiaoapp --pageId String 必返回✅ 页面ID; 比如: demoPage --actionId String 必返回✅ 操作ID; 比如: selectStudentList --errorCode String 错误码, 当status==='fail'时返回, E.g: token_expired, request_body_invalid --errorReason String 错误码说明, 当status==='fail'时返回, E.g: token失效, params字段字段却失 --errorReasonSupplement String 错误码说明 补充, E.g: "id must not be null" --resultData Object 必返回✅ resource请求的响应数据
参考用例
const package = {
packageId: '123456',
packageType: 'httpRequest',
appData: {
appId: `${window.appInfo.appId}`,
pageId: 'protocolDemo',
actionId: 'insertItem',
userAgent: 'demo_userAgent',
authToken: localStorage.getItem(`${window.appInfo.appId}_authToken`),
actionData: {
studentId: 'G00003',
classId: '2021-01级-02班',
gender: 'male',
level: '02',
name: '小虾米',
},
where: { 字段A: 'A', 字段B: 'B' },
whereIn: { name: ['张三丰', '张无忌'] },
whereLike: { 字段A: 'A', 字段B: 'B' },
whereOptions: [['name', '=', 'zhangshan'],['level', '>', 3]],
whereOrOptions: [['name', '=', 'zhangshan'],['level', '>', 3]],
offset: 10,
limit: 20,
orderBy: [{ column: 'email' }, { column: 'age', order: 'desc' }],
}
};
const result = await axios(package);
const rows = result.data.appData.resultData.rows;
console.log('rows', rows);
jianghuAxios用例
{% include 'common/jianghuAxios.html' %}
<script>
const result = await window.jianghuAxios({
data: {
appData: {
pageId: 'protocolDemo',
actionId: 'insertItem',
actionData: {
studentId: 'G00003',
classId: '2021-01级-02班',
gender: 'male',
level: '02',
name: '小虾米',
}
}
}
});
const result = await axios(package);
const rows = result.data.appData.resultData.rows;
console.log('rows', rows);
</script>
4. 配置数据示例
1. resource sql
场景
- 方式1:
resource sql
请求中添加where过滤数据
// 只查询登录用户相关的数据
await window.jianghuAxios({
data: {
appData: {
pageId: 'backendSearchDemo',
actionId: 'selectItemList',
where: { userId: "G00001" }
}
}
})
- 方式2:通过配置
_resource
表中resourceData.where
过滤数据,支持where
、whereIn
、whereLike
、whereOptions
、whereOrOptions
{
"table": "student_basic",
"operation": "select",
"whereOrOptions": [["classId", "like","%2021-01级-02班%"],["bodyHeight", ">", 172]]
}
- 方式3:通过配置
_resource
表中的resourceHook
实现sql where
条件追加
class StudentService extends Service {
/**
* 查询学生列表BeforeHook:只查询当前用户添加的学生
*/
async selectStudentItemBeforeHook() {
const { userId } = this.ctx.userInfo;
this.ctx.request.body.appData.where.operationByUserId = userId;
}
}
module.exports = StudentService;
resourceHook | pageId | actionId | desc | resourceType | resourceData |
---|---|---|---|---|---|
{"after": [], "before": [{"service": "student", "serviceFunction": "selectStudentItemBeforeHook"}]} | studentManagement | selectItemList | ✅查询列表 | sql | { "table": "student", "operation": "select" } |
- 方式4:通过配置
_resource
表中的accessControlTable
实现用户维度的resourceData
覆盖_resource表
id accessControlTable resourceHook pageId actionId desc resourceType appDataSchema resourceData requestDemo responseDemo 1 access_control_student_basic backendSearchDemo selectItemList 查询列表 sql { "table": "student_basic", "operation": "select" } access_control_student_basic表
id userId username resourceData 1 userId 洪七公 { "table": "student_basic", "operation": "select", "whereOrOptions": [["classId", "like", "%2021-01级-02班%"],["bodyHeight", ">", 172]]}
2. resource servcie
场景
- 方式1:自定义代码逻辑,实现数据过滤
const { actionData } = this.ctx.request.body.appData;
const { classId } = actionData;
await this.app.jianghuKnex('student_basic').where({classId}).select()
- 方式2:通过配置
_resource
表中的resourceHook
实现数据过滤, 然后service
中直接使用resourceHook
查询出来的数据'use strict';
const Service = require('egg').Service;
class StudentService extends Service {
async beforHook() {
const studentInfo = await this.app.jianghuKnex('student_basic').where({studentId: this.ctx.userInfo.userId}).first();
const studentList = await this.app.jianghuKnex('student_basic').where({classId: studentInfo.classId}).select();
this.ctx.beforHookData = { studentList }
}
}
module.exports = StudentService;
// _resource表中的resourceHook配置如下
{ "before": [{ "service": "student", "serviceFunction": "beforHook" }], "after": [] }
小结
本文介绍了Jianghu框架中的数据权限配置,数据权限指的是不同用户查询Resource获取的数据不同。文章介绍了两种使用数据权限的方式,分别是在resource sql场景中添加where过滤或通过配置_resource表中的resourceData.where过滤;在resource service场景中自定义代码逻辑或通过配置_resource表中的resourceHook实现数据过滤。
课后练习:
尝试给W00001的student页面