人人做人人澡人人爽欧美,国产主播一区二区,久久久精品五月天,羞羞视频在线观看免费

當(dāng)前位置:蘿卜系統(tǒng)下載站 > 技術(shù)開發(fā)教程 > 詳細(xì)頁面

“穿越”防火墻的XML技術(shù)

“穿越”防火墻的XML技術(shù)

更新時間:2022-06-05 文章作者:未知 信息來源:網(wǎng)絡(luò) 閱讀次數(shù):

程序員可能會經(jīng)常碰到這樣的事情:建立一個servlet應(yīng)用程序,它與公司的數(shù)據(jù)庫相連接,為客戶提供一種特定的服務(wù),這個應(yīng)用程序受到一個強大的驗證機制保護,全世界有成千上萬的客戶都在使用它。現(xiàn)在就出現(xiàn)了一個問題:當(dāng)應(yīng)用程序處在公司的防火墻之外時,你將如何從應(yīng)用程序提供用戶對數(shù)據(jù)庫的訪問?你知道,網(wǎng)絡(luò)管理員是不會專門為你的應(yīng)用程序與數(shù)據(jù)庫相連接而打開一個特殊端口的。

HTTP隧道技術(shù)和XML
如何越過防火墻與客戶/服務(wù)器應(yīng)用程序相連接這個問題已經(jīng)困擾程序員很久了。在多數(shù)情況下,一個公司的防火墻總是盡可能少地打開端口。一般情況下,你能夠使用的唯一端口就是80,這也就是Web所使用的端口。

解決這個問題的方法就是使用HTTP隧道技術(shù)(HTTP tunneling)。這個方法首先將請求包裝在一個HTTP POST請求中,然后這個請求再由一個在防火墻內(nèi)的Web 服務(wù)器上的CGI 應(yīng)用程序(例如一個servlet)來處理。

Servlet恢復(fù)原始的請求,執(zhí)行它,然后將結(jié)果插入到HTTP響應(yīng)流中。防火墻將這種相互作用解釋為對一個 Web頁面的常規(guī)請求,并允許對它繼續(xù)進行處理。這就象特洛伊木馬一樣:看起來是一個普通的請求,但其中隱藏著預(yù)料不到的負(fù)載。

下一個問題是如何對請求進行格式化?當(dāng)然,使用XML是將這些負(fù)載送入一個HTTP請求中去的最佳選擇。這個觀念很有獨創(chuàng)性,HTTP之上的XML是一個熱門的新興領(lǐng)域,一些新的規(guī)則正在編寫中,將來可以成為分布式應(yīng)用程序的標(biāo)準(zhǔn)通訊協(xié)議。其中,簡單對象訪問協(xié)議(SOAP)是最得到公認(rèn)的。

遺憾的是,現(xiàn)在還沒有一個穩(wěn)定執(zhí)行的SOAP供我們使用,目前所能找到的最好的一個來自Apache集團,但是它只支持簡單的返回類型。因此,它對于我們的項目是沒有用的。但是這也不錯,這意味著我們可以提出自己的HTTP上的XML的協(xié)議,并且借此來學(xué)習(xí)其中包含的概念。
概念
現(xiàn)在我們來建立一個簡單的框架結(jié)構(gòu)(framework),它將HTTP上的XML作為基本的通訊策略,從而讓我們能夠創(chuàng)建一套服務(wù),而且使得這些服務(wù)從分布在Internet上四面八方的桌面應(yīng)用程序都可以進行訪問。

首先,我們需要建立普通請求和響應(yīng)的語法。請求看起來是這樣的:

<?xml version='1.0' encoding='utf-8' ?>
<http-request>
<requestType>
[type of request]
</requestType>
<request>
[Application specific request.This will be an XML Elment ]
</request>
</http-request>

響應(yīng)看起來是這樣的:

<?xml version='1.0' encoding='utf-8' ?>
<response>
<responseMessage>
[the response Message]
</responseCode>
<responseCode>
[an application specific return code.]
</responseCode>
<response>
[Application specific request.This will be an XML Element]
</response>
</http-response>

為了理解這個框架結(jié)構(gòu)背后的概念,我們要編寫一個應(yīng)用服務(wù)例程:一個簡單的數(shù)據(jù)庫服務(wù),它對任何SQL語句進行處理,并且將結(jié)果作為一個向量來返回。請求的細(xì)節(jié),也就是請求元素所包含的XML標(biāo)記非常簡單,如下:

