Vincent.Chan‘s Blog

          常用鏈接

          統(tǒng)計(jì)

          積分與排名

          網(wǎng)站

          最新評(píng)論

          使用 AJAX 調(diào)用 SOAP Web 服務(wù),第 1 部分: 構(gòu)建 Web 服務(wù)客戶機(jī)

          級(jí)別: 中級(jí)

          James Snell, 軟件工程師, IBM

          2006 年 1 月 16 日

          本文介紹如何使用異步 JavaScript 和 XML (Asynchronous JavaScript and XML, AJAX) 設(shè)計(jì)模式來(lái)實(shí)現(xiàn)基于 Web 瀏覽器的 SOAP Web 服務(wù)客戶機(jī)。

          本文是一篇短的系列文章的第 1 部分,演示了如何使用針對(duì) Web 應(yīng)用程序的 AJAX 設(shè)計(jì)模式來(lái)實(shí)現(xiàn)跨平臺(tái)的基于 JavaScript 的 SOAP Web 服務(wù)客戶機(jī)。

          AJAX 已普遍用于許多知名的 Web 應(yīng)用程序服務(wù),例如 GMail、Google Maps、Flickr 和 Odeo.com。通過(guò)使用異步 XML 消息傳遞,AJAX 為 Web 開發(fā)人員提供了一種擴(kuò)展其 Web 應(yīng)用程序價(jià)值和功能的途徑。這里介紹的 Web Services JavaScript Library 擴(kuò)展了該基礎(chǔ)機(jī)制,其通過(guò)引入對(duì)調(diào)用基于 SOAP 的 Web 服務(wù)的支持來(lái)增強(qiáng) AJAX 設(shè)計(jì)模式。

          從瀏覽器中調(diào)用 Web 服務(wù)

          從 Web 瀏覽器中調(diào)用 SOAP Web 服務(wù)可能會(huì)比較麻煩,這是因?yàn)榇蠖鄶?shù)流行的 Web 瀏覽器在生成和處理 XML 方面都略有不同。所有瀏覽器都一致實(shí)現(xiàn)且用于 XML 處理的標(biāo)準(zhǔn) API 或功能少之又少。

          瀏 覽器實(shí)現(xiàn)人員一致支持的機(jī)制之一是 XMLHttpRequest API,它是 AJAX 設(shè)計(jì)模式的核心。developerWorks 網(wǎng)站最近發(fā)布的另一篇由 Philip McCarthy 撰寫的的文章詳細(xì)介紹了該 API。XMLHttpRequest 是一個(gè)用于執(zhí)行異步 HTTP 請(qǐng)求的 JavaScript 對(duì)象。Philip McCarthy 在其文章中描述了一個(gè)順序圖(請(qǐng)參見圖 1),此圖對(duì)于理解 XMLHttpRequest 對(duì)象如何支持 AJAX 設(shè)計(jì)非常有幫助(請(qǐng)參閱參考資料,以獲得指向全文的鏈接)。


          圖 1. Philip McCarthy 的 AJAX 順序圖
          Philip McCarthy 的 AJAX 順序圖

          從 此圖中,您可以清楚地看到 XMLHttpRequest 對(duì)象是如何工作的。一些運(yùn)行在 Web 瀏覽器內(nèi)的 JavaScript 創(chuàng)建了一個(gè) XMLHttpRequest 實(shí)例和一個(gè)用于異步回調(diào)的函數(shù)。然后,該腳本使用 XMLHttpRequest 對(duì)象對(duì)服務(wù)器執(zhí)行 HTTP 操作。在接收到響應(yīng)后,調(diào)用回調(diào)函數(shù)。在該回調(diào)函數(shù)內(nèi),可能處理返回的數(shù)據(jù)。如果返回的數(shù)據(jù)碰巧是 XML,則 XMLHttpRequest 對(duì)象將自動(dòng)使用瀏覽器中內(nèi)置的 XML 處理機(jī)制來(lái)解析該數(shù)據(jù)。

          遺憾的是,使用 AJAX 方法的主要難題在于 XMLHttpRequest 對(duì)象自動(dòng)解析 XML 的詳細(xì)過(guò)程。例如,假設(shè)我正在請(qǐng)求的數(shù)據(jù)是一個(gè) SOAP 信封,其包含來(lái)自許多不同 XML 命名空間的元素,并且我希望提取 yetAnotherElement 中屬性 attr 的值。(請(qǐng)參見清單 1


          清單 1. 一個(gè)包含多個(gè)命名空間的 SOAP 信封
          												
          														

          <s:Envelope
          xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"
          xmlns:xsd="http://www.w3.org/2001/XMLSchema"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
          <s:Header/>
          <s:Body>
          <m:someElement xmlns:m="http://example">
          <n:someOtherElement
          xmlns:n="http://example"
          xmlns:m="urn:example">
          <m:yetAnotherElement
          n:attr="abc"
          xmlns:n="urn:foo"/>
          </n:someOtherElement>
          </m:someElement>
          </s:Body>
          </s:Envelope>

          在 Mozilla 瀏覽器和 Firefox 瀏覽器中,提取 attr 屬性值非常簡(jiǎn)單,如清單 2所示。


          清單 2. 在 Mozilla 和 Firefox 中檢索 attr 屬性值的方法不能運(yùn)用在 Internet Explorer 中
          												
          														

          var m = el.getElementsByTagNameNS(
          'urn:example',
          'yetAnotherElement')[0].
          getAttributeNS(
          'urn:foo',
          'attr');
          alert(m); // displays 'abc'

          關(guān)于安全性

          由 于涉及許多實(shí)際安全問(wèn)題,因此在缺省情況下,大多數(shù) Web 瀏覽器中的 XMLHttpRequest 對(duì)象都限制為只能與用戶正在查看的 Web 頁(yè)所在的域中承載的資源和服務(wù)進(jìn)行交互。例如,如果我正在訪問(wèn)一個(gè)位于 http://example.com/myapp/ 的頁(yè)面,則 XMLHttpRequest 將只允許訪問(wèn)位于 example.com 域中的資源。對(duì)于阻止惡意應(yīng)用程序代碼潛在地對(duì)其不應(yīng)該訪問(wèn)的信息進(jìn)行不適當(dāng)?shù)脑L問(wèn),這種預(yù)防措施非常必要。因?yàn)檫@里介紹的 Web 服務(wù)客戶機(jī)基于 XMLHttpRequest,所以這種限制同樣適用于您將會(huì)調(diào)用的 Web 服務(wù)。

          如果您需要能夠訪問(wèn)位于另一個(gè)域中的 Web 服務(wù),您可以使用以下兩種合理的解決方案:

          • 對(duì) JavaScript 進(jìn)行數(shù)字簽名。通過(guò)對(duì) JavaScript 腳本進(jìn)行數(shù)字簽名,您就告訴了 Web 瀏覽器可以信任該腳本不會(huì)執(zhí)行任何惡意的活動(dòng),并且對(duì) XMLHttpRequest 可以訪問(wèn)的數(shù)據(jù)的限制也應(yīng)該取消。
          • 使用代理。一 個(gè)簡(jiǎn)單的解決方案是,通過(guò)位于加載的頁(yè)面所在的域中的代理資源來(lái)傳遞所有來(lái)自 XMLHttpRequest 的請(qǐng)求。該代理將 XMLHttpRequest 的請(qǐng)求轉(zhuǎn)發(fā)到遠(yuǎn)程位置,并將結(jié)果返回給瀏覽器。從 XMLHttpRequest 對(duì)象的角度來(lái)看,這種交互發(fā)生在現(xiàn)有的安全配置之內(nèi)。

          遺 憾的是,以上代碼無(wú)法在 Internet Explorer Version 6 中運(yùn)行,因?yàn)樵摓g覽器不僅沒(méi)有實(shí)現(xiàn) getElementsByTagNameNS 功能,而且事實(shí)上還使用了一種很糟糕的方法——將 XML 命名空間的前綴作為其元素和屬性名稱的一部分來(lái)對(duì)待。

          Internet Explorer 缺少對(duì) XML 命名空間的支持,這使得它很難處理命名空間密集的 XML 格式,例如采用獨(dú)立于瀏覽器的方式的 SOAP。即使要執(zhí)行一些像提取結(jié)果中的屬性值這樣簡(jiǎn)單的操作,您也必須編寫能夠在多個(gè)瀏覽器中實(shí)現(xiàn)一致預(yù)期行為的特殊代碼。幸運(yùn)的是,這種特殊代碼可以 封裝并重用。

          為了從 Web 瀏覽器中調(diào)用 Web 服務(wù),并可靠地處理 SOAP 消息,您需要首先了解一些安全問(wèn)題(請(qǐng)參見側(cè)欄“關(guān)于安全性”)。此外,您還需要編寫一個(gè) JavaScript 腳本庫(kù)(圖 2),以便將底層瀏覽器 XML 實(shí)現(xiàn)中的不一致情況抽象出來(lái),從而使您能夠直接處理 Web 服務(wù)數(shù)據(jù)。


          圖 2. 在使用 Web Services JavaScript Library 的 Web 瀏覽器中通過(guò) Javascript 調(diào)用 Web 服務(wù)
          在 Web 瀏覽器中通過(guò) Javascript 調(diào)用 Web 服務(wù)

          圖 2 中的 Web Services JavaScript Library (ws.js) 是一組 JavaScript 對(duì)象和實(shí)用功能,它們?yōu)榛?SOAP 1.1 的 Web 服務(wù)提供了基本的支持。Ws.js 定義了下列對(duì)象:

          • WS.Call:一個(gè)包裝了 XMLHttpRequest 的 Web 服務(wù)客戶機(jī)
          • WS.QName:XML 限定名實(shí)現(xiàn)
          • WS.Binder:自定義 XML 序列化器/反序列化器的基礎(chǔ)
          • WS.Handler:請(qǐng)求/響應(yīng)處理程序的基礎(chǔ)
          • SOAP.Element:包裝了 XML DOM 的基本 SOAP 元素
          • SOAP.Envelope:SOAP Envelope 對(duì)象擴(kuò)展了 SOAP.Element
          • SOAP.Header:SOAP Header 對(duì)象擴(kuò)展了 SOAP.Element
          • SOAP.Body:SOAP Body 對(duì)象擴(kuò)展了 SOAP.Element
          • XML:用于處理 XML 的跨平臺(tái)實(shí)用方法

          ws.js 的核心是 WS.Call 對(duì)象,該對(duì)象提供了調(diào)用 Web 服務(wù)的方法。WS.Call 主要負(fù)責(zé)與 XMLHttpRequest 對(duì)象進(jìn)行交互,并處理 SOAP 響應(yīng)。

          WS.Call 對(duì)象公開了以下三個(gè)方法:

          • add_handler。向處理鏈添加請(qǐng)求/響應(yīng)處理程序。處理程序?qū)ο笤谡{(diào)用 Web 服務(wù)的前后被調(diào)用,以支持可擴(kuò)展的預(yù)調(diào)用處理和后調(diào)用處理。
          • invoke。將指定的 SOAP.Envelope 對(duì)象發(fā)送給 Web 服務(wù),然后在接收到響應(yīng)后調(diào)用回調(diào)函數(shù)。當(dāng)調(diào)用使用文本 XML 編碼的文檔樣式的 Web 服務(wù)時(shí),請(qǐng)使用此方法。
          • invoke_rpc。創(chuàng)建一個(gè)封裝 RPC 樣式請(qǐng)求的 SOAP.Envelope,并將其發(fā)送到 Web 服務(wù)。當(dāng)接收到響應(yīng)時(shí),調(diào)用回調(diào)函數(shù)。

          在 通常情況下,WS.Call 對(duì)象只不過(guò)是位于 XMLHttpRequest 對(duì)象頂層的瘦包裝器 (thin wrapper),該包裝器能夠執(zhí)行許多簡(jiǎn)化處理的操作。這些操作包括設(shè)置 SOAP 1.1 規(guī)范要求的 SOAPAction HTTP Header。





          回頁(yè)首


          使用 ws.js

          Web services JavaScript Library 提供的 API 非常簡(jiǎn)單。

          SOAP.* 對(duì)象(SOAP.ElementSOAP.EnvelopeSOAP.HeaderSOAP.Body)提供了構(gòu)建和讀取 SOAP 信封的方法,如清單 3 所示,因而處理 XML 文檔對(duì)象模型的底層細(xì)節(jié)就順利地抽象出來(lái)。


          清單 3. 構(gòu)建一個(gè) SOAP 信封
          												
          														

          var envelope = new SOAP.Envelope();
          var body = envelope.create_body();
          var el = body.create_child(new WS.QName('method','urn:foo'));
          el.create_child(new WS.QName('param','urn:foo')).set_value('bar');

          清單 4 顯示了由 清單 3 中的代碼生成的 SOAP 信封。


          清單 4. 構(gòu)建一個(gè) SOAP 信封
          												
          														

          <Envelope xmlns="http://schemas.xmlsoap.org">
          <Body>
          <method xmlns="urn:foo">
          <param>bar</param>
          </method>
          </Body>
          </Envelope>

          如果您正在創(chuàng)建的 SOAP 信封代表一個(gè) RPC 樣式的請(qǐng)求,則 SOAP.Body 元素提供了一個(gè)簡(jiǎn)便方法 set_rpc(如清單 5 所示),該方法能夠構(gòu)造一個(gè)完整的 RPC 請(qǐng)求——包含一個(gè)指定的操作名稱、一個(gè)指定的輸入?yún)?shù)數(shù)組和一個(gè) SOAP 編碼樣式的 URI。


          清單 5. 構(gòu)建一個(gè) RPC 請(qǐng)求信封
          												
          														

          var envelope = new SOAP.Envelope();
          var body = envelope.create_body();
          body.set_rpc(
          new WS.QName('param','urn:foo'),
          new Array(
          {name:'param',value:'bar'}
          ), SOAP.NOENCODING
          );

          每個(gè)參數(shù)都作為一個(gè) JavaScript 對(duì)象結(jié)構(gòu)進(jìn)行傳遞,且可能帶有以下屬性:

          • name。一個(gè)指定參數(shù)名稱的字符串或 WS.QName 對(duì)象。必需
          • value。參數(shù)的值。如果該值不是一個(gè)簡(jiǎn)單數(shù)據(jù)類型(例如,字符串、整數(shù)或其他),則應(yīng)該指定一個(gè)能將該值序列化為適當(dāng)?shù)?XML 結(jié)構(gòu)的 WS.Binder。必需
          • xsitype:標(biāo)識(shí)參數(shù)的 XML 模式實(shí)例類型的 WS.QName(例如,xsi:type="int" 對(duì)應(yīng) xsitype:new WS.QName('int','http://www.w3.org/2000/10/XMLSchema'))。可選
          • encodingstyle:標(biāo)識(shí)參數(shù)所使用的 SOAP 編碼樣式的 URI。可選
          • binder:能夠?qū)?shù)序列化為 XML 的 WS.Binder 實(shí)現(xiàn)。可選

          例如,如果要指定的參數(shù)名為“abc”、XML 命名空間為“urn:foo”、xsi:type 為“int”且值為“3”,則我會(huì)使用以下代碼:new Array({name:new WS.QName('abc','urn:foo'), value:3, xsitype:new WS.QName('int','http://www.w3.org/2000/10/XMLSchema')})

          一旦我為服務(wù)請(qǐng)求構(gòu)建了 SOAP.Envelope,我就會(huì)將該 SOAP.Envelope 傳遞到 WS.Call 對(duì)象的 invoke 方法,以便調(diào)用該信封內(nèi)編碼的方法: (new WS.Call(service_uri)).invoke(envelope, callback)

          另一種可選方案是手動(dòng)構(gòu)建 SOAP.Envelope。我會(huì)將參數(shù) WS.QName、參數(shù)數(shù)組和編碼樣式傳遞到 WS.Call 對(duì)象的 invoke_rpc 方法,如清單 6 所示。


          清單 6. 使用 WS.Call 對(duì)象調(diào)用 Web 服務(wù)
          												
          														

          var call = new WS.Call(serviceURI);
          var nsuri = 'urn:foo';
          var qn_op = new WS.QName('method',nsuri);
          var qn_op_resp = new WS.QName('methodResponse',nsuri);
          call.invoke_rpc(
          qn_op,
          new Array(
          {name:'param',value:'bar'}
          ),SOAP.NOENCODING,
          function(call,envelope) {
          // envelope is the response SOAP.Envelope
          // the XML Text of the response is in arguments[2]
          }
          );

          在調(diào)用 invoke 方法或 invoke_rpc 方法時(shí),WS.Call 對(duì)象會(huì)創(chuàng)建一個(gè)基本的 XMLHttpRequest 對(duì)象,用包含 SOAP 信封的 XML 元素進(jìn)行傳遞,并接收和解析響應(yīng),然后調(diào)用提供的回調(diào)函數(shù)。

          為了能夠擴(kuò)展 SOAP 消息的預(yù)處理和后處理,WS.Call 對(duì)象允許您注冊(cè)一組 WS.Handler 對(duì)象,如清單 7 所示。對(duì)于調(diào)用周期內(nèi)的每個(gè)請(qǐng)求、每個(gè)響應(yīng)和每個(gè)錯(cuò)誤,都將調(diào)用這些對(duì)象。可以通過(guò)擴(kuò)展 WS.Handler JavaScript 對(duì)象來(lái)實(shí)現(xiàn)新的處理程序。


          清單 7. 創(chuàng)建和注冊(cè)響應(yīng)/響應(yīng)處理程序
          												
          														

          var MyHandler = Class.create();
          MyHandler.prototype = (new WS.Handler()).extend({
          on_request : function(envelope) {
          // pre-request processing
          },
          on_response : function(call,envelope) {
          // post-response, pre-callback processing
          },
          on_error : function(call,envelope) {
          }
          });

          var call = new WS.Call(...);
          call.add_handler(new MyHandler());

          處理程序?qū)Σ迦牖蛱崛≌趥鬟f的 SOAP 信封中的信息最有用。例如,您可以設(shè)想一個(gè)處理程序自動(dòng)向 SOAP Envelope 的 Header 插入適當(dāng)?shù)?Web 服務(wù)尋址 (Web Services Addressing) 元素,如清單 8 中的示例所示。


          清單 8. 一個(gè)將 Web 服務(wù)尋址操作 Header 添加到請(qǐng)求中的處理程序示例
          												
          														

          var WSAddressingHandler = Class.create();
          WSAddressingHandler.prototype = (new WS.Handler()).extend({
          on_request : function(call,envelope) {
          envelope.create_header().create_child(
          new WS.QName('Action','http://ws-addressing','wsa')
          ).set_value('http://www.example.com');
          }
          });

          WS.Binder 對(duì)象(清單 9)執(zhí)行 SOAP.Element 對(duì)象的自定義序列化和反序列化。WS.Binder 的實(shí)現(xiàn)必須提供以下兩個(gè)方法:

          • to_soap_element。將 JavaScript 對(duì)象序列化為 SOAP.Element。傳入的第一個(gè)參數(shù)是要序列化的值。第二個(gè)參數(shù)是 SOAP.Element,必須將要序列化的值序列化為 SOAP.Element。該方法不返回任何值。
          • to_value_object。將 SOAP.Element 反序列化為 JavaScript 對(duì)象。該方法必須返回反序列化的值對(duì)象。

          清單 9. WS.Binding 實(shí)現(xiàn)示例
          												
          														

          var MyBinding = Class.create();
          MyBinding.prototype = (new WS.Binding()).extend({
          to_soap_element : function(value,element) {
          ...
          },
          to_value_object : function(element) {
          ...
          }
          });





          回頁(yè)首


          一個(gè)簡(jiǎn)單示例

          我已經(jīng)提供了一個(gè)示例項(xiàng)目來(lái)闡釋 Web Services JavaScript Library 的基本功能。該演示所使用的 Web 服務(wù)(如清單 10 所示)已經(jīng)在 WebSphere Application Server 中進(jìn)行了實(shí)現(xiàn),并提供了簡(jiǎn)單的 Hello World 功能。


          清單 10. 一個(gè)簡(jiǎn)單的基于 Java 的“Hello World”Web 服務(wù)
          												
          														

          package example;

          public class HelloWorld {
          public String sayHello(String name) {
          return "Hello " + name;
          }
          }

          在實(shí)現(xiàn)了該服務(wù)并將其部署到 WebSphere Application Server 后,該服務(wù)(清單 11)的 WSDL 描述定義了您需要傳遞的 SOAP 消息(用于調(diào)用 Hello World 服務(wù))。


          清單 11. HelloWorld.wsdl 的代碼片段
          												
          														

          <wsdl:portType name="HelloWorld">
          <wsdl:operation name="sayHello">
          <wsdl:input
          message="impl:sayHelloRequest"
          name="sayHelloRequest"/>
          <wsdl:output
          message="impl:sayHelloResponse"
          name="sayHelloResponse"/>
          </wsdl:operation>
          </wsdl:portType>

          通過(guò)使用 Web Services JavaScript Library,您可以實(shí)現(xiàn)一個(gè)調(diào)用 Hello World 服務(wù)的方法,如清單 12所示。


          清單 12. 使用 WS.Call 調(diào)用 HelloWorld 服務(wù)
          												
          														

          <html>
          <head>
          ...
          <script
          type="text/javascript"
          src="scripts/prototype.js"></script>
          <script
          type="text/javascript"
          src="scripts/ws.js"></script>
          <script type="text/javascript">
          function sayHello(name, container) {
          var call = new WS.Call('/AjaxWS/services/HelloWorld');
          var nsuri = 'http://example';
          var qn_op = new WS.QName('sayHello',nsuri);
          var qn_op_resp = new WS.QName('sayHelloResponse',nsuri);
          call.invoke_rpc(
          qn_op,
          new Array(
          {name:'name',value:name}
          ),null,
          function(call,envelope) {
          var ret =
          envelope.get_body().get_all_children()[0].
          get_all_children()[0].get_value();
          container.innerHTML = ret;
          $('soap').innerHTML = arguments[2].escapeHTML();
          }
          );

          }
          </script>
          </head>
          ...

          然后,您可以在我們的 Web 應(yīng)用程序中的任意位置通過(guò)調(diào)用 sayHello 函數(shù)來(lái)調(diào)用 Hello World 服務(wù)。請(qǐng)參見清單 13


          清單 13. 調(diào)用 sayHello 函數(shù)
          												
          														

          <body>
          <input name="name" id="name" />
          <input value="Invoke the Web Service"
          type="button"
          onclick="sayHello($('name').value,$('result'))" />
          <div id="container">Result:
          <div id="result">
          </div>
          <div id="soap">
          </div>
          </div>
          </body>

          調(diào)用成功后的結(jié)果如圖 3 所示。在 Mozilla、Firefox 和 Internet Explorer 中運(yùn)行該示例應(yīng)該會(huì)得到相同的結(jié)果。


          圖 3. Firefox 中的 Hello World 示例
          Firefox 中的 Hello World 示例




          回頁(yè)首


          后續(xù)部分

          使 用 Web Services JavaScript Library,可以采用簡(jiǎn)單的獨(dú)立于瀏覽器的方式將基本的 SOAP Web 服務(wù)合并到 Web 應(yīng)用程序中。在本系列的下一個(gè)部分中,您不僅可以探討如何使用該庫(kù)來(lái)調(diào)用更多基于 Web 服務(wù)資源框架 (WS-Resource Framework ) 系列規(guī)范的高級(jí) Web 服務(wù),而且還可以了解擴(kuò)展該 Web 服務(wù)功能并將其集成到 Web 應(yīng)用程序中的方法。






          回頁(yè)首


          下載

          描述 名字 大小 下載方法
          Sample project ws-wsajaxcode.zip 19 KB ?FTP
          關(guān)于下載方法的信息 Get Adobe? Reader?




          回頁(yè)首


          參考資料

          學(xué)習(xí)

          獲得產(chǎn)品和技術(shù)

          討論




          回頁(yè)首


          關(guān)于作者

          James M Snell 的照片

          James Snell 是 IBM Emerging Technologies Toolkit 團(tuán)隊(duì)的成員。在過(guò)去幾年里,他一直致力于新興 Web 服務(wù)技術(shù)和標(biāo)準(zhǔn)的研究,并且還參與了 Atom 1.0 規(guī)范的制定。他現(xiàn)在維護(hù)著一個(gè)專注于新興技術(shù)的博客 http://www.ibm.com/developerworks/blogs/dw_blog.jspa?blog=351

          posted on 2006-03-19 16:00 Vincent.Chen 閱讀(322) 評(píng)論(0)  編輯  收藏 所屬分類: AJAX

          主站蜘蛛池模板: 阳曲县| 平遥县| 兰考县| 长葛市| 龙里县| 绍兴县| 吉隆县| 庆元县| 洪湖市| 嘉义市| 黑河市| 翁牛特旗| 隆回县| 内江市| 广河县| 马边| 元江| 门源| 琼海市| 尤溪县| 吉林省| 启东市| 阿巴嘎旗| 隆尧县| 达尔| 万山特区| 太和县| 阜平县| 禹城市| 吉林市| 长宁县| 沽源县| 湖口县| 波密县| 闽侯县| 阳谷县| 茶陵县| 玛纳斯县| 柳林县| 石首市| 徐闻县|