第二篇:利用shell腳本執行webservice請求——基於soap


1. 項目背景

以往我們在開發基於webservice的項目中,我們總習慣於直接使用webservice的一些框架,如Axis,axis2和Xfire等。框架的好處是將webservice所涉及到的soap協議、wsdl以及uddi都封裝起來,我們只需要直接調用方法傳值並執行請求就可以。但框架也有缺陷,比如axis2要基於java工程,哪怕只是一個簡單的功能我們都要搭建一個java web項目去實現它。這樣做的后果就是:

1. 占用服務器磁盤資源(一個java web項目至少也要幾M);
2. 占用內存資源。特別是對那些不需要頁面的項目來說,java web實在是大材小用了。

就拿我最近開發的短信定時發送項目為例。在這個項目里,我們只需要每天從數據庫取數據然后拼成短信,定時調用總公司的短信接口發送短信就可以了。不需要任何圖形界面,功能也很單一,如果用java項目去做就太小題大做了。所以我們想到一個巧妙的辦法,直接利用webservice的底層知識去實現這個功能。即直接用Linux的shell腳本生成soap消息,然后用soap請求webservice。

2. 實施過程

2.1項目分析

上文提到的短信項目是一個為了實現我們公司辦公信息化的小項目。其主要功能就是每天從數據庫讀取分公司即各支公司保費收入及業務達成情況,然后組合成短信發給各個領導以便於其合理調整公司發展策略。

這個項目有兩個特點:

1. 功能比較單一;
2. 不需要圖形界面操作。因此我們完全可以拋開java項目,直接用shell腳本去實現。

2.2實現流程

2.2.1 技術介紹

在介紹實現過程之前我們先介紹下soap協議。Soap協議全稱為simple Object Access Protocol,是一個基於http的訪問協議,這里的simple Object指代就是soap消息,它本質上是一個xml格式的文件,必須包含以下元素:

* 	必需的 Envelope 元素,可把此 XML 文檔標識為一條 SOAP 消息
*  可選的 Header 元素,包含頭部信息
*  	必需的 Body 元素,包含所有的調用和響應信息	
*   可選的 Fault 元素,提供有關在處理此消息所發生錯誤的信息

同時soap協議還要基於POST請求,因為post請求有body域,可以用來封裝soap消息。

Soap請求的方式有很多種,可以在框架中請求(后文中有介紹,直接封裝在框架中,用戶只要傳值就可以)。也可以自己基於格式編寫soap消息,直接用http命令請求,比如Linux的curl命令。相對來說后者的效率會更高些。

2.3.2 具體實現

2.3.2.1 soap消息生成工具腳本

由於soap消息是一個有固定格式的xml文件,因此我們可以用工具腳本去生成。其代碼如下所示:

#!/bin/bash
# filename: create_xml.sh
# create_MarsCheng_20160922
# 從外部傳入的第一個參數作為xml的文件名
outfile=$1
# xml中的縮進位
tabs=0
# ++++++++++++++++++++++++++++
# 組裝一個節點,輸出到文件
# 說一說傳參數時的這幾個區別:假如有下面這個腳本執行的命令
# /path/to/scriptname  opt1  opt2  opt3  opt4 
# $0: 的值是默認是腳本的名字,從$1-$4 開始就是參數的值
# $# :代表后接的參數『個數』
# $@ :代表『 "$1" "$2" "$3" "$4" 』之意,每個變量是獨立的(用雙引號括起來); 
# $* :代表『 "$1c$2c$3c$4" 』,其中 c 為分隔字節,默認為空白鍵, 所以本例中代表『 "$1 $2 $3 $4" 』之意。
# 在shell中我們可以也可以使用${}包含變量名,來調用變量
# ++++++++++++++++++++++++++++
put(){
    echo '<'${*}'>' >> /tmp/sms/request/$outfile
}

# 這里也是輸出一個xml的節點,只是比上面的節點有更多的設置
# ${@:3} 的意思:它的值就是由第二個參數開始到最后一個參數,為什么要這樣?有時可能你的第二個參數中有空格,shell接受參數是以空格計算的
put_tag() {
    echo '<'${1}' xsi:type="'${2}'">'${@:3}'</'${1}'>' >> /tmp/sms/request/$outfile
}
# 同樣是一個輸出節點函數,但是添加了CDATA,防止特殊字符造成xml解析失敗
put_tag_cdata() {
    echo '<'${1}' xsi:type="'${2}'"><![CDATA['${@:3}']]></'${1}'>' >> /tmp/sms/request/$outfile
}
#由於soap消息的頭部一部分一般固定,因此可以直接寫一個固定的方法輸出
put_head(){
	echo –e “…”>> /tmp/sms/request/$outfile
}
#由於soap消息的尾部一部分一般固定,因此可以直接寫一個固定的方法輸出
put_end(){
	echo –e “…”>> /tmp/sms/request/$outfile
}
tag_start(){
	str=""
	str=${1}' xsi:type="'${2}'"'
    put $str
}

tag_start_array(){
	str=""
	str=${1}' q1:arrayType="'${2}'" xsi:type="q1:Array"' 
	put $str
}