<sql-statement>
[The SQL statement to be executed]
</ sql-statement >
響應(yīng)結(jié)果是這樣的:
<result-set>
</result-count>
[the number of rows in the result set]
</result-count>
<row>
<col name='name'>
[the value]
</col>
?
</row>
?
<result-set>

HTTPService是一個servlet,它響應(yīng)一個POST請求,恢復(fù)XML負(fù)載,并使用它來創(chuàng)建一個ServiceRequest例示。然后,根據(jù)請求的類型,將請求交給HttpServiceHandler 抽象類的一個特定子類。Handler類執(zhí)行請求,在ServiceResponse的一個例示中存儲結(jié)果,然后將這個結(jié)果發(fā)送回客戶端應(yīng)用程序。

通過按照慣例為服務(wù)處理器類命名,我們可以利用Java的映象功能來創(chuàng)建處理器類的一個例示,這個處理器類僅僅是建立在ServiceRequest 對象的服務(wù)類型屬性的基礎(chǔ)上。這就取消了HttpService和所有服務(wù)處理器類之間的依賴性,從而意味著當(dāng)我們增加一個新服務(wù)時,不再需要改變 HttpService類。

在我們這個例子中,服務(wù)的類型是DBService,因此我們將創(chuàng)建HttpServiceHandler的一個子類,叫做DBServiceHandler。在HttpService中,我們使用以下代碼:

String className = PACKAGE_NAME + "." + request.getRequestType() + "Handler";
HttpServiceHandler handler = Class.fromName(className).newInstance();
handler.handleRequest(request);

一個HttpServiceHandler子類需要執(zhí)行一個方法processRequest(),它需要取一個ServiceRequest對象,然后返回一個ServiceResponse對象。這個方法是在handleRequest 方法的過程中由子類調(diào)用的:

Public void handleRequest(ServiceRequest request)
{
Serviceresponse response = processRequest(request);
SendResponse(response);
}

這是使用Template(模板)方法模式的一個典型例子,在這個過程中,抽象超類調(diào)用在一個子類中執(zhí)行的方法。

ServiceRequest類將服務(wù)特定數(shù)據(jù)存儲為一個XML 文檔。這個類由訪問者來設(shè)置和獲取請求類型,它還有方法來處理請求的細(xì)節(jié)。getRequest()方法返回包含在請求標(biāo)記中的XML節(jié)點,setRequest()方法則用一個新生成的請求來覆蓋原來的請求。這個類還使用兩個factory方法,創(chuàng)建新的元素和文本節(jié)點,允許開發(fā)人員生成新的請求。ServiceResponse類以一種非常簡單的方法來處理請求的細(xì)節(jié)。

雖然這兩個類允許我們對所有類型的請求和響應(yīng)進行處理,但是開發(fā)人員也必須要了解每個請求的特殊語法。開發(fā)人員不能對請求的格式是否正確進行任何確認(rèn)。

為了簡化這個過程,我們將創(chuàng)建ServiceRequest和 ServiceResponse的子類,它們分別叫做DBServiceRequest和DBServiceResponse,它們都有處理服務(wù)特定細(xì)節(jié)的方法。例如,DBServiceRequest有設(shè)置和獲取SQL 語句的方法,同時 DBServiceResponse有設(shè)置和獲取結(jié)果記數(shù)值和結(jié)果設(shè)置矢量的方法。

服務(wù)是用HttpServiceClient類來訪問的。在客戶端應(yīng)用程序中,有以下的代碼:

HttpServiceClient client = new HttpServiceClient(serviceURL);
DBServiceRequest request = new DBServiceRequest();
request.setSqlStatement(statement);
DBServiceResponse response = new DBServiceResponse(client.executeRequest(request));

其中服務(wù)的URL是這樣的:

http://myHost/servlet/httpservice.HttpService.

細(xì)節(jié)
上面我們已經(jīng)看到了框架結(jié)構(gòu)中的所有元素,現(xiàn)在來看看那些有趣的細(xì)節(jié)。首先讓我們來注意一下協(xié)議層。我們應(yīng)該如何創(chuàng)建包裝XML負(fù)載的HTTP POST 請求?我們應(yīng)該如何處理HTTP響應(yīng)?

