Rest模式
有(yǒu)些時候客戶端環境可(kě)能(néng)比較複雜,如采用(yòng)非Java語言編寫的客戶端,如Javascript、C++或C#等,或者是客戶端不希望加入URule Pro的相關Jar包等等,但這些客戶端也需要調用(yòng)規則引擎進行業務(wù)規則的計算,這個時候傳統的獨立服務(wù)模式就有(yǒu)了用(yòng)武之地。
在URule Pro中(zhōng)提供了統一的Restful服務(wù)調用(yòng)接口,通過在知識包上進行簡單的配置,即可(kě)實現将業務(wù)規則計算暴露成Restful接口,對于客戶端來說,調用(yòng)接口時,隻需要符合要求的JSON格式數據即可(kě)實現業務(wù)規則計算,同時Restful接口也會返回統一的JSON格式數據作(zuò)為(wèi)計算結果輸出。
這裏需要特别強調的是,如果您當前打算把URule Pro用(yòng)在一個構建于SpringBoot之上的項目中(zhōng),那麽在配置知識包的Rest服務(wù)前需要重寫SpringBoot中(zhōng)的HiddenHttpMethodFilter攔截器。
SpringBoot在運行時會自動加入這個HiddenHttpMethodFilter攔截器,在這個攔截器裏又(yòu)調用(yòng)了HttpServletRequest的getParameter方法, 這就導緻後續在URule Pro中(zhōng)用(yòng)于處理(lǐ)Rest服務(wù)請求的Servlet通過HttpServletRequest獲取到的InputStream參數值為(wèi)null,從而會産(chǎn)生輸入數據為(wèi)空的異常。
要解決這一問題就需要重寫這個HiddenHttpMethodFilter攔截器,具(jù)體(tǐ)大家可(kě)以打開百度,搜索關鍵詞“SpringBoot中(zhōng)request.getInputStream()”,就可(kě)以看到大量關于這一問題的讨論和解決辦(bàn)法,這裏就不再贅述。
如果您單獨使用(yòng)了SpringMVC框架,那麽也會存在上面的問題,解決方法大家同樣可(kě)以在百度中(zhōng)搜索關鍵詞“SpringMVC中(zhōng)request.getInputStream()”,就可(kě)以看到對應的解決問題的辦(bàn)法。
配置Restful接口
打開知識包管理(lǐ)頁(yè)面,選中(zhōng)某一具(jù)體(tǐ)的知識包項目(知識包狀态處于啓用(yòng)時可(kě)操作(zuò),停用(yòng)狀态的知識無法配置),在目标知識包上點擊右鍵,在彈出的菜單中(zhōng)選擇Rest服務(wù)配置菜單項,即可(kě)彈出配置窗口,如下图所示:
配置窗口比較簡單,我們需要做的就是配置好輸入、輸出數據以及調用(yòng)時是否啓用(yòng)用(yòng)戶名(míng)密碼驗證即可(kě)。需要注意的是,輸入輸出數據必須要都配置好後才能(néng)保存,不能(néng)隻配置輸入數據不配置輸出數據,反過來也是一樣。
輸入、輸出信息配置完成,點擊配置窗口下方的的“查看Restful描述”按鈕,就可(kě)以看到當前知識包已配置好的Restful服務(wù)接口的描述數據,描述服務(wù)的格式為(wèi)JSON,内容如下所示:
{
"output": [
{
"name": "會員",
"clazz": "com.bstek.entity.Customer",
"fields": [
{
"name": "address",
"label": "地址",
"type": "List"
},
{
"name": "level",
"label": "等級",
"type": "String"
},
{
"name": "name",
"label": "名(míng)稱",
"type": "String"
}
]
}
],
"input": [
{
"name": "會員",
"clazz": "com.bstek.entity.Customer",
"fields": [
{
"name": "age",
"label": "年齡",
"type": "Integer"
},
{
"name": "gender",
"label": "性别",
"type": "Boolean"
}
]
}
],
"url": "http://localhost:8080/urule-pro-test/urule/rest/7",
"authentication": false
}
為(wèi)減少内容占用(yòng),上面的JSON中(zhōng)"......"代表隐去的部分(fēn)數據。
在上面的JSON格式調用(yòng)描述數據當中(zhōng),“input”和“output”屬性值分(fēn)别表示輸入和輸出數據,“url”表示的是調用(yòng)的URL;“authentication”屬性值表示的是當前Restful服務(wù)調用(yòng)是否需要用(yòng)戶名(míng)密碼驗證,這裏的值是false,表示不需要驗證。
需要注意的是,點擊窗口下的任何一個按鈕都會觸發保存操作(zuò)。
注意:這裏的輸入、輸出選擇會受當前知識包引用(yòng)的變量庫、參數庫的用(yòng)途屬性影響,用(yòng)途為(wèi)In或InOut類型的變量或參數,會在輸入中(zhōng)出現,用(yòng)途為(wèi)Out或InOut類型的變量或參數,會在輸出中(zhōng)出現。
Restful接口調用(yòng)測試
回到服務(wù)調用(yòng)配置窗口,點擊窗口下方的“Restful服務(wù)調用(yòng)測試”按鈕,就可(kě)以打開當前知識包的服務(wù)調用(yòng)測試頁(yè)面,如下图所示:
在測試頁(yè)面當中(zhōng),左邊為(wèi)要提交的JSON格式數據,這裏引擎已将我們配置中(zhōng)定義好的輸入數據轉換成标準的JSON格式,我們隻需要填充具(jù)體(tǐ)數據即可(kě)。
從图中(zhōng)可(kě)以看出,這裏的JSON格式的輸入數據,與我們想象中(zhōng)的對象的标準JSON格式略有(yǒu)不同。這裏,要提交的輸入數據可(kě)以有(yǒu)多(duō)個,所以JSON以"[ ]"包裹,表示為(wèi)一個集合類型的數據(當然,如果你配置的輸入數據對象隻有(yǒu)一個,那就直接提交一個對象就行;這裏的“會員”對象,用(yòng)name屬性來标明對象類型,這裏的name屬性用(yòng)的是我們規則變量庫裏定義的對象分(fēn)類名(míng),實際上,name屬性值也可(kě)以是變量庫裏對象類路徑全名(míng),無論用(yòng)哪個引擎都可(kě)以正确識别;接下來就是“fields”屬性,它是一個對象類型,裏面具(jù)體(tǐ)的屬性用(yòng)于标明當前對象需要的屬性名(míng)及屬性值,所有(yǒu)的屬性值都是一個空的字符串,實際填寫時需要根據對應屬性數據類型進行具(jù)體(tǐ)值的填充。可(kě)以看到,這裏對象屬性名(míng)采用(yòng)的是變量庫裏具(jù)體(tǐ)屬性的“字段名(míng)”,而非“标題”,實際使用(yòng)時即可(kě)以使用(yòng)“字段名(míng)”,也可(kě)以使用(yòng)“标題”,引擎都能(néng)正确識别。
了解輸入的JSON數據格式後,接下來就可(kě)以填充JSON數據,填充完成後,點擊工(gōng)具(jù)欄上的“提交數據”按鈕,即可(kě)将輸入的數據提交到目标知識包所暴露的Restful服務(wù)接口。提交數據後結果如下图所示:
可(kě)以看到,計算後的輸出數據是一個标準的JSON對象格式,“duration”屬性值表示當前業務(wù)規則計算耗時,單位為(wèi)毫秒(miǎo)(ms),這裏是0,表示時間非常的短(通常第一次計算時間較慢,這由Java特性導緻);“output”屬性值為(wèi)一個集合類型,通過“name”屬性值來 标明對象名(míng)稱,與變量庫裏定義的對象分(fēn)類名(míng)保持一緻;“class”屬性則标明對象類全名(míng),與變量庫裏定義的對象類路徑一緻;“fields”屬性值是一個對象類型,用(yòng)于标明當前對象的具(jù)體(tǐ)屬性名(míng)及其值,這裏的屬性名(míng)采用(yòng)的是屬性的字段名(míng),主要是方便後續JSON數據對象化處理(lǐ)。
上面的測試是通過URule内置的Restful服務(wù)測試頁(yè)面完成,實際使用(yòng)時也可(kě)以用(yòng)第三方測試工(gōng)具(jù)實現,比如上面的Restful服務(wù)就可(kě)以通過postman來進行測試,如下图所示:
如截图所示,在postman中(zhōng),輸入好請求的URL,我們這裏是http://localhost:8080/urule-pro-test/urule/rest/7,提交類型改為(wèi)POST(URule Pro提供的Restful服務(wù)隻接收POST類型的請求),輸入要提交的數據,我們這裏是要提交的JSON數據,數據格式與上面介紹的保持一緻,點擊“Send”按鈕,就可(kě)以得到響應結果。
在配置Restful服務(wù)時,還可(kě)以打開“用(yòng)戶名(míng)密碼驗證”選項,打開該選項後,我們需要輸入用(yòng)戶名(míng)及密碼信息,保存後再次請求這個Restful服務(wù)我們就需要在請求的Header裏加上用(yòng)戶和密碼信息,否則請求将不被允許,在URule Pro内置的Restful服務(wù)測試頁(yè)面裏, 如果當前Restful服務(wù)需要用(yòng)戶名(míng)密碼驗證,它會自動加上用(yòng)戶名(míng)密碼信息;而如果我們使用(yòng)postman來請求這個Restful服務(wù),若不在Header裏提供用(yòng)戶名(míng)密碼信息,那麽請求将會得到如下图所示信息:
我們可(kě)以在請求的Header中(zhōng)添加用(yòng)戶名(míng)密碼信息,Header的Key分(fēn)别是Username和Password,如下图所示:
實際應用(yòng)當中(zhōng),我們會在應用(yòng)在外層加上業務(wù)系統的安(ān)全管理(lǐ)功能(néng),比如使用(yòng)系統需要先進行登錄等,這時要保證URule Pro中(zhōng)内置的Restful服務(wù)可(kě)用(yòng),那麽就需要讓/urule/rest這個URL可(kě)匿名(míng)訪問,這點非常重要。
在使用(yòng)這個内置的Restful服務(wù)過程中(zhōng),如果出現錯誤,比如用(yòng)戶名(míng)密碼不正确或規則計算過程出現異常等,類似這些錯誤信息也會以一個标準的JSON格式返回,所有(yǒu)的錯誤消息(如果是異常則是異常的堆棧信息)會放在返回的JSON對象的error屬性中(zhōng), 如果沒有(yǒu)錯誤,則返回的JSON中(zhōng)就不會包含error屬性,這點從上面的示例中(zhōng)我們也已經看到。
複雜對象類型的支持
數據“客戶”對象裏有(yǒu)個名(míng)為(wèi)“cards”屬性,這個屬性是一個集合類型的屬性,所以如果該屬性為(wèi)空時我們需要給該屬性添加一個[]字符,表示一個空的集合屬性,如下所示:
[
{
"name": "客戶",
"fields": {
"cards": [],
"company.id": "",
"gender": "",
"company.level": "",
"degree": "",
"name": "",
"company.name": "",
"salary": "",
"married": "",
"age": ""
}
},
{
"name": "貸款信息",
"fields": {
"result": "",
"money": "",
"id": ""
}
}
]
如果我們需要填充這個集合屬性,那麽需要先看看變量庫裏定義的“卡”對象以及與卡對象相關的對象的結構,然後仿照上面的JSON寫出來就好;還有(yǒu)種簡單的方式,那就是在“服務(wù)配置”窗口中(zhōng)直接勾選“卡”對象以及與卡對象相關的對象,如下图所示:
保存後再次進入頁(yè)面,就可(kě)以看到如下图所示的輸入數據結構:
接下來,我們需要填充“cards”屬性,填充好的結構如下图所示:
可(kě)以看到,填充後的“cards”屬性是一個集合類型,裏面由若幹個“卡”對象構成,每個卡對象有(yǒu)四個屬性,其中(zhōng)“cardDetails”也是一個集合對象,這個集合對象是由若幹個“卡明細”對象組成。按照這樣的規則, 我們在構建輸入數據時就可(kě)以把輸入數據按照業務(wù)的需要構建的足夠複雜,可(kě)最大限度滿足業務(wù)需求。
在上面的截图中(zhōng),“客戶”對象還有(yǒu)“company.id”以及“company.level”兩個屬性,從命名(míng)以及變量庫裏對這兩個屬性的聲名(míng)可(kě)以看出,“客戶”這個對象下還有(yǒu)一個名(míng)為(wèi)company的子對象,“company.id”和“company.level”兩個屬性 是用(yòng)來填充company子對象的id以及level屬性,這裏我們直接輸入這兩個屬性值即可(kě)實現對company子對象的id以及level兩個屬性值的填充。
實際使用(yòng)時,如果company對象在變量庫中(zhōng)也有(yǒu)定義,那麽上面的寫法可(kě)直接改為(wèi)下面的樣子,依次類推:
{
"name": "客戶",
"fields": {
"cards": [],
"company":{
"name":"公(gōng)司",
"fields":{
"id":"bstek",
"level":12
}
},
"degree": "",
"name": "",
"company.name": "bstek",
"salary": "",
"married": "",
"age": ""
}
}
上面的寫法中(zhōng),要求我們在變量庫裏必須定義好名(míng)為(wèi)“公(gōng)司”的對象,否則解析的時候産(chǎn)生錯誤,如果沒有(yǒu)定義,那麽需要采用(yòng)“company.id”、“company.level”的定義方式。