<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                # Spring MVC 3.1新特性 生產者、消費者請求限定 —— 第六章 注解式控制器詳解——跟著開濤學SpringMVC **6.6.5、生產者、消費者限定** #### 6.6.5.1、基本概念 首先讓我們看一下通過HTTP協議傳輸的媒體類型及如何表示媒體類型: 一、Media Type: 互聯網媒體類型,一般就是我們所說的MIME類型,用來確定請求的內容類型或響應的內容類型。 寫道媒體類型格式:type/subtype(;parameter)? type主類型,任意的字符串,如text,如果是*號代表所有; subtype 子類型,任意的字符串,如html,如果是*號代表所有; parameter 可選,一些參數,如Accept請求頭的q參數, Content-Type的 charset參數。 詳見http://tools.ietf.org/html/rfc2616#section-3.7 常見媒體類型: text/html : HTML格式????????? text/plain :純文本格式 ? ? ? ? ? ? text/xml :XML格式 image/gif :gif圖片格式 ? ? ? ? ?image/jpeg :jpg圖片格式????????? image/png:png圖片格式 application/x-www-form-urlencoded : &lt;form encType=””&gt;中默認的encType,form表單數據被編碼為key/value格式發送到服務器(表單默認的提交數據的格式)。 multipart/form-data : 當你需要在表單中進行文件上傳時,就需要使用該格式; application/xhtml+xml :XHTML格式 ? ? ? ? ? ? ? application/xml ????: XML數據格式? application/atom+xml ?:Atom XML聚合格式? ??application/json ???: JSON數據格式 application/pdf????? ?:pdf格式 ? ? ? ? ? ? ? ? ? ? ? ?application/msword? : Word文檔格式 application/octet-stream : 二進制流數據(如常見的文件下載)。 在如tomcat服務器的 “conf/web.xml”中指定了擴展名到媒體類型的映射,在此我們可以看到服務器支持的媒體類型。 二、Content-Type:內容類型,即請求/響應的內容區數據的媒體類型; **2.1、請求頭的內容類型,表示發送到服務器的內容數據的媒體類型;** request中設置請求頭“Content-Type: application/x-www-form-urlencoded”表示請求的數據為key/value數據; **(1、控制器cn.javass.chapter6.web.controller.consumesproduces.contenttype.RequestContentTypeController** ``` @RequestMapping(value = "/ContentType", method = RequestMethod.GET) public String showForm() throws IOException { //form表單,使用application/x-www-form-urlencoded編碼方式提交表單 return "consumesproduces/Content-Type"; } @RequestMapping(value = "/ContentType", method = RequestMethod.POST, headers = "Content-Type=application/x-www-form-urlencoded") public String request1(HttpServletRequest request) throws IOException { //①得到請求的內容區數據的類型 String contentType = request.getContentType(); System.out.println("========ContentType:" + contentType); //②得到請求的內容區數據的編碼方式,如果請求中沒有指定則為null //注意,我們的CharacterEncodingFilter這個過濾器設置了編碼(UTF-8) //編碼只能被指定一次,即如果客戶端設置了編碼,則過濾器不會再設置 String characterEncoding = request.getCharacterEncoding(); System.out.println("========CharacterEncoding:" + characterEncoding); //③表示請求的內容區數據為form表單提交的參數,此時我們可以通過request.getParameter得到數據(key=value) System.out.println(request.getParameter("realname")); System.out.println(request.getParameter("username")); return "success"; } ``` showForm功能處理方式:展示表單,且form的enctype="application/x-www-form-urlencoded",在提交時請求的內容類型頭為“Content-Type:application/x-www-form-urlencoded”; request1功能處理方法:只對請求頭為“Content-Type:application/x-www-form-urlencoded”的請求進行處理(即消費請求內容區數據); request.getContentType():可以得到請求頭的內容區數據類型(即Content-Type頭的值) request.getCharacterEncoding():如“Content-Type:application/json;charset=GBK”,則得到的編碼為“GBK”,否則如果你設置過濾器(CharacterEncodingFilter)則得到它設置的編碼,否則返回null。 request.getParameter():因為請求的內容區數據為application/x-www-form-urlencoded格式的數據,因此我們可以通過request.getParameter()得到相應參數數據。 request中設置請求頭“Content-Type:application/json;charset=GBK”表示請求的內容區數據為json類型數據,且內容區的數據以GBK進行編碼; **(1、控制器cn.javass.chapter6.web.controller.consumesproduces.contenttype.RequestContentTypeController** ``` @RequestMapping(value = "/request/ContentType", method = RequestMethod.POST, headers = "Content-Type=application/json") public String request2(HttpServletRequest request) throws IOException { //①表示請求的內容區數據為json數據 InputStream is = request.getInputStream(); byte bytes[] = new byte[request.getContentLength()]; is.read(bytes); //②得到請求中的內容區數據(以CharacterEncoding解碼) //此處得到數據后你可以通過如json-lib轉換為其他對象 String jsonStr = new String(bytes, request.getCharacterEncoding()); System.out.println("json data:" + jsonStr); return "success"; }? ``` request2功能處理方法:只對請求頭為“Content-Type:application/json”的進行請求處理(即消費請求內容區數據); request.getContentLength():可以得到請求頭的內容區數據的長度; request.getCharacterEncoding():如“Content-Type:application/json;charset=GBK”,則得到的編碼為“GBK”,否則如果你設置過濾器(CharacterEncodingFilter)則得到它設置的編碼,否則返回null。 我們得到json的字符串形式后就能很簡單的轉換為JSON相關的對象。 **(2、客戶端發送json數據請求** ``` //請求的地址 String url = "http://localhost:9080/springmvc-chapter6/request/ContentType"; //①創建Http Request(內部使用HttpURLConnection) ClientHttpRequest request = new SimpleClientHttpRequestFactory(). createRequest(new URI(url), HttpMethod.POST); //②設置請求頭的內容類型頭和內容編碼(GBK) request.getHeaders().set("Content-Type", "application/json;charset=gbk"); //③以GBK編碼寫出請求內容體 String jsonData = "{\"username\":\"zhang\", \"password\":\"123\"}"; request.getBody().write(jsonData.getBytes("gbk")); //④發送請求并得到響應 ClientHttpResponse response = request.execute(); System.out.println(response.getStatusCode()); ``` 此處我們使用Spring提供的Http客戶端API SimpleClientHttpRequestFactory創建了請求并設置了請求的Content-Type和編碼并在響應體中寫回了json數據(即生產json類型的數據),此處是硬編碼,實際工作可以使用json-lib等工具進行轉換。 具體代碼在cn.javass.chapter6.web.controller.consumesproduces.contenttype.RequestContentTypeClient。 **2.2、響應頭的內容類型,表示發送到客戶端的內容數據類型,和請求頭的內容類型類似,只是方向相反。** ``` @RequestMapping("/response/ContentType") public void response1(HttpServletResponse response) throws IOException { //①表示響應的內容區數據的媒體類型為html格式,且編碼為utf-8(客戶端應該以utf-8解碼) response.setContentType("text/html;charset=utf-8"); //②寫出響應體內容 response.getWriter().write("<font style='color:red'>hello</font>"); } ``` &lt;!--[endif]--&gt; 如上所示,通過response.setContentType("text/html;charset=utf-8") 告訴客戶端響應體媒體類型為html,編碼為utf-8,大家可以通過chrome工具查看響應頭為“Content-Type:text/html;charset=utf-8”,還一個“Content-Length:36”表示響應體大小。 代碼在cn.javass.chapter6.web.controller.consumesproduces.contenttype.ResponseContentTypeController。 **如上代碼可以看出Content-Type可以指定請求/響應的內容體的媒體格式和可選的編碼方式。如圖6-9**? ![](https://box.kancloud.cn/2016-05-15_5737edda78fda.jpg) ①客戶端—發送請求—服務器:客戶端通過請求頭Content-Type指定內容體的媒體類型(即客戶端此時是生產者),服務器根據Content-Type消費內容體數據(即服務器此時是消費者); ②服務器—發送請求—客戶端:服務器生產響應頭Content-Type指定的響應體數據(即服務器此時是生產者),客戶端根據Content-Type消費內容體數據(即客戶端此時是消費者)。 **問題:** ①服務器端可以通過指定【headers = "Content-Type=application/json"】來聲明可處理(可消費)的媒體類型,即只消費Content-Type指定的請求內容體數據; ②客戶端如何告訴服務器端它只消費什么媒體類型的數據呢?即客戶端接受(需要)什么類型的數據呢?服務器應該生產什么類型的數據?此時我們可以請求的Accept請求頭來實現這個功能。 **三、Accept:**用來指定什么媒體類型的響應是可接受的,即告訴服務器我需要什么媒體類型的數據,此時服務器應該根據Accept請求頭生產指定媒體類型的數據。 **2.1、json數據** **(1、服務器端控制器** ``` @RequestMapping(value = "/response/ContentType", headers = "Accept=application/json") public void response2(HttpServletResponse response) throws IOException { //①表示響應的內容區數據的媒體類型為json格式,且編碼為utf-8(客戶端應該以utf-8解碼) response.setContentType("application/json;charset=utf-8"); //②寫出響應體內容 String jsonData = "{\"username\":\"zhang\", \"password\":\"123\"}"; response.getWriter().write(jsonData); } ``` 服務器根據請求頭“Accept=application/json”生產json數據。 **(2、客戶端端接收服務器端json數據響應** **使用瀏覽器測試(Ajax場景使用該方式)** 請求地址為:http://localhost:9080/springmvc-chapter6/response/ContentType,且把修改請求頭Accept改為“Accept=application/json”: ![](https://box.kancloud.cn/2016-05-15_5737edda97d64.jpg) 大家可以下載chrome的JSONView插件來以更好看的方式查看json數據,安裝地址:https://chrome.google.com/webstore/detail/chklaanhfefbnpoihckbnefhakgolnmc **使用普通客戶端測試(服務器之間通信可使用該方式)** ``` private static void jsonRequest() throws IOException, URISyntaxException { //請求的地址 String url = "http://localhost:9080/springmvc-chapter6/response/ContentType"; //①創建Http Request(內部使用HttpURLConnection) ClientHttpRequest request = new SimpleClientHttpRequestFactory(). createRequest(new URI(url), HttpMethod.POST); //②設置客戶端可接受的媒體類型(即需要什么類型的響應體數據) request.getHeaders().set("Accept", "application/json"); //③發送請求并得到響應 ClientHttpResponse response = request.execute(); //④得到響應體的編碼方式 Charset charset = response.getHeaders().getContentType().getCharSet(); //⑤得到響應體的內容 InputStream is = response.getBody(); byte bytes[] = new byte[(int)response.getHeaders().getContentLength()]; is.read(bytes); String jsonData = new String(bytes, charset); System.out.println("charset : " + charset + ", json data : " + jsonData); } ``` request.getHeaders().set("Accept", "application/json"):表示客戶端只接受(即只消費)json格式的響應數據; response.getHeaders():可以得到響應頭,從而可以得到響應體的內容類型和編碼、內容長度。 **2.2、xml數據** **(1、服務器端控制器** ``` @RequestMapping(value = "/response/ContentType", headers = "Accept=application/xml") public void response3(HttpServletResponse response) throws IOException { //①表示響應的內容區數據的媒體類型為xml格式,且編碼為utf-8(客戶端應該以utf-8解碼) response.setContentType("application/xml;charset=utf-8"); //②寫出響應體內容 String xmlData = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"; xmlData += "<user><username>zhang</username><password>123</password></user>"; response.getWriter().write(xmlData); } ``` 和生產json數據唯一不同的兩點:請求頭為“Accept=application/xml”,響應體數據為xml。 **(2、客戶端端接收服務器端xml數據響應** **使用瀏覽器測試(Ajax場景使用該方式)** 請求地址為:http://localhost:9080/springmvc-chapter6/response/ContentType,且把修改請求頭Accept改為“Accept=application/xml”,和json方式類似,此處不再重復。 **使用普通客戶端測試(服務器之間通信可使用該方式)** ``` private static void xmlRequest() throws IOException, URISyntaxException { //請求的地址 String url = "http://localhost:9080/springmvc-chapter6/response/ContentType"; //①創建Http Request(內部使用HttpURLConnection) ClientHttpRequest request = new SimpleClientHttpRequestFactory(). createRequest(new URI(url), HttpMethod.POST); //②設置客戶端可接受的媒體類型(即需要什么類型的響應體數據) request.getHeaders().set("Accept", "application/xml"); //③發送請求并得到響應 ClientHttpResponse response = request.execute(); //④得到響應體的編碼方式 Charset charset = response.getHeaders().getContentType().getCharSet(); //⑤得到響應體的內容 InputStream is = response.getBody(); byte bytes[] = new byte[(int)response.getHeaders().getContentLength()]; is.read(bytes); String xmlData = new String(bytes, charset); System.out.println("charset : " + charset + ", xml data : " + xmlData); } ``` request.getHeaders().set("Accept", "application/xml"):表示客戶端只接受(即只消費)xml格式的響應數據; response.getHeaders():可以得到響應頭,從而可以得到響應體的內容類型和編碼、內容長度。 許多開放平臺,都提供了同一種數據的多種不同的表現形式,此時我們可以根據Accept請求頭告訴它們我們需要什么類型的數據,他們根據我們的Accept來判斷需要返回什么類型的數據。 實際項目使用Accept請求頭是比較麻煩的,現在大多數開放平臺(國內的新浪微博、淘寶、騰訊等開放平臺)使用如下兩種方式: 擴展名:如response/ContentType.json response/ContentType.xml方式,使用擴展名表示需要什么類型的數據; 參數:如response/ContentType?format=json response/ContentType?format=xml,使用參數表示需要什么類型的數據; 也就是說,目前我們可以使用如上三種方式實現來告訴服務器我們需要什么類型的數據,但麻煩的是現在有三種實現方式,難道我們為了支持三種類型的數據就要分別進行三種實現嗎?當然不要這么麻煩,后續我們會學ContentNegotiatingViewResolver,它能幫助我們做到這一點。 #### 6.6.5.2、生產者消費者流程圖 **生產者消費者流程,如圖6-10:** ![](https://box.kancloud.cn/2016-05-15_5737eddaaf278.jpg) 從圖6-10可以看出: 請求階段:客戶端是生產者【生產Content-Type媒體類型的請求內容區數據】,服務器是消費者【消費客戶端生產的Content-Type媒體類型的請求內容區數據】; 響應階段:服務器是生產者【生產客戶端請求頭參數Accept指定的響應體數據】,客戶端是消費者【消費服務器根據Accept請求頭生產的響應體數據】。 如上生產者/消費者寫法無法很好的體現我們分析的生產者/消費者模式,Spring3.1為生產者/消費者模式提供了簡化支持,接下來我們學習一下如何在Spring3.1中來實現生產者/消費者模式吧。 #### 6.6.5.3、生產者、消費者限定 Spring3.1開始支持消費者、生產者限定,而且必須使用如下HandlerMapping和HandlerAdapter才支持: ``` <!--Spring3.1開始的注解 HandlerMapping --> <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/> <!--Spring3.1開始的注解 HandlerAdapter --> <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"/> ``` **一、功能處理方法是消費者** @RequestMapping(value = "/consumes", consumes = {"application/json"}):此處使用consumes來指定功能處理方法能消費的媒體類型,其通過請求頭的“Content-Type”來判斷。 此種方式相對使用@RequestMapping的“headers = "Content-Type=application/json"”更能表明你的目的。 服務器控制器代碼詳解cn.javass.chapter6.web.controller.consumesproduces.ConsumesController; 客戶端代碼類似于之前的Content-Type中的客戶端,詳見ConsumesClient.。 **二、功能處理方法是生產者** @RequestMapping(value = "/produces", produces = "application/json"):表示將功能處理方法將生產json格式的數據,此時根據請求頭中的Accept進行匹配,如請求頭“Accept:application/json”時即可匹配; @RequestMapping(value = "/produces", produces = "application/xml"):表示將功能處理方法將生產xml格式的數據,此時根據請求頭中的Accept進行匹配,如請求頭“Accept:application/xml”時即可匹配。 此種方式相對使用@RequestMapping的“headers = "Accept=application/json"”更能表明你的目的。 服務器控制器代碼詳解cn.javass.chapter6.web.controller.consumesproduces.ProducesController; 客戶端代碼類似于之前的Content-Type中的客戶端,詳見ProducesController.。 當你有如下Accept頭: ①Accept:text/html,application/xml,application/json 將按照如下順序進行produces的匹配 ①text/html ②application/xml ③application/json ②Accept:application/xml;q=0.5,application/json;q=0.9,text/html 將按照如下順序進行produces的匹配 ①text/html ②application/json ③application/xml q參數為媒體類型的質量因子,越大則優先權越高(從0到1) ③Accept:*/*,text/*,text/html 將按照如下順序進行produces的匹配 ①text/html ②text/* ③*/* 即匹配規則為:最明確的優先匹配。 代碼詳見ProducesPrecedenceController1、ProducesPrecedenceController2、ProducesPrecedenceController3。 Accept詳細信息,請參考[http://tools.ietf.org/html/rfc2616#section-14.1](http://tools.ietf.org/html/rfc2616#section-14.1)。 **三、窄化時是覆蓋 而 非繼承** 如類級別的映射為 @RequestMapping(value="/narrow", produces="text/html"),方法級別的為@RequestMapping(produces="application/xml"),此時方法級別的映射將覆蓋類級別的,因此請求頭“Accept:application/xml”是成功的,而“text/html”將報406錯誤碼,表示不支持的請求媒體類型。 詳見cn.javass.chapter6.web.controller.consumesproduces.NarrowController。 只有生產者/消費者 模式 是 覆蓋,其他的使用方法是繼承,如headers、params等都是繼承。 **四、組合使用是“或”的關系** @RequestMapping(produces={"text/html", "application/json"}) :將匹配“Accept:text/html”或“Accept:application/json”。 **五、問題** 消費的數據,如JSON數據、XML數據都是由我們讀取請求的InputStream并根據需要自己轉換為相應的模型數據,比較麻煩; 生產的數據,如JSON數據、XML數據都是由我們自己先把模型數據轉換為json/xml等數據,然后輸出響應流,也是比較麻煩的。 Spring提供了一組注解(`@RequestBody、@ResponseBody`)和一組轉換類(`HttpMessageConverter`)來完成我們遇到的問題,詳見6.6.8節。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看