HTTP 請求是標(biāo)準(zhǔn)化的、基于ASCII的、與一個Web 服務(wù)器的socket通訊。這里有一個例子:

POST /servlet/
httpService.Httpservice HTTP/1.0
Host: localhostt:80
Content-Type: text/xml
Content-Length: 248
<?xml version='1.0' encoding='utf-8' ?>
<http-request>
<requestType>DBService</requestType>
<request>
<sql-statement>
SELECT * FROM MyTable
</sql-statement >
</request>
</http-request>

來自Web服務(wù)器的響應(yīng)如下所示:

HTTP/1.0 200 OK
Date: Fri, 24 Nov 2000 16:09:57 GMT
Status: 200
Servlet-Engine: Tomcat Web Server/3.1 (JSP 1.1;
Servlet 2.2; Java 1.3.0; Windows 2000 5.0 x86;
java.vendor=Sun Microsystems Inc.)
Content-Type: text/xml
Content-Length: 726
Content-Language: en
<?xml version='1.0' encoding='utf-8' ?>
<http-response>
<responseMessage>OK</responseCode>
<responseCode>200</responseCode>
<response>
<result-set>
</result-count>2 </result-count>
<row>
<col name='col1'>value11</col>
<col name='col2'>value12</col>
</row>
<row>
<col name='col1'>value21</col>
<col name='col2'>value22/</col>
</row>
<result-set>
</response>
</http-response>


下面的代碼顯示了HttpServiceClient類的執(zhí)行,它將處理HTTP 請求的所有細(xì)節(jié)。你能看到,一旦你了解了那些請求的準(zhǔn)確格式,這就是一個非常簡單的過程:

public class HttpServiceClient
{
private static final String HTTP_VERSION = "1.0";
private static final String HTTP_POST_REQUEST ="POST";
private static final String HEADER_HOST = "Host";
private static final String HEADER_CONTENT_TYPE = "Content-Type";
private static final String XML_MIME_TYPE = "text/xml";
private static final String
HEADER_CONTENT_LENGTH = "Content-Length";
private static final int DEFAULT_PORT = 80;

private String serviceUrl;
private int returnCode;
private String returnMessage;
private Reader responsePayload;

public HttpServiceClient(String serviceUrl)
{
this.serviceUrl = serviceUrl;
}

public ServiceResponse executeRequest(ServiceRequest request)
throws HttpServiceException
{

try
{
String data = request.serializeRequestToString("utf-8");
postRequest(data);

//check for failures
if(returnCode != 200)
throw new HttpServiceException(returnMessage);

InputSource source =
new InputSource(responsePayload);
DOMParser parser = new DOMParser();
parser.parse(source);
ServiceResponse serviceResponse =
new ServiceResponse(parser.getDocument());

String theResponse =
serviceResponse.serializeResponseToString("utf-8");
System.err.println(theResponse);

return serviceResponse;

}
catch(Exception ex)
{
ex.printStackTrace(System.err);
throw new HttpServiceException(ex.getMessage());
}
}


private void postRequest(String payload)
throws HttpServiceException
{
PrintWriter out = null;
BufferedReader in = null;


URL url = null;
try
{
url = new URL(serviceUrl);

// No port? use default port 80
int port = url.getPort () < 0 ? DEFAULT_PORT : url.getPort();

Socket soket = new Socket (url.getHost (), port);
out = new PrintWriter (soket.getOutputStream ());

in = new BufferedReader (new InputStreamReader(soket.getInputStream ()));
}
catch (Exception ex)
{
throw new HttpServiceException ("error opening socket: " + ex.getMessage ());
}

out.print (HTTP_POST_REQUEST + " " + url.getFile() + "HTTP/" + HTTP_VERSION + "\r\n");
out.print (HEADER_HOST + ": " + url.getHost () + ':' + url.getPort () + "\r\n");
out.print (HEADER_CONTENT_TYPE + ": " + XML_MIME_TYPE + "\r\n");
out.print (HEADER_CONTENT_LENGTH + ": " + payload.length () + "\r\n");
out.print ("\r\n");
out.print (payload);
out.print ("\r\n\r\n");
out.flush ();

try
{
String statusLine = in.readLine();
System.err.println(statusLine);
parseStatusLine(statusLine);
}
catch (Exception ex)
{
throw new HttpServiceException ("error parsing HTTP status line: " + ex.getMessage ());
}

// I will ignore all the headers and keep reading
// until I get an empty line
try
{
String headerLine = null;
while ((headerLine = in.readLine ()) != null)
{
if (headerLine.length () == 0)
break;
}
}
catch (Exception ex)
{
throw new HttpServiceException ("error reading HTTP headers: " + ex.getMessage ());
}

//what remains of the input Stream is my payload
responsePayload = in;

}

private void parseStatusLine(String statusLine)
throws Exception
{
StringTokenizer st =
new StringTokenizer (statusLine);

// this is the HTTP Version
st.nextToken ();

returnCode = Integer.parseInt (st.nextToken ());

StringBuffer retMessage = new StringBuffer ();

while (st.hasMoreTokens ())
{
retMessage.append (st.nextToken ());
if (st.hasMoreTokens ())
{
retMessage.append (" ");
}
}

returnMessage = retMessage.toString ();

}

}


