(轉)HTTP詳解(2)-請求、響應、緩存

1. HTTP請求格式      

       作過Socket編程的人都知道,當咱們設計一個通訊協議時,「消息頭/消息體」的分割方式是很經常使用的,消息頭告訴對方這個消息是幹什麼的,消息體告訴對方怎麼幹。HTTP協議傳輸的消息也是這樣規定的,每個HTTP包都分爲HTTP頭和HTTP體兩部分,消息體是可選的,而消息頭是必須的。每當咱們打開一個網頁,在上面點擊右鍵,選擇「查看源文件」,這時看到的HTML代碼就是HTTP的消息體,那麼消息頭能夠經過瀏覽器的開發工具或者插件能夠看到,若是火狐的Firebug,IE的Httpwatch。javascript

      客戶端經過發送 HTTP 請求向服務器請求對資源的訪問。 它向服務器傳遞了一個數據塊,也就是請求信息,HTTP 請求由三部分組成:請求行、  請求頭和請求正文。php

 請求行:請求方法 URI 協議/版本css

  請求頭(Request Header)html

 

 請求正文java

下面是一個HTTP請求的數據:apache

POST /index.php HTTP/1.1
Host: localhost
User-Agent: Mozilla/5.0 (Windows NT 5.1; rv:10.0.2) Gecko/20100101 Firefox/10.0.2
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-cn,zh;q=0.5
Accept-Encoding: gzip, deflate
Connection: keep-alive
Referer: http://localhost/
Content-Length:25
Content-Type:application/x-www-form-urlencoded
 
username=aa&password=1234
 

一、請求行:請求方法URI協議/版本

 請求的第一行是「方法 URL  協議/版本」,並以 回車換行做爲結尾。請求行以空格分隔。格式以下:
POST /index.php HTTP/1.1
以上代碼中「GET」表明請求方法,「//ndex.php」表示URI,「HTTP/1.1表明協議和協議的版本。
        根據HTTP標準,HTTP請求可使用多種請求方法。例如:HTTP1.1支持7種請求方法:GET、POST、HEAD、OPTIONS、PUT、DELETE和TARCE。在Internet應用中,最經常使用的方法是GET和POST。
         URL完整地指定了要訪問的網絡資源,一般只要給出相對於服務器的根目錄的相對目錄便可,所以老是以「/」開頭,最後,協議版本聲明瞭通訊過程當中使用HTTP的版本。   

請求方法

在 HTTP 協議中,HTTP 請求可使用多種請求方法,這些方法指明瞭要以何種方式來訪問 Request-URI 所標識的資源。HTTP1.1 支持的請求方法以下表所示:編程

HTTP1.1 中的請求方式:
方法 做用
GET 請求獲取由 Request-URI 所標識的資源
POST 請求服務器接收在請求中封裝的實體,並將其做爲由 Request-Line 中的 Request-URI 所標識的資源的一部分
HEAD 請求獲取由 Request-URI 所標識的資源的響應消息報頭
PUT

請求服務器存儲一個資源,並用 Request-URI 做爲其標識符跨域

DELETE 請求服務器刪除由 Request-URI 所標識的資源
TRACE 請求服務器回送到的請求信息,主要用於測試或診斷
CONNECT 保留未來使用
OPTIONS 請求查詢服務器的性能,或者查詢與資源相關的選項和需求
 

重點介紹 GET、POST 和 HEAD 三個方法:瀏覽器

(1)GET緩存

        GET 方法用於獲取由 Request-URI 所標識的資源的信息,常見的形式是:

        GET Request-URI HTTP/1.1
        GET方法是默認的HTTP請求方法,例如當咱們經過在瀏覽器的地址欄中直接輸入網址的方式去訪問網頁的時候,瀏覽器採用的就是 GET 方法向服務器獲取資源。

        咱們可使用GET方法來提交表單數據,用GET方法提交的表單數據只通過了簡單的編碼,同時它將做爲URL的一部分向服務器發送,所以,若是使用GET方法來提交表單數據就存在着安全隱患上。例如:
         Http://localhost/login.php?username=aa&password=1234

        從上面的URL請求中,很容易就能夠辯認出表單提交的內容。(?以後的內容)另外因爲GET方法提交的數據是做爲URL請求的一部分因此提交的數據量不能太大。這是由於瀏覽器對url的長度有限制

       各類瀏覽器也會對url的長度有所限制,下面是幾種常見瀏覽器的url長度限制:(單位:字符)