tag() {
	str=""
	str=${1}' xsi:type="'${2}'" /'
    put $str
}

tag_value(){
	if [ "$1" == 0 ]
    then
        put_tag ${2} ${3} $(echo ${@:4})
    elif [ "$1" == 1 ]
    then
        put_tag_cdata ${2} ${3} $(echo ${@:4})
    fi
}

tag_end(){
    put '/'${1}
}

2.3.2.2具體執行腳本

具體執行請求的腳本,在這個腳本里會調用soap消息生成工具腳本,會連接數據庫讀取短信內容,最后用curl實現soap請求。

#統計日期,即當前日期前一天
cal_date=$(date -d -1day +%Y-%m-%d)
#短信是否發送標記,若為1則表示短信已發送,否則為0
flag=$(sqlplus -S statistic/123@tjfxdb << EOF
        set heading off feedback off pagesize 0 verify off echo off
        select flag from statistic.smsflg where calDate ='$cal_date';
        commit;
        quit;
EOF)

if [ ! -n "$flag" ];
then
#從數據庫取出短信內容,存入smsinfo
smsinfo=$(sqlplus -S statistic/123@tjfxdb << EOF
        set heading off feedback off pagesize 0 verify off echo off
        select smsinfo from statistic.smsInfo where calDate =date'$cal_date' and smscode ='AHYZ' and bvalid = 1 ;
        commit;
        quit;
EOF)

if [ ! -n "$smsinfo" ]; #若短信內容為空,不發送
then
echo $(date "+%Y-%m-%d %H:%M:%S")" The content is not ready.">> /tmp/sms/log/$cal_date'.log'
else
#生成soap xml文件名
file_name='request'$(date "+%Y%m%d%H%M%S")'.xml'
#調用xml的生成腳本
source '/tmp/sms/create_xml.sh' $file_name
put_head

taskID=$(date "+%s")
declare -i message_size=1
declare -i labels_size=4
name=('DEPTCODE' 'CUR_DATE' 'FUN_DESC' 'CONTENT')
value[0]='123456'
value[1]=$cal_date'日'
value[2]='保費達成情況'
value[3]=$smsinfo
#短信發送對象
telephone=(‘12345678')

message_str2='q2:SmsMessage['$message_size']'
label_str2='q2:Label['$labels_size']'
tag_start_array 'messages' $message_str2
declare -i j=0

#短信核心主體
while ((j<message_size))
do
	tag_start 'SmsMessage' 'q2:SmsMessage'
	tag_value 0 'packetLength' 'xsd:int' '0'
	tag_value 0 'contents' 'xsd:string' '中國人壽'
	tag_start_array 'labels' $label_str2
	declare -i i=0
	while ((i<labels_size))
	do
		tag_start 'Label' 'q2:Label'
		tag_value 0 'packetLength' 'xsd:int' '0'
		tag_value 0 'labelName' 'xsd:string' ${name[i]}
		if [ $i == 3 ]
		then
			tag_value 1 'labelValue' 'xsd:string' ${value[i]}
		else
			tag_value 0 'labelValue' 'xsd:string' ${value[i]}
		fi
		tag_end 'Label'
		let i++
	done
	tag_end 'labels'
	tag_value 0 'orgCode' 'xsd:string' '123456'
	tag_value 0 'receiver' 'xsd:string' ${telephone[j]}
	tag_end 'SmsMessage'
	let j++
done
tag_end 'messages'
tag_value 0 'taskId' 'xsd:string' $taskID
put_end

#請求webservice服務發送短信
curl -H "Content-Type: text/xml;charset=UTF-8" -H "SOAPAction: "http://WebXml.com.cn/getStockInfoByCode""  -d @/tmp/sms/request/$file_name http://Xxxxxxxxxx  >> /tmp/sms/log/$cal_date'.log'
#寫短信發送標記位
sqlplus -S statistic/123@tjfxdb << EOF
        set heading off feedback off pagesize 0 verify off echo off
        insert into smsflg(caldate,flag) values('$cal_date',1);
        commit;
        quit;
EOF

分析:

curl是一個利用URL規則在命令行下工作的文件傳輸工具,它支持文件的上傳和下載,因此在腳本中我們用它來進行soap請求。其命令格式如下。

-H后面跟的是http請求頭,’SOAPAction’鍵通常用來表示這個HTTP請求是一個web服務請求,同時取值可以為空字符串,但是也可以是請求的web服務操作的名稱。

2.3.2.3定時執行

為了讓系統更加智能化,執行腳本可以加入Linux的crontab定時任務當中。

40 10-18 * * * root sh /tmp/sms/sms_send.sh

上面表示每天10點到18點的40分,腳本都會啟動一次,因為在執行腳本中我們加了短信發送標志位,因此不用擔心短信回重復發送。

3. 經驗總結

Webservice接口的請求方式有很多種,可以通過框架請求,也可以在java項目中直接基於soap請求,還可以像我們這樣直接用shell腳本來完成請求。不同的方式有不同的優缺點,我們在實際項目中要學會靈活選擇,適合的才是最好的。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM