1. 程式人生 > >使用ABAP Data Validator驗證資料有效性

使用ABAP Data Validator驗證資料有效性

在日常的開發過程中,我們常常要處理不同來源的資料。資料可能來自不可靠的外部系統、不可靠的使用者輸入和甚至設計有誤的資料庫表,因此,對資料有效性進行驗證是必要的工作。

開源工具ABAP Data Validator是一個使用ABAP開發的資料驗證工具,它可以簡化開發者在這方面的工作。本文將介紹它的用法和一些設計思路。

 

本文連結:https://www.cnblogs.com/hhelibeb/p/12206648.html

原創內容,轉載請註明

目的

具體而言,ABAP Data Validator將通過以下的思路簡化資料有效性驗證方面的工作:

  • 提供統一的檢查介面,讓開發者通過單次方法呼叫就可以實現對資料的檢查。
  • 將驗證邏輯集中實現,避免相似的檢查程式碼分散在系統各處造成的邏輯不一致,從而降低相關程式的維護成本。
  • 避免檢查過程中的潛在dump,減少開發者處理dump問題的精力花費。

為了實現以上目的,該工具實現了一些功能:

  • 內建常見的驗證邏輯。
  • 可配置的檢查規則。
  • 可擴充套件的檢查程式。
  • 異常的統一處理。

支援的檢查列表

ABAP Data Validator目前支援以下型別的檢查(持續更新中):

  • 日期.
  • 時間.
  • 時間戳.
  • 郵件地址.
  • INT4.
  • 正則字串.
  • URL.
  • JSON.
  • HEX.
  • IMEI.
  • GUID.
  • BASE64.
  • HTML (實驗性的).

此外,它也支援對內表字段的必填檢查、根據資料元素的型別進行檢查等功能,下文會詳細介紹。

使用

ABAP Data Validator支援多種檢查方式,下面會由簡單到複雜進行逐一介紹。

對單一欄位的直接檢查

對於每種資料型別,ABAP Data Validator會有一個專門的檢查類,可以用這些檢查類的is_valid方法來檢查變數的值是否有效,就像使用abap的內建函式那樣。比如,要檢查一個字串是否是有效的郵箱地址,可以用如下程式碼實現,

IF zcl_adv_email_check=>is_valid( '[email protected]' ).
 "do something
ENDIF.

所有的檢查類都實現了介面zif_adv_check,因此它們都擁有靜態方法is_valid,如下圖(為了方便閱讀,圖中沒有包含全部的實現類),

 

對於類方法而言,is_valid是個別名,也可以不使用別名來呼叫介面方法:

IF zcl_adv_email_check=>zif_adv_check~is_valid( '[email protected]' ).
 "do something
ENDIF.

當然並不推薦這種方式,因為會讓程式碼變長。

檢查類的命名規則是ZCL_ADV_型別名_CHECK。

使用參考資料元素檢查

為了便於使用,方法is_valid的定義十分簡單,只有一個輸入引數、一個返回值,不包含任何異常,這使得它無法勝任數量、金額等型別(一般而言是P型別,包含長度和小數位定義)欄位的檢查。為了解決這一問題,ABAP Data Validator提供了參考資料元素進行檢查的用法。可以使用類zcl_adata_validator的方法validate_by_element使用該功能,示例如下,

DATA(result) = NEW zcl_adata_validator( )->->validate_by_element(
   data    = data
   element = 'MENGE_D' "數量
).

result是一個結構,包含兩個欄位valid和type,如果data的值無法轉換為數字、或者轉換後溢位的話,result-valid將被賦值為abap_false(即空);如果data是一個有效值,那麼rresult-valid將被賦值為abap_true(即'X')。

正常情況下,result-type總會被賦值為資料元素的型別,對P型別,則為固定值’PACKED‘(c_type_packed)。但如果輸入的資料元素無效的話,type則會被賦值為固定值’Invalid Type‘(c_type_invalid)。

目前支援以下型別的資料元素:

  • 日期
  • 時間
  • 時間戳
  • INT4
  • GUID
  • HEX
  • DEC(P型別)

