ws4sqlite是通过http进行sqlite操作的工具. 类似于 PocketBase这种,将数据库与其它分开,可以实现于前端的分离.
但我依然不是太明白分享sqlite这种小型数据库的理由.似乎作者也在尝试支持其它数据库类型.
我能想到的好处就是便于稍大的团队分工: 将数据库的工作交给其它人来完成.或者在数据库没有搭建完善之前,“虚拟"返回数据.
象微服务这种开发理念,也越来越多团队在反省. 小团队不要想当然的微服务,用一些没有实际意义的概念.
ws4sqlite其实就是将数据服务放到服务器上,前端就可以通过认证和sql进行查询.它是通过REST来实现,就意味着javascript也可以直接调用数据库.
另外,它也实现了一个简单的静态http服务功能.
如果它能实现其它数据库的REST,或许意义更大一些.
运行: ws4sqlite –db mydatabase.db 提供一个对mydatabase.db的操作.此文件可以自动建立.
同时提供一个静态web服务: ws4sqlite –db mydatabase.db –serve-dir ..
同时提供多个数据库, 其中内存数据库mem1,且搜索配置文件meme1.yaml
ws4sqlite –db /file1.db –mem-db mem1:/mem1.yaml –mem-db mem2
这样就可以对三个数据库进行操作.当然也可以带多个–db
–db myFile.db:./myFileConfig.yaml 这为myFile.db指定了一个配置文件.
如果有与数据库名相同的yaml,它也会默认加载为配置文件.例如上面的 myFile.yaml
服务器建立后,就可以Post请求了.
地址: http://127.0.0.1:12321/mydatabase (这里以数据库名为ID,即mydatabase)
请求:
{
"transaction": [
{
"statement": "CREATE TABLE TEST_TABLE (ID int primary key, VAL text)"
}
]
}
多个请求:
{
"transaction": [
{
"statement": "CREATE TABLE TEST_TABLE_2 (ID int primary key, VAL text)"
},
{
"statement": "INSERT INTO TEST_TABLE_2 (ID, VAL) VALUES (1, 'hello')"
}
]
}
{
"transaction": [
{
"statement": "INSERT INTO TEST_TABLE (ID, VAL) VALUES (1, 'hello'), (2, 'world')"
},
{
"query": "SELECT * FROM TEST_TABLE ORDER BY ID ASC"
}
]
}
返回信息:
{
"results": [
{
"success": true,
"rowsUpdated": 2
},
{
"success": true,
"resultHeaders": [
"ID",
"VAL"
],
"resultSet": [
{
"ID": 1,
"VAL": "hello"
},
{
"ID": 2,
"VAL": "world"
}
]
}
]
}
查询:
{
"transaction": [
{
"statement": "INSERT INTO TEST_TABLE (ID, VAL) VALUES (:id, :val)",
"values": { "id": 101, "val": "A hundred and 1" }
},
{
"query": "SELECT * FROM TEST_TABLE WHERE ID = :id",
"values": { "id": 101 }
}
]
}
通过配置文件,可以实现授权访问:
{
"credentials": { // 仅当auth_mode == inline时才需要
"user": "myUser1",
"password": "myCoolPassword"
},
"transaction": [
{
"query": "SELECT * FROM TEMP"
},
{
"query": "SELECT * FROM TEMP WHERE ID = :id",
"values": { "id": 1 }
},
{
"statement": "INSERT INTO TEMP (ID, VAL) VALUES (0, 'ZERO')"
},
{
"noFail": true,
"statement": "INSERT INTO TEMP (ID, VAL) VALUES (:id, :val)",
"values": { "id": 1, "val": "a" }
},
{
"statement": "#Q2", // ‘#’ + 存储语句的 ID
"valuesBatch": [
{ "id": 2, "val": "b" },
{ "id": 3, "val": "c" }
]
}
]
}
默认情况下,它直接打开了WAL模式,以改善性能(多个读和写并发,减少IO,事务更快,崩溃恢复).
配置文件示例
auth:
mode: HTTP # 身份验证模式 HTTP:HTTP基本认证/INLINE:JSON请求中指定凭据
byCredentials:
- user: myUser1
password: myCoolPassword
- user: myUser2
hashedPassword: b133a0c0e9bee3be20163d2ad31d6248db292aa6dcb1ee087a2aa50e0fc75ae2
disableWALMode: true # 禁止WAL模式
readOnly: false # 是否为只读
corsOrigin: https://myownsite.com # 可信访问,可以使用"*"
scheduledTasks: # 计划任务
- schedule: "0 0 * * *"
doVacuum: true # 优化和清理内部结构
doBackup: true # 执行备份
backupTemplate: ~/first_%s.db # 备份文件的文件夹(带路径)。它必须包含一个 %s,该% s 将替换为 yyyyMMdd-HHmm 格式的备份分钟。
numFiles: 3 # 只保存几个备份
statements: # 一组SQL,可以清理临时表等
- DELETE FROM myTable WHERE tstamp < CURRENT_TIMESTAMP - 3600
- ...
- atStartup: true # 启动时就执行任务
doVacuum: true
useOnlyStoredStatements: true # 指定一些语句存储在服务端,而无需客户端发送(这里的true将导致只能使用服务器保存的语句)
storedStatements:
- id: Q1
sql: SELECT * FROM TEMP
- id: Q2
sql: INSERT INTO TEMP VALUES (:id, :val)
initStatements: # 创建数据库时的初始化语句
- CREATE TABLE AUTH (USER TEXT PRIMARY KEY, PASSWORD TEXT)
- INSERT INTO AUTH VALUES ('myUser1', 'myCoolPassword')
- CREATE TABLE TEMP (ID INT PRIMARY KEY, VAL TEXT)
- INSERT INTO TEMP (ID, VAL) VALUES (1, 'ONE'), (4, 'FOUR')
将SQL语句存储到服务器有一些好处,比如较快的交互,安全性.
这样只需要客户端按id进行调用
{
"transaction": [
{
"query": "#Q1"
}
]
}
关于认证:
[...]
auth:
mode: HTTP
customErrorCode: 499
byCredentials:
- user: myUser1
password: myCoolPassword
- user: myUser2
hashedPassword: b133a0c0e9bee3be20163d2ad31d6248db292aa6dcb1ee087a2aa50e0fc75a[e2 # SHA-256 散列
[...]
auth:
mode: INLINE
byQuery: SELECT 1 FROM AUTH WHERE USER = :user AND PASSWORD = :password
在INLINE模式中, 查询 SQL 必须包含两个占位符 :user 和 :password,服务器将用客户端提供的用户名和密码替换它们。
如果查询至少返回一个结果,则凭据有效;如果返回0条记录,则访问将被拒绝。
请求格式如:
{
"credentials": {
"user": "myUser1",
"password": "myCoolPassword"
},
[...]
如果令牌验证失败,将在1秒后返回响应,以防止暴力破解。
示例一个完整的请求:
{
"resultFormat": "map",
"credentials": {
"user": "myUser1",
"password": "myCoolPassword"
},
"transaction": [
{
"query": "SELECT * FROM TEMP"
},
{
"query": "SELECT * FROM TEMP WHERE ID = ?",
"values": [ 1 ] // Positional parameters
},
{
"statement": "INSERT INTO TEMP (ID, VAL) VALUES (0, 'ZERO')"
},
{
"noFail": true, # 发生错误时不失败,继续执行并提交事务
"statement": "INSERT INTO TEMP (ID, VAL) VALUES (:id, :val)",
"values": { "id": 1, "val": "a" } // Named parameters
},
{
"statement": "#Q2",
"valuesBatch": [
{ "id": 2, "val": "b" },
{ "id": 3, "val": "c" }
]
}
]
}
PS: 如何解决javascript保存了明文授权的问题? 思考: 不通过授权,通过服务器保存的sql进行敲门认证
一个小项目中使用中一下ws4sqlite.(个人还是很习惯于在每个项目中使用一些新技术)除了编辑配置文件yaml(主要是sql语句生成),基本就不需要后端了.余下的就是前端的html和javascript,确实减轻了一些工作量.
不过安全问题如何解决还没想到好的方法.
补充一下HTTP授权验证部份代码:
const username = prompt('请输入帐号:');
const password = prompt('请输入密码:');
// Base64编码凭证
const credentials = btoa(`${username}:${password}`);
const response = await fetch(WS4SQLITE_BASE_URL, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Basic ${credentials}`
}
});
打赏