IE : 2803
Firefox:65536
Chrome:8182
Safari:80000
Opera:190000 

(2)POST

          POST方法是GET方法的一個替代方法,它主要是向Web服務器提交表單數據,尤爲是大批量的數據。 在請求頭信息結束以後的兩個回車換行以後(實際是空一行),就是表單提交的數據。如上面提到的post表單數據:

        username=aa&password=1234

        POST方法克服了GET方法的一些缺點。經過POST方法提交表單數據時,數據不是做爲URL請求的一部分而是做爲標準數據傳送給Web服務器,這就克服了GET方法中的信息沒法保密和數據量過小的缺點。所以,出於安全的考慮以及對用戶隱私的尊重,一般表單提交時採用POST方法。

  從編程的角度來說,若是用戶經過GET方法提交數據,則數據存放在QUERY_STRING環境變量中,而POST方法提交的數據則能夠從標準輸入流中獲取。

 

 GET與POST方法有如下區別:

      一、  在客戶端,Get方式在經過URL提交數據,數據在URL中能夠看到;POST方式,數據放在HTTP包的body中。

      二、 GET方式提交的數據大小有限制(由於瀏覽器對URL的長度有限制),而POST則沒有此限制。

      三、安全性問題。正如在(1)中提到,使用 Get 的時候,參數會顯示在地址欄上,而 Post 不會。因此,若是這些數據是中文數據並且是非敏感數據,那麼使用 get;若是用戶輸入的數據不是中文字符並且包含敏感數據,那麼仍是使用 post爲好。

      4.、服務器取值方式不同。GET方式取值,如php可使用$_GET來取得變量的值,而POST方式經過$_POST來獲取變量的值。

 

(3)HEAD

     HEAD 方法與 GET 方法幾乎是相同的,它們的區別在於 HEAD 方法只是請求消息報頭,而不是完整的內容。對於 HEAD 請求的迴應部分來講,它的 HTTP 頭部中包含的信息與經過 GET 請求所獲得的信息是相同的。利用這個方法,沒必要傳輸整個資源內容,就能夠獲得 Request-URI 所標識的資源的信息。這個方法一般被用於測試超連接的有效性,是否能夠訪問,以及最近是否更新。

    要注意的是,在 HTML 文檔中,書寫 get 和 post,大小寫均可以,但在 HTTP 協議中的 GET 和 POST 只能是大寫形式。

2. 請求頭

每一個頭域由一個域名,冒號(:)和域值三部分組成。域名是大小寫無關的,域值前能夠添加任何數量的空格符,頭域能夠被擴展爲多行,在每行開始處,使用至少一個空格或製表符。

HTTP最多見的請求頭以下:

Transport 頭域

Connection:

做用:表示是否須要持久鏈接。

若是服務器看到這裏的值爲「Keep-Alive」,或者看到請求使用的是HTTP 1.1(HTTP 1.1默認進行持久鏈接),它就能夠利用持久鏈接的優勢,當頁面包含多個元素時(例如Applet,圖片),顯著地減小下載所須要的時間。要實現這一點,服務器須要在應答中發送一個Content-Length頭,最簡單的實現方法是:先把內容寫入 ByteArrayOutputStream,而後在正式寫出內容以前計算它的大小;

例如: Connection: keep-alive   當一個網頁打開完成後,客戶端和服務器之間用於傳輸HTTP數據的TCP鏈接不會關閉,若是客戶端再次訪問這個服務器上的  網頁,會繼續使用這一條已經創建的鏈接

例如:  Connection: close  表明一個Request完成後,客戶端和服務器之間用於傳輸HTTP數據的TCP鏈接會關閉,  當客戶端再次發送Request,須要從新創建TCP鏈接。

Host(發送請求時,該報頭域是必需的)

Host請求報頭域主要用於指定被請求資源的Internet主機和端口號,它一般從HTTP URL中提取出來的。

eg:http://;localhost/index.html
瀏覽器發送的請求消息中,就會包含Host請求報頭域,以下:
Host:localhost

