觸發(fā)器是一種響應(yīng)特定服務(wù)器端事件(例如將數(shù)據(jù)保存到特定集合時)執(zhí)行操作的方法。 這些通常有兩種情況。
觸發(fā)器針對特定的集合(例如 ?EthTransactions
?)進(jìn)行操作。 例如,要在轉(zhuǎn)移特定 NFT 時將訂單對象的狀態(tài)更新為“已售出”,您可以在 ?EthNFTTransfers
?上定義 ?afterSave
?觸發(fā)器。 每次將新條目添加到 ?EthNFTTransfers
?集合時都會調(diào)用觸發(fā)器,您可以檢查它是否與其中一個未結(jié)訂單匹配。
測試網(wǎng)和主網(wǎng)上的交易可能需要一段時間才能得到確認(rèn)。 當(dāng) Moralis 檢測到處于未確認(rèn)狀態(tài)的新交易(或事件)時,這些交易將被放入帶有confirmed:true集合中。 當(dāng)交易被確認(rèn)時,狀態(tài)更新為confirmed:true。
這意味著如果您在集合上定義 ?afterSave
?觸發(fā)器,則觸發(fā)器可以被觸發(fā)兩次——一次為confirmed:false,再次為confirmed:true。 任何具有confirmed屬性的集合都可能發(fā)生這種情況。 考慮到這種行為,編寫您的觸發(fā)器回調(diào)函數(shù)。 如果觸發(fā)器在其他集合中創(chuàng)建條目,則這可能導(dǎo)致重復(fù)條目或計算中的重復(fù)計數(shù)等。
可以通過使用 ?request.object
? 獲取觸發(fā)觸發(fā)器的對象來檢查確認(rèn)狀態(tài)。
Moralis.Cloud.afterSave("EthTransactions", async function (request) {
const confirmed = request.object.get("confirmed");
if (confirmed) {
// do something
} else {
// handle unconfirmed case
}
});
?
Ganache
?和 ?Hardhat
?僅在進(jìn)行交易時處理新塊。 這意味著,如果您鑄造新的 ?NFT
?或進(jìn)行代幣轉(zhuǎn)移,這些交易可能會卡在待處理表中。 一次確認(rèn)后,這些將被移出待處理。
在云中運行代碼的另一個原因是強制執(zhí)行特定的數(shù)據(jù)格式。 例如,您可能同時擁有一個 Android 和 iOS 應(yīng)用程序,并且您希望驗證每個應(yīng)用程序的數(shù)據(jù)。 無需為每個客戶端環(huán)境編寫一次代碼,您可以使用云代碼只編寫一次。
數(shù)據(jù)驗證可以在觸發(fā)器的代碼中完成。
Moralis.Cloud.beforeSave("Review", (request) => {
throw "validation error";
}
});
如果函數(shù)拋出,那么 ?Review
?對象將不會被保存并且客戶端會得到一個錯誤。 如果沒有拋出任何東西,對象將被正常保存。
一個有用的提示是,即使您的應(yīng)用程序有許多不同的版本,相同版本的云代碼也適用于所有版本。 因此,如果您啟動的應(yīng)用程序沒有正確檢查輸入數(shù)據(jù)的有效性,您仍然可以通過使用 ?beforeSave
?添加驗證來解決此問題。
在某些情況下,您不想丟棄無效數(shù)據(jù)。 您只想在保存之前對其進(jìn)行一些調(diào)整。 ?beforeSave
?也可以處理這種情況。 您對 ?request.object
? 所做的任何調(diào)整都將被保存。
在我們的電影評論示例中,我們可能希望確保評論不會太長。 一條長評論可能很難顯示。 我們可以使用 ?beforeSave
?將評論字段截斷為 140 個字符:
Moralis.Cloud.beforeSave("Review", (request) => {
const comment = request.object.get("comment");
if (comment.length > 140) {
// Truncate and add a ...
request.object.set("comment", comment.substring(0, 137) + "...");
}
});
如果您想對 Moralis JavaScript SDK 中的預(yù)定義類使用 ?beforeSave
?,則不應(yīng)為第一個參數(shù)傳遞字符串。 相反,您應(yīng)該傳遞類本身,例如:
Moralis.Cloud.beforeSave(
Moralis.User,
async (request) => {
// code here
}
// Validation Object or Validation Function
);
在某些情況下,您可能希望在保存對象后執(zhí)行某種類型的操作,例如推送。 您可以通過使用 ?afterSave
?方法注冊處理程序來做到這一點。 例如,假設(shè)您想要跟蹤博客文章的評論數(shù)量。 您可以通過編寫如下函數(shù)來做到這一點:
const logger = Moralis.Cloud.getLogger();
Moralis.Cloud.afterSave("Comment", (request) => {
const query = new Moralis.Query("Post");
query
.get(request.object.get("post").id)
.then(function (post) {
post.increment("comments");
return post.save();
})
.catch(function (error) {
logger.error("Got an error " + error.code + " : " + error.message);
});
});
在上面的示例中,客戶端將在處理程序中的承諾完成之前收到成功的響應(yīng),無論承諾如何解決。 例如,即使處理程序拋出異常,客戶端也會收到成功的響應(yīng)。 運行處理程序時發(fā)生的任何錯誤都可以在云代碼日志中找到。
在將響應(yīng)發(fā)送回客戶端后,您可以使用 ?afterSave
?處理程序執(zhí)行冗長的操作。 為了在 ?afterSave
?處理程序完成之前響應(yīng)客戶端,您的處理程序可能不會返回承諾,并且您的 ?afterSave
?處理程序可能不會使用 ?async/await
?。
如果您想將 ?afterSave
?用于 Moralis JavaScript SDK 中的預(yù)定義類,則不應(yīng)為第一個參數(shù)傳遞字符串。 相反,您應(yīng)該傳遞類本身,例如:
Moralis.Cloud.afterSave(Moralis.User, async (request) => {
// code here
});
保存 ?Moralis.Object
? 時,您可以傳遞可在 ?Cloud Code Save Triggers
? 中訪問的上下文字典。
上下文也從 ?beforeSave
?處理程序傳遞到 ?afterSave
?處理程序。 以下示例異步向正在添加到 ?Moralis.Role
? 的用戶關(guān)系的用戶發(fā)送電子郵件,以便客戶端在電子郵件完成發(fā)送之前收到響應(yīng):
const beforeSave = function beforeSave(request) {
const { object: role } = request;
// Get users that will be added to the users relation.
const usersOp = role.op("users");
if (usersOp && usersOp.relationsToAdd.length > 0) {
// add the users being added to the request context
request.context = { buyers: usersOp.relationsToAdd };
}
};
const afterSave = function afterSave(request) {
const { object: role, context } = request;
if (context && context.buyers) {
const purchasedItem = getItemFromRole(role);
const promises = context.buyers.map(emailBuyer.bind(null, purchasedItem));
item.increment("orderCount", context.buyers.length);
promises.push(item.save(null, { useMasterKey: true }));
Promise.all(promises).catch(request.log.error.bind(request.log));
}
};
您可以在刪除對象之前運行自定義云代碼。 您可以使用 ?beforeDelete
?方法執(zhí)行此操作。 例如,這可用于實現(xiàn)比通過 ?ACLs
? 表達(dá)的內(nèi)容更復(fù)雜的受限刪除策略。 例如,假設(shè)您有一個相冊應(yīng)用程序,其中許多照片與每個相冊相關(guān)聯(lián),并且您希望阻止用戶刪除仍然有照片的相冊。 您可以通過編寫如下函數(shù)來做到這一點:
Moralis.Cloud.beforeDelete("Album", async (request) => {
const query = new Moralis.Query("Photo");
query.equalTo("album", request.object);
const count = await query.count({ useMasterKey: true });
if (count > 0) {
throw "Can't delete album if it still has photos.";
}
});
如果函數(shù)拋出,?Album
?對象不會被刪除,客戶端會報錯。 否則,對象將被正常刪除。
如果您想對 ?Moralis JavaScript SDK
? 中的預(yù)定義類使用 ?beforeDelete
?,則不應(yīng)為第一個參數(shù)傳遞字符串。 相反,您應(yīng)該傳遞類本身,例如:
Moralis.Cloud.beforeDelete(Moralis.User, async (request) => {
// code here
});
在某些情況下,您可能希望在刪除對象后執(zhí)行某種類型的操作,例如推送。 您可以通過使用 ?afterDelete
?方法注冊處理程序來完成此操作。 例如,假設(shè)在刪除博客文章后,您還想刪除所有關(guān)聯(lián)的評論。 您可以通過編寫如下函數(shù)來做到這一點:
const logger = Moralis.Cloud.getLogger();
Moralis.Cloud.afterDelete("Post", (request) => {
const query = new Moralis.Query("Comment");
query.equalTo("post", request.object);
query
.find()
.then(Moralis.Object.destroyAll)
.catch((error) => {
logger.error(
"Error finding related comments " + error.code + ": " + error.message
);
});
});
?afterDelete
? 處理程序可以訪問通過 ?request.object
? 刪除的對象。 此對象已完全提取,但無法重新提取或重新保存。
在處理程序終止后,客戶端將收到對刪除請求的成功響應(yīng),無論 ?afterDelete
?是如何終止的。 例如,即使處理程序拋出異常,客戶端也會收到成功的響應(yīng)。 運行處理程序時發(fā)生的任何錯誤都可以在云代碼日志中找到。
如果您想對 ?Moralis JavaScript SDK
? 中的預(yù)定義類使用 ?afterDelete
?,則不應(yīng)為第一個參數(shù)傳遞字符串。 相反,您應(yīng)該傳遞類本身,例如:
Moralis.Cloud.afterDelete(Moralis.User, async (request) => {
// code here
});
使用 ?beforeSaveFile
?方法,您可以在保存任何文件之前運行自定義云代碼。 返回一個新的 ?Moralis.File
? 將保存新文件,而不是客戶端發(fā)送的文件。
例如
// Changing the file name
Moralis.Cloud.beforeSaveFile(async (request) => {
const { file } = request;
const fileData = await file.getData();
const newFile = new Moralis.File("a-new-file-name.txt", { base64: fileData });
return newFile;
});
// Returning an already saved file
Moralis.Cloud.beforeSaveFile((request) => {
const { user } = request;
const avatar = user.get("avatar"); // this is a Moralis.File that is already saved to the user object
return avatar;
});
// Saving a different file from uri
Moralis.Cloud.beforeSaveFile((request) => {
const newFile = new Moralis.File("some-file-name.txt", {
uri: "www.somewhere.com/file.txt",
});
return newFile;
});
向您的文件添加元數(shù)據(jù)和標(biāo)簽允許您向存儲在您的存儲解決方案(即 AWS S3)中的文件添加額外的數(shù)據(jù)位。 ?beforeSaveFile
?鉤子是在文件上設(shè)置元數(shù)據(jù)和/或標(biāo)簽的好地方。
注意:并非所有存儲適配器都支持元數(shù)據(jù)和標(biāo)簽。 檢查您正在使用的存儲適配器的文檔以了解兼容性。
// Adding metadata and tags
Moralis.Cloud.beforeSaveFile((request) => {
const { file, user } = request;
file.addMetadata("createdById", user.id);
file.addTag("groupId", user.get("groupId"));
});
?afterSaveFile
?方法是跟蹤應(yīng)用程序中存儲的所有文件的好方法。 例如:
Moralis.Cloud.afterSaveFile(async (request) => {
const { file, fileSize, user } = request;
const fileObject = new Moralis.Object("FileObject");
fileObject.set("file", file);
fileObject.set("fileSize", fileSize);
fileObject.set("createdBy", user);
const token = { sessionToken: user.getSessionToken() };
await fileObject.save(null, token);
});
您可以在刪除任何文件之前運行自定義云代碼。 例如,假設(shè)您要添加只允許創(chuàng)建文件的用戶刪除文件的邏輯。 您可以使用 ?afterSaveFile
?和 ?beforeDeleteFile
?方法的組合,如下所示:
Moralis.Cloud.afterSaveFile(async (request) => {
const { file, user } = request;
const fileObject = new Moralis.Object('FileObject');
fileObject.set('fileName', file.name());
fileObject.set('createdBy', user);
await fileObject.save(null, { useMasterKey: true );
});
Moralis.Cloud.beforeDeleteFile(async (request) => {
const { file, user } = request;
const query = new Moralis.Query('FileObject');
query.equalTo('fileName', file.name());
const fileObject = await query.first({ useMasterKey: true });
if (fileObject.get('createdBy').id !== user.id) {
throw 'You do not have permission to delete this file';
}
});
在上面的 ?beforeDeleteFile
?示例中,?FileObject
?集合用于跟蹤應(yīng)用程序中保存的文件。 成功刪除文件后,?afterDeleteFile
?觸發(fā)器是清理這些對象的好地方。
Moralis.Cloud.afterDeleteFile(async (request) => {
const { file } = request;
const query = new Moralis.Query("FileObject");
query.equalTo("fileName", file.name());
const fileObject = await query.first({ useMasterKey: true });
await fileObject.destroy({ useMasterKey: true });
});
在某些情況下,您可能希望轉(zhuǎn)換傳入查詢、添加額外限制或增加默認(rèn)限制、添加額外包含或?qū)⒔Y(jié)果限制為鍵的子集。 您可以使用 ?beforeFind
?觸發(fā)器來執(zhí)行此操作。
例如
// Properties available
Moralis.Cloud.beforeFind("MyObject", (req) => {
let query = req.query; // the Moralis.Query
let user = req.user; // the user
let triggerName = req.triggerName; // beforeFind
let isMaster = req.master; // if the query is run with masterKey
let isCount = req.count; // if the query is a count operation
let logger = req.log; // the logger
let installationId = req.installationId; // The installationId
});
// Selecting keys
Moralis.Cloud.beforeFind("MyObject", (req) => {
let query = req.query; // the Moralis.Query
// Force the selection on some keys
query.select(["key1", "key2"]);
});
// Asynchronous support
Moralis.Cloud.beforeFind("MyObject", (req) => {
let query = req.query;
return aPromise().then((results) => {
// do something with the results
query.containedIn("key", results);
});
});
// Returning a different query
Moralis.Cloud.beforeFind("MyObject", (req) => {
let query = req.query;
let otherQuery = new Moralis.Query("MyObject");
otherQuery.equalTo("key", "value");
return Moralis.Query.or(query, otherQuery);
});
// Rejecting a query
Moralis.Cloud.beforeFind("MyObject", (req) => {
// throw an error
throw new Moralis.Error(101, "error");
// rejecting promise
return Promise.reject("error");
});
// Setting the read preference for a query
Moralis.Cloud.beforeFind("MyObject2", (req) => {
req.readPreference = "SECONDARY_PREFERRED";
req.subqueryReadPreference = "SECONDARY";
req.includeReadPreference = "PRIMARY";
});
如果要將 ?beforeFind
?用于 ?Moralis JavaScript SDK
? 中的預(yù)定義類,則不應(yīng)為第一個參數(shù)傳遞字符串。 相反,您應(yīng)該傳遞類本身,例如:
Moralis.Cloud.beforeFind(Moralis.User, async (request) => {
// code here
});
在某些情況下,您可能希望在將查詢結(jié)果發(fā)送到客戶端之前對其進(jìn)行操作。 您可以使用 ?afterFind
?觸發(fā)器執(zhí)行此操作。
Moralis.Cloud.afterFind("MyCustomClass", async (request) => {
// code here
});
如果要將 ?afterFind
?用于 ?Moralis JavaScript SDK
? 中的預(yù)定義類,則不應(yīng)為第一個參數(shù)傳遞字符串。 相反,您應(yīng)該傳遞類本身,例如:
Moralis.Cloud.afterFind(Moralis.User, async (request) => {
// code here
});
有時您可能希望對登錄請求運行自定義驗證。 ?beforeLogin
?觸發(fā)器可用于阻止帳戶登錄(例如,如果他們被禁止),記錄登錄事件以進(jìn)行分析,如果登錄發(fā)生在不尋常的 IP 地址上,則通過電子郵件通知用戶等等。
Moralis.Cloud.beforeLogin(async (request) => {
const { object: user } = request;
if (user.get("isBanned")) {
throw new Error("Access denied, you have been banned.");
}
});
需要注意的一些注意事項
promise
?解決。
beforeLogin
?成功完成之后,才向用戶提供會話。
Moralis.User
? 上的 ?afterSave
?一樣,除非明確保存,否則它不會將突變保存到用戶。觸發(fā)器將運行
authProvider
?登錄時。觸發(fā)器不會運行
有時您可能希望在用戶注銷后運行操作。 例如,?afterLogout
?觸發(fā)器可用于用戶注銷后的清理操作。 觸發(fā)器包含已在注銷時刪除的會話對象。 從此會話對象中,您可以確定注銷以執(zhí)行用戶特定任務(wù)的用戶。
Moralis.Cloud.afterLogout(async (request) => {
const { object: session } = request;
const user = session.get("user");
user.set("isOnline", false);
user.save(null, { useMasterKey: true });
});
需要注意的一些注意事項
afterDelete
?觸發(fā)器一樣,請求中包含的 ?_Session
? 對象已被刪除。觸發(fā)器將運行
_Session
? 對象時。觸發(fā)器不會運行
_Session
? 對象。
_Session
? 對象被刪除,而用戶沒有通過調(diào)用 SDK 的 logout 方法注銷。您可以在用戶嘗試使用 ?beforeConnect
?方法連接到您的 LiveQuery 服務(wù)器之前運行自定義云代碼。 例如,這可用于僅允許已登錄的用戶連接到 LiveQuery 服務(wù)器。
Moralis.Cloud.beforeConnect((request) => {
if (!request.user) {
throw "Please login before you attempt to connect.";
}
});
大多數(shù)情況下,?connect
?事件在客戶端第一次調(diào)用 ?subscribe
?時被調(diào)用。 如果這是您的用例,您可以使用此事件偵聽錯誤。
const logger = Moralis.Cloud.getLogger();
Moralis.LiveQuery.on("error", (error) => {
logger.info(error);
});
在某些情況下,您可能希望轉(zhuǎn)換傳入的訂閱查詢。 示例包括添加額外限制、增加默認(rèn)限制、添加額外包含或?qū)⒔Y(jié)果限制為鍵的子集。 您可以使用 ?beforeSubscribe
?觸發(fā)器來執(zhí)行此操作。
Moralis.Cloud.beforeSubscribe("MyObject", (request) => {
if (!request.user.get("Admin")) {
throw new Moralis.Error(
101,
"You are not authorized to subscribe to MyObject."
);
}
let query = request.query; // the Moralis.Query
query.select("name", "year");
});
在某些情況下,您可能希望在將實時查詢的結(jié)果發(fā)送到客戶端之前對其進(jìn)行操作。 您可以使用 ?afterLiveQueryEvent
?觸發(fā)器執(zhí)行此操作。
例如
// Changing values on object and original
Moralis.Cloud.afterLiveQueryEvent("MyObject", (request) => {
const object = request.object;
object.set("name", "***");
const original = request.original;
original.set("name", "yolo");
});
// Prevent LiveQuery trigger unless 'foo' is modified
Moralis.Cloud.afterLiveQueryEvent("MyObject", (request) => {
const object = request.object;
const original = request.original;
if (!original) {
return;
}
if (object.get("foo") != original.get("foo")) {
request.sendEvent = false;
}
});
默認(rèn)情況下,?MoralisLiveQuery
?不執(zhí)行需要額外數(shù)據(jù)庫操作的查詢。 這是為了讓您的 ?Moralis Server
? 盡可能快速和高效。 如果您需要此功能,您可以在 ?afterLiveQueryEvent
?中執(zhí)行這些功能。
// Including an object on LiveQuery event, on update only.
Moralis.Cloud.afterLiveQueryEvent("MyObject", async (request) => {
if (request.event != "update") {
request.sendEvent = false;
return;
}
const object = request.object;
const pointer = object.get("child");
await pointer.fetch();
});
// Extend matchesQuery functionality to LiveQuery
Moralis.Cloud.afterLiveQueryEvent("MyObject", async (request) => {
if (request.event != "create") {
return;
}
const query = request.object.relation("children").query();
query.equalTo("foo", "bart");
const first = await query.first();
if (!first) {
request.sendEvent = false;
}
});
afterLiveQueryEvent
?觸發(fā)器完成之前,不會觸發(fā)實時查詢事件。 確保觸發(fā)器內(nèi)的任何功能都有效且具有限制性,以防止出現(xiàn)瓶頸。有時您可能希望監(jiān)視要與第三方(例如“?Datadog
?”)一起使用的實時查詢事件。 ?onLiveQueryEvent
?觸發(fā)器可以記錄觸發(fā)的事件、連接的客戶端數(shù)量、訂閱數(shù)量和錯誤。
Moralis.Cloud.onLiveQueryEvent(
({
event,
client,
sessionToken,
useMasterKey,
installationId,
clients,
subscriptions,
error,
}) => {
if (event !== "ws_disconnect") {
return;
}
// Do your magic
}
);
“connect”與“ws_connect”不同,前者意味著客戶端完成了Moralis Live Query協(xié)議定義的連接過程,其中“ws_connect”只是意味著創(chuàng)建了一個新的websocket。
更多建議: