使用ABAP Data Validator驗證數據有效性

在平常的開發過程當中,咱們經常要處理不一樣來源的數據。數據可能來自不可靠的外部系統、不可靠的用戶輸入和甚至設計有誤的數據庫表,所以,對數據有效性進行驗證是必要的工做。html

開源工具ABAP Data Validator是一個使用ABAP開發的數據驗證工具,它能夠簡化開發者在這方面的工做。本文將介紹它的用法和一些設計思路。git

 

本文連接:http://www.javashuo.com/article/p-xyujegzf-ha.htmlgithub

原創內容,轉載請註明web

目的

具體而言,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( 'example@github.com' ).
 "do something
ENDIF.

全部的檢查類都實現了接口zif_adv_check,所以它們都擁有靜態方法is_valid,以下圖(爲了方便閱讀,圖中沒有包含所有的實現類),

 

對於類方法而言,is_valid是個別名,也能夠不使用別名來調用接口方法:

IF zcl_adv_email_check=>zif_adv_check~is_valid( 'example@github.com' ).
 "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 = 'ZZZ2@gmail.com') "正確,是gmail郵箱
    ( field3 = 'ZZZ2@qq.com')    "不正確,非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位貢獻者larshpFreHu,他們幫助修正了代碼格式和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的檢查邏輯收集自一些相對權威的來源,這些檢查邏輯一般會符合行業標準。來源包括,

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的約束後,動態編程會變得複雜,也難以保持檢查規則的簡單性。
總之,通過一番權衡以後我選擇了當前的設計,儘管它也存在某些缺點,若是你有更好的想法,請告訴我。
相關文章
相關標籤/搜索