為了保持檢查方法的簡單性,validate_by_element方法的定義同樣不包含異常。任何異常都會在方法內部被處理。出現異常時,result-valid會被賦值為abap_false,type會被賦值為’Invalid Type‘。

內表檢查

ABAP Data Validator可以根據指定的規則對內表進行檢查,如下所示,將規則my_rules和內表uploaded_data傳入類zcl_adata_validator的方法validate,可以得到檢查結果內表results,

TRY.
    DATA(results) = NEW zcl_adata_validator( )->validate(
         rules   = my_rules
         data    = uploaded_data
     ).
  CATCH zcx_adv_exception INTO DATA(ex).
    DATA(msg) = ex->get_text( ).
ENDTRY. 

檢查規則rules用於設定檢查邏輯。

rules的型別定義如下,

    TYPES: BEGIN OF ty_rule,
             fname            TYPE name_komp,   "欄位名
             required         TYPE abap_bool,   "必填
             initial_or_empty TYPE abap_bool,   "必須為空或初始值
             user_type        TYPE ty_spec_type,"檢查型別(參考類zcl_adata_validator的常量列表)
             regex            TYPE string,      "自定義正則表示式
             regex_msg        TYPE string,      "自定義正則表示式檢查失敗訊息
             ref_element      TYPE rollname,    "參考資料元素
           END OF ty_rule.

    TYPES: ty_rules_t TYPE HASHED TABLE OF ty_rule WITH UNIQUE KEY fname.

簡單的示例如下,

DATA: rules TYPE zcl_adata_validator=>ty_rules_t.
*欄位1為必填欄位,型別是日期,欄位2非必填,型別為郵件地址
rules = VALUE #(
  ( fname = 'FIELD1' required = abap_true  user_type = zcl_adata_validator=>c_type_date )
  ( fname = 'FIELD2' user_type = zcl_adata_validator=>c_type_email )
).

擴充套件檢查功能

使用ABAP Data Validator自帶的檢查邏輯可以滿足很多場景下的基本檢查需要,但是你也許還有更多的檢查邏輯。有2種方式可以實現檢查功能的擴充套件。

  1. 通過rules-regex傳入正則表示式。
  2. 建立新的檢查型別:定義新的型別名,建立它的檢查類,實現介面zif_adv_check,將自定義檢查邏輯寫在is_valid方法的實現中。接著,將型別名和類名傳入zcl_adata_validator的構造方法constructor中。這樣就可以使用新的檢查型別了。

正則表示式示例:如果你不僅要檢查一個欄位的值是否為郵件地址,還要驗證它是gmail郵箱,那麼可以把正則表示式’gmail\.com$‘複製給rules-regex,

DATA: rules TYPE zcl_adata_validator=>ty_rules_t.

DATA: cases TYPE ty_case_t.

cases = VALUE #(
    ( field3 = '[email protected]') "正確,是gmail郵箱
    ( field3 = '[email protected]')    "不正確,非gmail郵箱
).

rules = VALUE #(
  ( fname = 'FIELD2' user_type = zcl_adata_validator=>c_type_email regex = 'gmail\.com$' regex_msg = 'Only gmail supported')
).

或者建立一個新型別和它的檢查類,並且把它的檢查類配置傳入constructor,

DATA: check_class_config TYPE zcl_adata_validator=>ty_check_config_t.

check_class_config = VALUE #( ( type = zcl_adata_validator=>c_type_new  class = 'ZCL_NEW_VALIDATOR' ) ).

TRY.
     DATA(result) = NEW zcl_adata_validator( check_class_conifg = check_class_config )->validate(
         rules   = rules
         data    = cases
     ).
CATCH zcx_adv_exception INTO DATA(ex).
    DATA(msg) = ex->get_text( ).
ENDTRY.

注意:constructor中已經包含了預設的檢查類配置和檢查訊息配置,它們是Hard coding,一旦傳入自定義配置,它們就會被覆蓋。

可以按需對方法進行重定義、或者傳入自己的配置。比如,從資料庫或其它來源讀取配置,這樣可以在不修改既有程式碼的情況下擴充套件程式的功能。

DATA: check_class_config TYPE zcl_adata_validator=>ty_check_config_t.

SELECT * FROM my_config_table INTO TABLE @check_class_config.

TRY.
    DATA(result) = NEW zcl_adata_validator( check_class_conifg = check_class_config )->validate(
        rules   = rules
        data    = cases
    ).
CATCH zcx_adv_exception INTO DATA(ex).
    DATA(msg) = ex->get_text( ).
ENDTRY.

 

專案地址

https://github.com/hhelibeb/abap-data-validator

我提交了絕大部分程式碼,此外還有2位貢獻者larshp和FreHu,他們幫助修正了程式碼格式和readme方面的一些問題。

歡迎參與這個工具的開發!如果你發現程式有任何問題,也可以在github提交issue告知我。

Q & A

怎樣定義有效性?

有效性的定義十分重要,如果有效的定義是模糊的,那檢查也失去了意義。

對於ABAP Data Validator,如果一個檢查型別存在對應的ABAP資料型別(比如日期,時間戳等),那麼“有效”是指:

  • 值可以直接被賦值給相應型別的ABAP變數,不產生異常,也不產生無意義的值。

以時間戳為例,

  • 2021-12-21 00:00:00   "無效
  • 19000000235959        "無效
  • 20200101235959        "有效

2021-12-21 00:00:00’\可能在某些情境下是合理的時間戳值,但是對於ABAP Data Validator而言,它是無效的,因為將它賦值給時間戳型別的變數會導致dump。而'19000000235959'雖然可以被賦值給ABAP時間戳變數,但因為沒有實際意義,它同樣是無效值。

這種型別檢查的邏輯主要由SAP提供的標準功能實現,時間戳的檢查實際上由正則檢查、標準函式DATE_CHECK_PLAUSIBILITY和TIME_CHECK_PLAUSIBILITY組成。

 

如果一個檢查型別沒有對應的ABAP資料型別,ABAP Data Validator的檢查邏輯收集自一些相對權威的來源,這些檢查邏輯通常會符合行業標準。來源包括,

  • Wikipedia (IMEI)
  • Mozilla Developer Network (Email)
  • regex-weburl.js (URL)
  • W3C Markup Validation Service (HTML)

ABAP Data Validator的檢查結果可靠嗎?

絕大部分檢查邏輯是成熟的,且它們都包含單元測試。你可以在單元測試中加上自己的用例來驗證它們的行為是否符合你的期望。

HTML檢查是個例外,它是唯一一個需要呼叫外部API(https://validator.w3.org/)的檢查,而且w3.org提供的API本身也只是實驗性的。請只把它的檢查結果當作參考。

目前,我已經在工作中部分地應用了ABAP Data Validator,看起來工作良好。

為什麼是多個類,而不是一個類、多個方法?

一個很自然的問題是為什麼不把這些檢查功能放到一個類裡面?畢竟,有時候為了幾行程式碼的檢查邏輯建立一個新類太過奢侈。 如果將所有檢查放在同一個類裡面,將會有一些好處,
  • 減少了全域性類數量,方便記憶/不易產生命名衝突。
  • 方法定義更靈活,不需要接受介面ZIF_ADV_CHECK的限制。
但是也會帶來一些損失,
  • 不同型別的檢查之間的耦合度增加,部署單個方法的更新時,可能會導致更多的LOAD_ PROGRAM CLASS_ MISMATCH異常。
  • 不同開發者的ABAP伺服器可能環境不同,如果個別方法不可用,問題將波及整個類,導致其它檢查也不可用。
  • 失去了介面介面ZIF_ADV_CHECK的約束後,動態程式設計會變得複雜,也難以保持檢查規則的簡單性。
總之,經過一番權衡之後我選擇了當前的設計,儘管它也存在某些缺點,如果你有更好的想法,請告訴我。

 

&nbs