近年來NODEJS發展迅速,很多工程師尤其是前端工程師,用NODEJS來開發一些後端應用。同時,研發效率和研發成本成爲開發者關注的重點,對於一些基礎常用功能,如何避免重複開發,成爲大家關注的重點,而 Parse Server 就是抽象了常用功能的NODEJS開源項目。
首先,從整體上看看 Parse Server
提供了哪些基礎功能:
- 用戶的登錄註冊
- 用戶身份的認證
- 數據存儲 && 靈活查詢
- 文件存儲
- 實時查詢
- 消息推送
- 緩存服務
- 與雲平臺很好的對接
- 自定義業務邏輯與Hook機制
服務快速搭建
默認情況下,Parse Server 使用的默認數據庫是 MonogDB
,所以需要提前安裝該數據庫。關於數據庫的安裝與使用不是本文的重點,暫且跳過。
const config = require('./config');
const app = express();
var api = new ParseServer({
databaseURI: config.databaseURI,
cloud: './cloud/main.js',
appId: config.appId,
masterKey: config.masterKey,
// push: { ... }, // See the Push wiki page
// filesAdapter: ..., // 對應不同雲廠商的 FilesAdapter
// javascriptKey: config.javascriptKey, // js客戶端認證
liveQuery: { // 建立websocket鏈接,實現實時通信
classNames: ['Sdtuent']
}
});
var dashboard = new ParseDashboard({
"apps": [
{
"serverURL": "http://localhost:1337/parse",
"appId": config.appId,
"masterKey": config.masterKey,
"appName": "test"
},
]
});
// Serve the Parse API at /parse URL prefix
app.use('/parse', api);
app.use('/dashboard', dashboard);
const port = 1337;
const httpServer = http.createServer(app);
httpServer.listen(port, function() {
console.log('parse-server-example running on port ' + port + '.');
});
var parseLiveQueryServer = ParseServer.createLiveQueryServer(httpServer);
通過上述少量的代碼,快速完成服務的搭建,/parse
是API的前綴,/dashboard
是後臺的頁面路由前綴,這樣就可以快速使用 Parse Server 提供的各種功能。
客戶端調用
針對不同環境、不同語言,parse server提供了大量的 SDK,本文以 javascript
爲例進行簡單介紹。
<head>
<script src="https://unpkg.com/[email protected]/dist/parse.js"></script>
</head>
<body>
...
<script>
// 對應服務端設置的id
Parse.initialize(${appId}, ${javascriptKey});
Parse.serverURL = 'http://127.0.0.1:1337/parse'
const username = "xxx", password = 'xxxx';
const email = 'xxx'
async function signup() {
try {
const user = new Parse.User;
user.set('username', username);
user.set('password', password);
user.set('email', email);
const result = await user.signUp();
console.log('result is: ', result);
} catch(err) {
console.log('error: ', err);
}
}
signup();
</script>
</body>
上面的示例,是在瀏覽器端進行用戶的註冊: 首先引入Javascript SDK
;然後進行基本的環境設置 Parse.initialize
、Parse.serverURL
;第三部就是使用SDK調用 user.signUp
完成註冊。
然後到 dashboard
,可以看到 user
中新增了一條記錄:
{
"results": [
{
"objectId": "1r2sFIzSRP",
"username": "xxx",
"createdAt": "2019-02-16T10:30:00.070Z",
"updatedAt": "2019-02-16T10:30:00.070Z",
"ACL": {
"*": {
"read": true
},
"1r2sFIzSRP": {
"read": true,
"write": true
}
}
}
]
}
重點功能介紹
權限控制
Parse Server的權限控制分爲兩層: Class level permission(類級別的權限控制)、Object level access control(對象級別的權限控制)。
async function saveData() {
const Student = Parse.Object.extend("Student");
const student = new Student();
student.setACL(new Parse.ACL(Parse.User.current()));
student.set("name", "小明");
student.set("age", 12);
const result = await student.save();
console.log('save data: ', result);
}
async function getData() {
const Student = Parse.Object.extend("Student");
const query = new Parse.Query(Student);
query.equalTo('name', '小明');
const result = await query.find();
console.log('save data: ', result);
}
async function run() {
await saveData();
await getData(); // 獲得一條數據
await Parse.User.logOut();
// 用另外一個賬號登錄
await getData(); // 結果爲空
}
上述示例設置了 Object level access control
,只有擁有該 object
權限的用戶,才能獲取對應的數據。
數據的實時通信
要實現數據的實時通信,通常的做法是搭建一個websocket服務,客戶端與服務端建立長連接,這樣就可以實現數據的實時通信。Parse Server
也集成了這個功能,把websocket服務跟HTTP服務整合在一起,便於 client 端統一調用。
Class的schema問題
因爲 Parse Server 背後的數據存儲使用的是 MongoDB,數據格式是 schemaless 的, 但是這並不是說可以隨意寫入數據:
- 第一次創建
Class
的時候,可以自定義字段及類型,同時默認添加 _id、_created_at、_updated_at、_acl 等字段 - 同時,在底層數據中,存在一個
_SCHEMA
集合,存儲了 Class 的格式信息,約束了數據再次寫入時的字段類型
爲了驗證上述兩點,直接登錄 MongoDB
數據庫後臺:
// step1: 登錄數據庫後臺
mongo
// step2: 查看 collection 情況
show collections
結果如下:
Student
_Role
_SCHEMA
_Session
_User
// step3: 查看 _SCHEMA 內容
db.getCollection('_SCHEMA').find()
結果如下:
{
"_id":"Student",
"objectId":"string",
"updatedAt":"date",
"createdAt":"date",
"name":"string",
"age":"number",
"address":"string"
}
...
接下來對Student表進行寫入操作,age的類型不是number,而使用string:
async function saveData() {
const Student = Parse.Object.extend("Student");
const student = new Student();
student.setACL(new Parse.ACL(Parse.User.current()));
student.set("name", "張三");
student.set("age", '9999');
student.set('address', '北京');
const result = await student.save();
console.log('save data: ', result);
}
執行上述代碼,會拋出異常信息 schema mismatch for Student.age; expected Number but got String
Cloud Code / HOOK 機制 - 實現自定義業務邏輯
如果進行業務開發,肯定存在自定義的業務邏輯,Parse Server 同樣提供了對應的解決途徑。
先來看看 Cloud Code 是如何工作的:
// step1 在Parse Server入口處指定路徑
var api = new ParseServer({
...
cloud: './cloud/main.js',
...
});
// step2 定義一般的Function
Parse.Cloud.define("averageStars", async (request, response) => {
const params = request.params;
...
const result = ...
response.success(result);
});
// 定義hook函數
Parse.Cloud.beforeSave("Student", (request) => {
...
});
// step3 客戶端調用自定義函數方式
async function invokeFn() {
const params = { ... };
const ratings = await Parse.Cloud.run("averageStars", params);
console.log('ratings: ', ratings);
}
// 客戶端出發hook比較簡單,對Student進行寫入,就會觸發hook函數
async function saveData() {
const Student = Parse.Object.extend("Student");
const student = new Student();
...
const result = await student.save();
}
同時,Parse Server 也支持遠程調用,使用 Dashboard 的 Webhook功能,就可以配置遠程調用API
思考總結
Parse Server這個開源項目給了不少項目設計上的思考:
- 具體功能抽象,把一些常用功能抽象出來,是研發提效的一個很重要的手段
- server + client模式,抽象出業務模型之後,如果只是解決了某一部分的問題(例如server),使用成本依然很高,但是如果解決更多鏈路上的問題,那就會變得更加容易使用和落地。Parser Server不僅提供了server端的實現,而且提供了Dashboard、Client、Adapter等內容
- hook功能,用於解決個性化需求,滿足業務定製
- 與雲的結合,目前已成爲未來技術選型的一個很大趨勢,Parse Server 的文件存儲,提供了很多雲存儲相關的 Adapter,快速實現跟雲產品的對接
- 權限控制設計,數據的安全已經成爲研發很重要的一個工作,分級控制是個思路