Web服務(wù)器接受了HTTP請求后,它就創(chuàng)建一個HttpService servlet的新例示,接著調(diào)用doPost()方法,在HttpServletRequest和HttpServletResponse對象中傳遞。然后這個servlet 就恢復(fù)XML負(fù)載,并且創(chuàng)建ServiceRequest類的一個例示,最后將其轉(zhuǎn)交給正確的處理器:

Document dom;
DOMParser parser = new DOMParser();
InputSource input = new InputSource(request.getInputStream());
parser.parse(input);
dom = parser.getDocument();
ServiceRequest serviceRequest = new ServiceRequest(dom);
String className = PACKAGE_NAME + "." + request.getRequestType() + "Handler";
HttpServiceHandler handler = Class.fromName(className).newInstance();
handler.handleRequest(request);


下面的代碼顯示了DBServiceHandler類的執(zhí)行情況,這個類創(chuàng)建數(shù)據(jù)庫連接、執(zhí)行查詢并且生成DBServiceResponse對象。同樣,要注意這個過程非常簡單,因為許多復(fù)雜的問題都隱藏在ServiceResponse和ServiceRequest類及子類的后面了:

public class DBServiceHandler extends
HttpServiceHandler

{
public ServiceResponse processRequest(ServiceRequest req)
throws HttpServiceException
{
DBServiceRequest request = new DBServiceRequest(req);

String sql = request.getSqlStatement();
DBServiceResponse response = new DBServiceResponse();
Connection connection;

try
{
Class.forName("oracle.jdbc.driver.OracleDriver");
String connectionString = "jdbc:oracle:thin:@fender.openport.com:1521:iplp";
connection = DriverManager.getConnection(connectionString ,"op1","op1");

}
catch(ClassNotFoundException ex)
{
ex.printStackTrace(System.err);
response.setResponseCode(400);
response.setResponseMessage("Oracle driver not found");
return response;
}
catch(SQLException ex2)
{
ex2.printStackTrace(System.err);
response.setResponseCode(400);
response.setResponseMessage("Could Not Connect To Database!");
return response;
}

String theSql = sql.trim().toUpperCase();
ResultSet resultSet;

try
{
Statement statement =
connection.createStatement();

if(theSql.startsWith("SELECT"))
{
resultSet = statement.executeQuery(theSql);

Vector theResults = parseResultSet(resultSet);
response.setResultsCount(theResults.size() - 1);
response.setResultSet(theResults);
}
else
{
statement.executeUpdate(theSql);
response.setResultsCount(-1);
response.setResultSet(new Vector());
}

}catch(SQLException ex)
{
response.setResponseCode(400);
response.setResponseMessage(ex.getMessage());
return response;
}

response.setResponseCode(200);
response.setResponseMessage("OK");

String res = response.serializeResponseToString("utf-8");
System.out.println(res);

return response;
}
?
}


在下面的代碼中,你能看到ServiceRequest 和DBServiceRequest的執(zhí)行。請注意隨著DBService-Request還提供了一個額外的構(gòu)造器,它將ServiceRequest作為一個自變量。這就允許我們把原始請求的XML 文檔作為當(dāng)前文檔來使用,從而向現(xiàn)有的數(shù)據(jù)提供一個額外的應(yīng)用程序特有的界面:

public class ServiceRequest

{
public final static String REQUEST_TYPE_TAG_NAME = "requestType";
public final static String REQUEST_TAG_NAME = "request";
public final static String ROOT_TAG_NAME = "http-request";

protected Document dom;

public ServiceRequest(Document request)
{
dom = request;
}

public ServiceRequest()
{
dom = new DocumentImpl();
initializeRequest();
}

//initializes an empty request
private void initializeRequest()
{
Element root = dom.createElement(ROOT_TAG_NAME);
dom.appendChild(root);

Element eRequestType =
dom.createElement(REQUEST_TYPE_TAG_NAME);
eRequestType.appendChild(dom.createTextNode(""));

root.appendChild(eRequestType);

Element eRequest =
dom.createElement(REQUEST_TAG_NAME);
root.appendChild(eRequest);
}

public String getRequestType()
throws HttpServiceException
{
try
{
return getTextAttribute(REQUEST_TYPE_TAG_NAME);
}
catch(Exception ex)
{
ex.printStackTrace(System.err);
throw new HttpServiceException("Invalid Request Format.");
}

}

public void setRequestType(String requestType)
throws HttpServiceException
{
try
{
setTextAttribute(REQUEST_TYPE_TAG_NAME,requestType);
}
catch(Exception ex)
{
ex.printStackTrace(System.err);
throw new HttpServiceException("Invalid Request Format.");
}

}

public Node getRequest()
throws HttpServiceException
{
try
{
Node request =
((NodeList)dom.getElementsByTagName(REQUEST_TAG_NAME)).item(0);

return request.getFirstChild().cloneNode(true);

}
catch(Exception ex)
{
ex.printStackTrace(System.err);
throw new HttpServiceException("Invalid Request Format.");
}
}

public Element createElementNode(String elementName)
{
return dom.createElement(elementName);
}

public Text createTextNode(String value)
{
return dom.createTextNode(value);
}

public void setRequest(Node request)
throws HttpServiceException
{

try
{
Node requestElement =
((NodeList)dom.getElementsByTagName(REQUEST_TAG_NAME)).item(0);
Node oldRequest =
requestElement.getFirstChild();

if(oldRequest != null)
requestElement.removeChild(oldRequest);

requestElement.appendChild(request);
}
catch(Exception ex)
{
ex.printStackTrace(System.err);
throw new HttpServiceException("Invalid Request Format.");
}
}

public byte[] serializeRequestToByteArray(String encoding)
throws HttpServiceException
{
try
{
return serializeDOM(encoding).toByteArray();
}
catch(Exception ex)
{
ex.printStackTrace(System.err);
throw new HttpServiceException("Error during serialization");
}
}

public String serializeRequestToString(String encoding)
throws HttpServiceException
{
try
{
return serializeDOM(encoding).toString();
}
catch(Exception ex)
{
ex.printStackTrace(System.err);
throw new HttpServiceException(
"Error during serialization");
}
}

private ByteArrayOutputStream serializeDOM(String encoding)
throws HttpServiceException
{
try
{
ByteArrayOutputStream bytes =
new ByteArrayOutputStream (4096);
PrintWriter out = new PrintWriter (new OutputStreamWriter (bytes,encoding),true) ;
OutputFormat of =
new OutputFormat(dom,encoding,true);
XMLSerializer serializer = new XMLSerializer(out,of);
serializer.serialize(dom);
out.close();

return bytes;
}
catch(Exception ex)
{
ex.printStackTrace(System.err);
throw new HttpServiceException("Error during serialization");
}
}

protected String getTextAttribute(String name)
{
Node textAttributeNode = ((NodeList)dom.getElementsByTagName(name)).item(0);
Node textAttribute = textAttributeNode.getFirstChild();
if(textAttribute.getNodeType() == Node.TEXT_NODE)
return textAttribute.getNodeValue();
else
return null;
}

protected void setTextAttribute(String name, String value)
{
if (value == null)
value ="";
Node textAttributeNode =((NodeList)dom.getElementsByTagName(name)).item(0);
Node textAttribute =
textAttributeNode.getFirstChild();
textAttribute.setNodeValue(value);

}
}

public class DBServiceRequest extends ServiceRequest
{

public final static String SERVICE_NAME = "DBService";

public final static String SQL_STATEMENT_TAG_NAME = "sql-statement";

public DBServiceRequest()
{
super();
initializeParameters();
}

public DBServiceRequest(Document request)
{
super(request);
}

public DBServiceRequest(ServiceRequest request)
{
dom = request.dom;
}

public void setSqlStatement(String sql)
{
setTextAttribute(SQL_STATEMENT_TAG_NAME,sql);
}

public String getSqlStatement()
{
return getTextAttribute(SQL_STATEMENT_TAG_NAME);
}
private void initializeParameters()
{
Element eDBRequest = null;
//not very nice but this should never fail

try
{
setRequestType(SERVICE_NAME);
eDBRequest = createElementNode(SQL_STATEMENT_TAG_NAME);
}
catch(Exception ex)
{}

eDBRequest.appendChild(dom.createTextNode(""));

try
{
setRequest(eDBRequest);
}
catch(Exception ex)
{
}
}


擴展框架結(jié)構(gòu)
我們可以對這個框架進行擴展,從而處理任何類型的服務(wù)。要想創(chuàng)建一個新的服務(wù),首先必須要定義XML請求和響應(yīng)的語法。然后,創(chuàng)建ServiceRequest和ServiceResponse的一個子類,它們將幫助處理服務(wù)特有的數(shù)據(jù)。最后,創(chuàng)建Http-ServiceHandler的一個新子類,它將處理請求并生成適當(dāng)?shù)捻憫?yīng)。整個過程就是這樣。

雖然這個框架包含了一些功能,但是如果不增加一些更多功能的話,它在實際應(yīng)用情況下還不是很實用。我有意識地省略了這些功能,以使框架結(jié)構(gòu)保持簡單,并將注意力集中到了最重要的細(xì)節(jié)上。為了完整起見,現(xiàn)在我們來簡要分析這個框架結(jié)構(gòu)的一些局限性以及應(yīng)該如何去克服這些局限性。

首先,這個框架結(jié)構(gòu)不能限制對服務(wù)的訪問。這就意味著知道如何訪問這個服務(wù)的每個人都能夠進行訪問。在你允許對服務(wù)的訪問之前,請求某種證明可以解決這個問題。你可以用這同一個框架來創(chuàng)建一個證明服務(wù),這樣就能確認(rèn)用戶并生成一個唯一的ID,當(dāng)用戶訪問任何其它服務(wù)時,都會被要求這個ID。系統(tǒng)會將這個ID存儲在某些類型的訪問列表中,并且要求在一個有限時間內(nèi),每個請求都必須通過一個有效ID才能訪問服務(wù)。

另外,每次用戶訪問服務(wù)時,服務(wù)都要創(chuàng)建一個與數(shù)據(jù)庫的連接。將服務(wù)與一個連接pooling框架組合起來可以解決這個問題,這樣就將給請求分配一個現(xiàn)有連接,而不是每次都創(chuàng)建新連接。

最后一個局限是缺乏session的管理。由于我們是通過一個socket 直接訪問servlet,因此不能使用特別有用的HttpSession對象。由于在Web 服務(wù)器與瀏覽器相互作用的過程中,在客戶計算機上沒有生成session cookie ,因此不能管理自動session。為了克服這個局限,我們可以執(zhí)行我們自己的session管理。一個session可以是一個與唯一ID相關(guān)聯(lián)的對象,它可以存儲其它對象。例如,你可以有一個上下文hash信號表格,在其中存儲session對象,使用唯一的ID 作為關(guān)鍵字。這個session對象還可以包含一個hash信號表格,它存儲了你想在session中堅持使用的每個對象。

這個框架利用HTTP隧道技術(shù),允許一個桌面應(yīng)用程序訪問防火墻后面的服務(wù),對其進行擴展后還提供對其它類型服務(wù)的簡易訪問。

溫馨提示:喜歡本站的話,請收藏一下本站!

本類教程下載

系統(tǒng)下載排行

網(wǎng)站地圖xml | 網(wǎng)站地圖html
主站蜘蛛池模板: 宁强县| 凉山| 玉溪市| 澄城县| 开原市| 武夷山市| 长汀县| 平乡县| 高平市| 庐江县| 灵台县| 武宣县| 青州市| 龙江县| 济阳县| 斗六市| 浙江省| 开远市| 宁波市| 布尔津县| 广宁县| 武川县| 山东省| 平度市| 林甸县| 东阿县| 沂源县| 温州市| 清河县| 商水县| 绥宁县| 玉溪市| 武冈市| 恩施市| 定远县| 醴陵市| 广饶县| 历史| 凤山县| 克山县| 安乡县|