驗證輸入變數

本指南說明如何驗證輸入變數。

定義輸入變數時,建議驗證使用者輸入的值是否適當。舉例來說,如果您要求使用者輸入數字,驗證使用者輸入 1 而非 a,即可確認步驟是否順利執行。

驗證輸入變數的方法有兩種:

  • 用戶端驗證: 透過用戶端驗證,您可以直接在使用者裝置上驗證輸入內容。使用者會立即收到意見回饋,並在設定步驟時修正輸入內容中的錯誤。
  • 伺服器端驗證: 伺服器端驗證可讓您在驗證期間於伺服器上執行邏輯,這在您需要查詢用戶端沒有的資訊 (例如其他系統或資料庫中的資料) 時非常實用。

用戶端驗證

導入用戶端驗證的方法有兩種:

  • 如要進行基本驗證,例如確認小工具包含的字元數少於特定數量,或包含 @ 符號,請叫用 Google Workspace 外掛程式的 Card 服務 Validation 類別。
  • 如要進行嚴謹的驗證 (例如比較小工具值與其他小工具值),可以使用 CardService,將一般運算語言 (CEL) 驗證新增至下列支援的資訊卡小工具。

叫用 Validation 類別

以下範例會驗證 TextInput 小工具是否包含 10 個或更少的字元:

Apps Script

const validation = CardService.newValidation().setCharacterLimit('10').setInputType(     CardService.InputType.TEXT); 

如需其他驗證選項,請使用 CEL 驗證。

CEL 驗證

一般運算語言 (CEL) 驗證可立即檢查輸入內容,不會像伺服器端驗證一樣有延遲問題,因為這項驗證會將不需從其他服務查詢資料的輸入值檢查作業,卸載至用戶端。

您也可以使用 CEL 建立資訊卡行為,例如根據驗證結果顯示或隱藏小工具。這類行為有助於顯示或隱藏錯誤訊息,協助使用者修正輸入內容。

如要建構完整的 CEL 驗證,需要下列元件:

  • ExpressionData (卡片中):包含指定的驗證邏輯和觸發小工具的邏輯 (當符合其中一個定義的條件時)。

    • Id:目前 Card 中 ExpressionData 的專屬 ID。
    • Expression:定義驗證邏輯的 CEL 字串 (例如"value1 == value2")。
    • Conditions:條件清單,內含預先定義的驗證結果 (SUCCESS 或 FAILURE)。條件會透過共用 actionRuleId 繫結至小工具端的 EventAction (透過 Triggers)。
    • 卡片層級 EventAction:在卡片中啟用 CEL 驗證,並透過事件後觸發程序,將 ExpressionData 欄位與結果小工具建立關聯。
      • actionRuleId:這個 EventAction 的專屬 ID。
      • ExpressionDataAction:設為 START_EXPRESSION_EVALUATION,表示此動作會開始評估 CEL。
      • Trigger:根據 actionRuleIdConditions 連線至小工具端 EventActions
  • 小工具層級 EventAction:控制結果小工具在符合成功或失敗條件時的行為。舉例來說,結果小工具可以是包含錯誤訊息的 TextParagraph,只有在驗證失敗時才會顯示。

    • actionRuleId:比對卡片端 Trigger 中的 actionRuleId
    • CommonWidgetAction:定義不涉及評估的動作,例如更新小工具顯示狀態。
      • UpdateVisibilityAction:更新小工具顯示狀態 (VISIBLE 或 HIDDEN) 的動作。

以下範例說明如何實作 CEL 驗證,檢查兩個文字輸入是否相等。如果不相等,系統會顯示錯誤訊息。

  • 當 failCondition 成立 (輸入內容不相等) 時,錯誤訊息小工具會設為 VISIBLE 並顯示。
    圖 1:當符合 failCondition 時 (輸入內容不相等),錯誤訊息小工具會設為 VISIBLE 並顯示。
  • 當 successCondition 達到條件 (輸入內容相同) 時,錯誤訊息小工具會設為 HIDDEN,因此不會顯示。
    圖 2:successCondition 條件符合 (輸入內容相同) 時,錯誤訊息小工具會設為 HIDDEN,且不會顯示。

以下是應用程式程式碼和 JSON 資訊清單檔案範例:

Apps Script

function onConfig() {    // Create a Card   let card = CardService.newCardBuilder();    const textInput_1 = CardService.newTextInput()     .setTitle("Input number 1")     .setFieldName("value1"); // FieldName's value must match a corresponding ID defined in the inputs[] array in the manifest file.   const textInput_2 = CardService.newTextInput()     .setTitle("Input number 2")     .setFieldName("value2"); // FieldName's value must match a corresponding ID defined in the inputs[] array in the manifest file.   let sections = CardService.newCardSection()     .setHeader("Two number equals")     .addWidget(textInput_1)     .addWidget(textInput_2);    // CEL Validation    // Define Conditions   const condition_success = CardService.newCondition()     .setActionRuleId("CEL_TEXTINPUT_SUCCESS_RULE_ID")     .setExpressionDataCondition(       CardService.newExpressionDataCondition()       .setConditionType(         CardService.ExpressionDataConditionType.EXPRESSION_EVALUATION_SUCCESS));   const condition_fail = CardService.newCondition()     .setActionRuleId("CEL_TEXTINPUT_FAILURE_RULE_ID")     .setExpressionDataCondition(       CardService.newExpressionDataCondition()       .setConditionType(         CardService.ExpressionDataConditionType.EXPRESSION_EVALUATION_FAILURE));    // Define Card-side EventAction   const expressionDataAction = CardService.newExpressionDataAction()     .setActionType(       CardService.ExpressionDataActionType.START_EXPRESSION_EVALUATION);   // Define Triggers for each Condition respectively   const trigger_success = CardService.newTrigger()     .setActionRuleId("CEL_TEXTINPUT_SUCCESS_RULE_ID");   const trigger_failure = CardService.newTrigger()     .setActionRuleId("CEL_TEXTINPUT_FAILURE_RULE_ID");    const eventAction = CardService.newEventAction()     .setActionRuleId("CEL_TEXTINPUT_EVALUATION_RULE_ID")     .setExpressionDataAction(expressionDataAction)     .addPostEventTrigger(trigger_success)     .addPostEventTrigger(trigger_failure);    // Define ExpressionData for the current Card   const expressionData = CardService.newExpressionData()     .setId("expData_id")     .setExpression("value1 == value2") // CEL expression     .addCondition(condition_success)     .addCondition(condition_fail)     .addEventAction(eventAction);   card = card.addExpressionData(expressionData);    // Create Widget-side EventActions and a widget to display error message   const widgetEventActionFail = CardService.newEventAction()     .setActionRuleId("CEL_TEXTINPUT_FAILURE_RULE_ID")     .setCommonWidgetAction(       CardService.newCommonWidgetAction()       .setUpdateVisibilityAction(         CardService.newUpdateVisibilityAction()         .setVisibility(           CardService.Visibility.VISIBLE)));   const widgetEventActionSuccess = CardService.newEventAction()     .setActionRuleId("CEL_TEXTINPUT_SUCCESS_RULE_ID")     .setCommonWidgetAction(       CardService.newCommonWidgetAction()       .setUpdateVisibilityAction(         CardService.newUpdateVisibilityAction()         .setVisibility(           CardService.Visibility.HIDDEN)));   const errorWidget = CardService.newTextParagraph()     .setText("The first and second value must match.")     .setVisibility(CardService.Visibility.HIDDEN) // Initially hidden     .addEventAction(widgetEventActionFail)     .addEventAction(widgetEventActionSuccess);   sections = sections.addWidget(errorWidget);    card = card.addSection(sections);   // Build and return the Card   return card.build(); } 

JSON 資訊清單檔案

{   "timeZone": "America/Los_Angeles",   "exceptionLogging": "STACKDRIVER",   "runtimeVersion": "V8",   "addOns": {     "common": {       "name": "CEL validation example",       "logoUrl": "https://www.gstatic.com/images/branding/productlogos/calculator_search/v1/web-24dp/logo_calculator_search_color_1x_web_24dp.png",       "useLocaleFromApp": true     },     "flows": {       "workflowElements": [         {           "id": "actionElement",           "state": "ACTIVE",           "name": "CEL Demo",           "description": "Demonstrates CEL Validation",           "workflowAction": {             "inputs": [               {                 "id": "value1",                 "description": "The first number",                 "cardinality": "SINGLE",                 "dataType": {                   "basicType": "INTEGER"                 }               },               {                 "id": "value2",                 "description": "The second number",                 "cardinality": "SINGLE",                 "dataType": {                   "basicType": "INTEGER"                 }               }             ],             "onConfigFunction": "onConfig",             "onExecuteFunction": "onExecute"           }         }       ]     }   } } 

支援的 CEL 驗證小工具和作業

支援 CEL 驗證的卡片小工具

下列小工具支援 CEL 驗證:

  • TextInput
  • SelectionInput
  • DateTimePicker

支援的 CEL 驗證作業

  • 算術運算
    • +:新增兩個 int64uint64double 數字。
    • -:從兩個 int64uint64double 數字中減去一個。
    • *:將兩個 int64uint64double 數字相乘。
    • /:將兩個 int64uint64double 數字相除 (整數除法)。
    • %:計算兩個 int64uint64 數字的模數。
    • -:將 int64uint64 數字取負數。
  • 邏輯運算:
    • &&:對兩個布林值執行邏輯 AND 運算。
    • ||:對兩個布林值執行邏輯 OR 運算。
    • !:對布林值執行邏輯 NOT 運算。
  • 比較作業:
    • ==:檢查兩個值是否相等。支援數字和清單。
    • !=:檢查兩個值是否不相等。支援數字和清單。
    • <:檢查第一個 int64uint64double 數字是否小於第二個。
    • <=:檢查第一個 int64uint64double 數字是否小於或等於第二個數字。
    • >:檢查第一個 int64uint64double 數字是否大於第二個。
    • >=:檢查第一個 int64uint64double 數字是否大於或等於第二個數字。
  • 列出作業:
    • in:檢查清單中是否有值。支援數字、字串和巢狀清單。
    • size:傳回清單中的項目數量。支援數字和巢狀清單。

不支援的 CEL 驗證情境

  • 二元運算的引數大小不正確:二元運算 (例如 add_int64、等於) 必須有兩個引數。如果提供的引數數量不同,系統會擲回錯誤。
  • 一元運算的引數大小有誤:一元運算 (例如 negate_int64) 只能有一個引數。如果提供的引數數量不同,系統會擲回錯誤。
  • 數值運算中不支援的型別:數值二元和一元運算只接受數字引數。提供其他類型 (例如布林值) 會擲回錯誤。

伺服器端驗證

透過伺服器端驗證,您可以在步驟的程式碼中指定 onSaveFunction(),執行伺服器端邏輯。當使用者離開步驟的設定資訊卡時,onSaveFunction() 會執行並驗證使用者的輸入內容。

如果使用者輸入的內容有效,請傳回 saveWorkflowAction

如果使用者輸入的內容無效,請傳回設定資訊卡,向使用者顯示錯誤訊息,說明如何解決錯誤。

由於伺服器端驗證是非同步作業,使用者可能要等到發布流程時,才會發現輸入錯誤。

資訊清單檔案中每個經過驗證的輸入內容的 id,都必須與程式碼中卡片小工具的 name 相符。

以下範例會驗證使用者輸入的文字是否包含「@」符號:

資訊清單檔案

資訊清單檔案摘錄指定名為「onSave」的 onSaveFunction()

JSON

{   "timeZone": "America/Los_Angeles",   "exceptionLogging": "STACKDRIVER",   "runtimeVersion": "V8",   "addOns": {     "common": {       "name": "Server-side validation example",       "logoUrl": "https://www.gstatic.com/images/branding/productlogos/calculator_search/v1/web-24dp/logo_calculator_search_color_1x_web_24dp.png",       "useLocaleFromApp": true     },     "flows": {       "workflowElements": [         {           "id": "actionElement",           "state": "ACTIVE",           "name": "Calculate",           "description": "Asks the user for an email address",           "workflowAction": {             "inputs": [               {                 "id": "email",                 "description": "email address",                 "cardinality": "SINGLE",                 "required": true,                 "dataType": {                   "basicType": "STRING"                 }               }             ],             "onConfigFunction": "onConfigCalculate",             "onExecuteFunction": "onExecuteCalculate",             "onSaveFunction": "onSave"           }         }       ]     }   } } 

應用程式程式碼

這個步驟的程式碼包含名為 onSave() 的函式。這個函式會驗證使用者輸入的字串是否包含「@」。如果包含,就會儲存流程步驟。如果沒有,系統會傳回設定資訊卡,並顯示錯誤訊息,說明如何修正錯誤。

Apps Script

/**  * Validates user input asynchronously when the user  * navigates away from a step's configuration card. */ function onSave(event) {    // "email" matches the input ID specified in the manifest file.   var email = event.workflow.actionInvocation.inputs["email"];    // Base card.   var card = {     "sections": [       {         "header": "Collect Email",         "widgets": [           {             "textInput": {               "name": "email",               "label": "email address",               "hostAppDataSource" : {                 "workflowDataSource" : {                   "includeVariables" : true                 }               }             }           }         ]       }     ]   };    // Validate that the email address contains an "@" sign:   if(email.includes("@")) {    // If successfully validated, save and proceed.   // To clear any previous validation errors, push an updated   // card that doesn't include an error message.     return {       "action": {         "navigations": [{           "pushCard": card         }]       },       "hostAppAction" : {         "workflowAction" : {           "saveWorkflowAction" : {}         }       }     };    // If the input is invalid, return a card with an error message   // and a validation error action.   } else {      // Add an error message to the card.     card.sections[0].widgets.push({       "textParagraph": {         "text": "<b>Error:</b> Email addresses must include the '@' sign.",         "maxLines": 1       }     });      return {       "action": {         "navigations": [{           "pushCard": card         }]       },       "hostAppAction": {         "workflowValidationErrorAction": {           "severity": "CRITICAL"         }       }     };   } }