此處使用缺省端口號80,若指定了端口號8080,則變成:Host:localhost:8080

Client 頭域

Accept:

做用:瀏覽器能夠接受的媒體類型(MIME類型),

例如:  Accept: text/html  表明瀏覽器能夠接受服務器回發的類型爲 text/html  也就是咱們常說的html文檔, 若是服務器沒法返回text/html類型的數據,服務器應該返回一個406錯誤(non acceptable)。

通配符 * 表明任意類型。例如  Accept: */*  表明瀏覽器能夠處理全部類型,(通常瀏覽器發給服務器都是發這個)

Accept-Encoding:

做用: 瀏覽器申明本身接收的編碼方法,一般指定壓縮方法,是否支持壓縮,支持什麼壓縮方法(gzip,deflate),(注意:這不是隻字符編碼);

例如: Accept-Encoding: gzip, deflate。Server可以向支持gzip/deflate的瀏覽器返回經gzip或者deflate編碼的HTML頁面。 許多情形下這能夠減小5到10倍的下載時間,也節省帶寬。

Accept-Language:

做用: 瀏覽器申明本身接收的語言。 

語言跟字符集的區別:中文是語言,中文有多種字符集,好比big5,gb2312,gbk等等;

例如: Accept-Language:zh-cn 。若是請求消息中沒有設置這個報頭域,服務器假定客戶端對各類語言均可以接受。

User-Agent:

做用:告訴HTTP服務器, 客戶端使用的操做系統和瀏覽器的名稱和版本.

咱們上網登錄論壇的時候,每每會看到一些歡迎信息,其中列出了你的操做系統的名稱和版本,你所使用的瀏覽器的名稱和版本,這每每讓不少人感到很神奇,實際上, 服務器應用程序就是從User-Agent這個請求報頭域中獲取到這些信息User-Agent請求報頭域容許客戶端將它的操做系統、瀏覽器和其它屬性告訴服務器。

例如: User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; CIBA; .NET CLR 2.0.50727; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729; .NET4.0C; InfoPath.2; .NET4.0E)

Accept-Charset:

做用:瀏覽器申明本身接收的字符集,這就是本文前面介紹的各類字符集和字符編碼,如gb2312,utf-8(一般咱們說Charset包括了相應的字符編碼方案);

例如:Accept-Charset:iso-8859-1,gb2312.若是在請求消息中沒有設置這個域,缺省是任何字符集均可以接受。

Authorization:受權信息,一般出如今對服務器發送的WWW-Authenticate頭的應答中;

Authorization請求報頭域主要用於證實客戶端有權查看某個資源。當瀏覽器訪問一個頁面時,若是收到服務器的響應代碼爲401(未受權),能夠發送一個包含Authorization請求報頭域的請求,要求服務器對其進行驗證。

Cookie/Login 頭域

Cookie:

做用: 最重要的header, 將cookie的值發送給HTTP 服務器

Entity頭域

Content-Length

做用:發送給HTTP服務器數據的長度。即請求消息正文的長度;

例如: Content-Length: 38

Content-Type:

做用:

例如:Content-Type: application/x-www-form-urlencoded

Miscellaneous 頭域

Referer:

做用: 提供了Request的上下文信息的服務器,告訴服務器我是從哪一個連接過來的,好比從我主頁上連接到一個朋友那裏, 他的服務器就可以從HTTP Referer中統計出天天有多少用戶點擊我主頁上的連接訪問    他的網站。

例如: Referer:http://translate.google.cn/?hl=zh-cn&tab=wT

Cache 頭域

If-Modified-Since:

做用: 把瀏覽器端緩存頁面的最後修改時間發送到服務器去,服務器會把這個時間與服務器上實際文件的最後修改時間進行對比。若是時間一致,那麼返回304,客戶端就直接使用本地緩存文件。若是時間不一致,就會返回200和新的文件內容。客戶端接到以後,會丟棄舊文件,把新文件緩存起來,並顯示在瀏覽器中。

例如:If-Modified-Since: Thu, 09 Feb 2012 09:07:57 GMT。

If-None-Match:

做用: If-None-Match和ETag一塊兒工做,工做原理是在HTTP Response中添加ETag信息。 當用戶再次請求該資源時,將在HTTP Request 中加入If-None-Match信息(ETag的值)。若是服務器驗證資源的ETag沒有改變(該資源沒有更新),將返回一個304狀態告訴客戶端使用本地緩存文件。不然將返回200狀態和新的資源和Etag.  使用這樣的機制將提升網站的性能

例如: If-None-Match: "03f2b33c0bfcc1:0"

Pragma:

做用: 防止頁面被緩存, 在HTTP/1.1版本中,它和Cache-Control:no-cache做用如出一轍

Pargma只有一個用法, 例如: Pragma: no-cache

注意: 在HTTP/1.0版本中,只實現了Pragema:no-cache, 沒有實現Cache-Control

Cache-Control:

做用: 這個是很是重要的規則。 這個用來指定Response-Request遵循的緩存機制。各個指令含義以下

Cache-Control:Public   能夠被任何緩存所緩存()

Cache-Control:Private     內容只緩存到私有緩存中

Cache-Control:no-cache  全部內容都不會被緩存

2. HTTP響應格式      

      在接收和解釋請求消息後,服務器會返回一個 HTTP 響應消息。與 HTTP 請求相似,HTTP 響應也是由三個部分組成,分別是:狀態行、消息報頭和響應正文。如:

HTTP/1.1 200 OK Date: Sun, 17 Mar 2013 08:12:54 GMT Server: Apache/2.2.8 (Win32) PHP/5.2.5 X-Powered-By: PHP/5.2.5 Set-Cookie: PHPSESSID=c0huq7pdkmm5gg6osoe3mgjmm3; path=/ Expires: Thu, 19 Nov 1981 08:52:00 GMT Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0 Pragma: no-cache Content-Length: 4393 Keep-Alive: timeout=5, max=100 Connection: Keep-Alive Content-Type: text/html; charset=utf-8

<html> <head> <title>HTTP響應示例<title> </head> <body> Hello HTTP! </body> </html>

 

一、狀態行

       狀態行由協議版本、數字形式的狀態代碼,及相應的狀態描述組成,各元素之間以空格分隔,結尾時回車換行符,格式以下:

HTTP-Version Status-Code Reason-Phrase CRLF

HTTP-Version 表示服務器 HTTP 協議的版本,Status-Code 表示服務器發回的響應代碼,Reason-Phrase 表示狀態代碼的文本描述,CRLF 表示回車換行。例如:

HTTP/1.1 200 OK (CRLF)

      狀態代碼與狀態描述

      狀態代碼由 3 位數字組成, 表示請求是否被理解或被知足,狀態描述給出了關於狀態碼的簡短的文字描述。狀態碼的第一個數字定義了響應類別,後面兩位數字沒有具體分類。第一個數字有 5 種取值,以下所示。

  • 1xx:指示信息——表示請求已經接受,繼續處理
  • 2xx:成功——表示請求已經被成功接收、理解、接受。
  • 3xx:重定向——要完成請求必須進行更進一步的操做
  • 4xx:客戶端錯誤——請求有語法錯誤或請求沒法實現
  • 5xx:服務器端錯誤——服務器未能實現合法的請求。

常見狀態代碼、狀態描述、說明:
200 OK      //客戶端請求成功
400 Bad Request  //客戶端請求有語法錯誤,不能被服務器所理解
401 Unauthorized //請求未經受權,這個狀態代碼必須和WWW-Authenticate報頭域一塊兒使用 
403 Forbidden  //服務器收到請求,可是拒絕提供服務
404 Not Found  //請求資源不存在,eg:輸入了錯誤的URL
500 Internal Server Error //服務器發生不可預期的錯誤
503 Server Unavailable  //服務器當前不能處理客戶端的請求,一段時間後可能恢復正常

二、響應正文

響應正文就是服務器返回的資源的內容,響應頭和正文之間也必須用空行分隔。如:

 

  1. <html>  
  2. <head>  
  3. <title>HTTP響應示例<title>  
  4. </head>  
  5. <body>  
  6. Hello HTTP!  
  7. </body>  
  8. </html>  

3 、響應頭信息

HTTP最多見的響應頭以下所示:

Cache頭域

Date:

做用:生成消息的具體時間和日期,即當前的GMT時間。

例如: Date: Sun, 17 Mar 2013 08:12:54 GMT

Expires:

做用: 瀏覽器會在指定過時時間內使用本地緩存,指明應該在何時認爲文檔已通過期,從而再也不緩存它。

例如: Expires: Thu, 19 Nov 1981 08:52:00 GMT  

Vary

做用:

例如: Vary: Accept-Encoding

Cookie/Login 頭域

P3P

做用: 用於跨域設置Cookie, 這樣能夠解決iframe跨域訪問cookie的問題

例如: P3P: CP=CURa ADMa DEVa PSAo PSDo OUR BUS UNI PUR INT DEM STA PRE COM NAV OTC NOI DSP COR

Set-Cookie

做用: 很是重要的header, 用於把cookie 發送到客戶端瀏覽器, 每個寫入cookie都會生成一個Set-Cookie.

例如: Set-Cookie: PHPSESSID=c0huq7pdkmm5gg6osoe3mgjmm3; path=/

Entity實體頭域:

            實體內容的屬性,包括實體信息類型,長度,壓縮方法,最後一次修改時間,數據有效性等。

ETag:

做用:  和If-None-Match 配合使用。 (實例請看上節中If-None-Match的實例)

例如: ETag: "03f2b33c0bfcc1:0"

Last-Modified:

做用: 用於指示資源的最後修改日期和時間。(實例請看上節的If-Modified-Since的實例)

例如: Last-Modified: Wed, 21 Dec 2011 09:09:10 GMT

Content-Type:

做用:WEB服務器告訴瀏覽器本身響應的對象的類型和字符集,

例如:

        Content-Type: text/html; charset=utf-8

  Content-Type:text/html;charset=GB2312

  Content-Type: image/jpeg

Content-Length:

指明實體正文的長度,以字節方式存儲的十進制數字來表示。在數據下行的過程當中,Content-Length的方式要預先在服務器中緩存全部數據,而後全部數據再一古腦兒地發給客戶端。

  例如: Content-Length: 19847

Content-Encoding:

做用:文檔的編碼(Encode)方法。通常是壓縮方式。

WEB服務器代表本身使用了什麼壓縮方法(gzip,deflate)壓縮響應中的對象。利用gzip壓縮文檔可以顯著地減小HTML文檔的下載時間。

例如:Content-Encoding:gzip

Content-Language:

做用: WEB服務器告訴瀏覽器本身響應的對象的語言者

例如: Content-Language:da

Miscellaneous 頭域

Server:

做用:指明HTTP服務器的軟件信息

例如:Apache/2.2.8 (Win32) PHP/5.2.5

X-Powered-By:

做用:表示網站是用什麼技術開發的

例如: X-Powered-By: PHP/5.2.5

Transport頭域

Connection:

例如: Connection: keep-alive   當一個網頁打開完成後,客戶端和服務器之間用於傳輸HTTP數據的TCP鏈接不會關閉,若是客戶端再次訪問這個服務器上的網頁,會繼續使用這一條已經創建的鏈接

例如:  Connection: close  表明一個Request完成後,客戶端和服務器之間用於傳輸HTTP數據的TCP鏈接會關閉, 當客戶端再次發送Request,須要從新創建TCP鏈接。

Location頭域

Location:

做用: 用於重定向一個新的位置, 包含新的URL地址

實例請看304狀態實例

HTTP協議是無狀態的和Connection: keep-alive的區別

  無狀態是指協議對於事務處理沒有記憶能力,服務器不知道客戶端是什麼狀態。從另外一方面講,打開一個服務器上的網頁和你以前打開這個服務器上的網頁之間沒有任何聯繫。

  HTTP是一個無狀態的面向鏈接的協議,無狀態不表明HTTP不能保持TCP鏈接,更不能表明HTTP使用的是UDP協議(無鏈接)。

  從HTTP/1.1起,默認都開啓了Keep-Alive,保持鏈接特性,簡單地說,當一個網頁打開完成後,客戶端和服務器之間用於傳輸HTTP數據的TCP鏈接不會關閉,若是客戶端再次訪問這個服務器上的網頁,會繼續使用這一條已經創建的鏈接。

  Keep-Alive不會永久保持鏈接,它有一個保持時間,能夠在不一樣的服務器軟件(如Apache)中設定這個時間。

 

3. 瀏覽器緩存      

          

       瀏覽器緩存:包括頁面html緩存和圖片js,css等資源的緩存。以下圖,瀏覽器緩存是基於把頁面信息保存到用戶本地電腦硬盤裏。

        

 

 

一、緩存的優勢:

 

     1)服務器響應更快:由於請求從緩存服務器(離客戶端更近)而不是源服務器被相應,這個過程耗時更少,讓服務器看上去響應更快。

     2)減小網絡帶寬消耗:當副本被重用時會減低客戶端的帶寬消耗;客戶能夠節省帶寬費用,控制帶寬的需求的增加並更易於管理。

一、緩存工做原理

       頁面緩存狀態是由http header決定的,一個瀏覽器請求信息,一個是服務器響應信息。主要包括Pragma: no-cache、Cache-Control、 Expires、 Last-Modified、If-Modified-Since。其中Pragma: no-cache由HTTP/1.0規定,Cache-Control由HTTP/1.1規定。

       工做原理圖:

 

從圖中咱們能夠看到原理主要分三步:

  1. 第一次請求:瀏覽器經過http的header報頭,附帶Expires,Cache-Control,Last-Modified/Etag向服務器請求,此時服務器記錄第一次請求的Last-Modified/Etag                  
  2. 再次請求:當瀏覽器再次請求的時候,請求頭附帶Expires,Cache-Control,If-Modified-Since/Etag向服務器請求
  3. 服務器根據第一次記錄的Last-Modified/Etag和再次請求的If-Modified-Since/Etag作對比,判斷是否須要更新,服務器經過這兩個頭判斷本地資源未發生變化,客 戶端不須要從新下載,返回304響應。常見流程以下圖所示:
 

 

與緩存相關的HTTP擴展消息頭

 

    Expires:設置頁面過時時間,格林威治時間GMT

    Cache-Control:更細緻的控制緩存的內容

    Last-Modified:請求對象最後一次的修改時間 用來判斷緩存是否過時 一般由文件的時間信息產生 

    ETag:響應中資源的校驗值,在服務器上某個時段是惟一標識的。ETag是一個能夠 與Web資源關聯的記號(token),和Last-Modified功能才很少,也是一個標識符,通常和Last-Modified一塊兒使用,增強服務器判斷的準確度。

    Date:服務器的時間

    If-Modified-Since:客戶端存取的該資源最後一次修改的時間,用來和服務器端的Last-Modified作比較

    If-None-Match:客戶端存取的該資源的檢驗值,同ETag。

Cache-Control的主要參數 
      Cache-Control: private/public Public 響應會被緩存,而且在多用戶間共享。 Private 響應只可以做爲私有的緩存,不能再用戶間共享。
      Cache-Control: no-cache:不進行緩存 
      Cache-Control: max-age=x:緩存時間 以秒爲單位 
      Cache-Control: must-revalidate:若是頁面是過時的 則去服務器進行獲取。

 

二、關於圖片,css,js,flash的緩存

這個主要經過服務器的配置來實現這個技術,若是使用apache服務器的話,可使用mod_expires模塊來實現:

編譯mod_expires模塊:

Cd  /root/httpd-2.2.3/modules/metadata

/usr/local/apache/bin/apxs -i -a -c mod_expires.c //編譯

編輯httpd.conf配置:添加下面內容

<IfModule mod_expires.c>

ExpiresActive on

ExpiresDefault "access plus 1 month"

ExpiresByType text/html "access plus 1 months"

ExpiresByType text/css "access plus 1 months"

ExpiresByType image/gif "access plus 1 months"

ExpiresByType image/jpeg "access plus 1 months"

ExpiresByType image/jpg "access plus 1 months"

ExpiresByType image/png "access plus 1 months"

EXpiresByType application/x-shockwave-flash "access plus 1 months"

EXpiresByType application/x-javascript      "access plus 1 months"

#ExpiresByType video/x-flv "access plus 1 months"

</IfModule>

解釋:第一句--開啓服務

第二句--默認時間是一個月

在下面是關於各類類型的資源的緩存時間設置

 

 

(緩存的部分修改自http://www.cnblogs.com/phphuaibei/archive/2011/09/27/2192817.html

相關文章
相關標籤/搜索