Webx框架:RequestContext詳解

RequestContext

RequestContext可以看成request和response的合體。多個RequestContext還可以串起來,就像Filter鏈條一樣。每個外層RequestContext都會在內層RequestContext的基礎上增加功能。在設計模式中這叫裝飾器。


RequestContext種類有basic/buffered/lazy-commit/parser/rewrite/session/set-locale功能。後面還會具體介紹。


下面是配置方法:

<services:request-contexts xmlns="http://www.alibaba.com/schema/services/request-contexts">
<basic />
<buffered />
<lazy-commit />
<parser />
<set-locale defaultLocale="zh_CN" defaultCharset="UTF-8" />
<!-- Optional -
<session />
<rewrite />
-->
</services:request-contexts>


RequestContext之間是有依賴關係的,比如session依賴於basic。框架會自動根據依賴關係排序RequestContext,所以開發的時候可以不考慮順序。


訪問特定的RequestContext。如果想要通過ParserRequestContext獲取上傳的文件信息怎麼做呢?可以通過findRequestContext獲取文件信息。

ParserRequestContext parserRequestContext =
RequestContextUtil.findRequestContext(request, ParserRequestContext.class);
ParameterParser params = parserRequestContext.getParameters();
FileItem myfile = params.getFileItem("myfile");
String filename = myfile.getName();
InputStream istream = myfile.getInputStream();


RequestContext種類

BasicRequestContext。基礎RC,它可以包含多個攔截器。配置方法如下。

<basic>
<request-contexts:interceptors
xmlns="http://www.alibaba.com/schema/services/request-contexts/basic/interceptors">
<interceptor class="...Interceptor1" />
<interceptor class="...Interceptor2" />
</request-contexts:interceptors>
</basic>


不管有沒有聲明,BasicRC總是會啓用一個默認攔截器。默認攔截器主要是一些安全檢查,避免Http Header Value中出現CRLF,status message增加html escape,限制cookie的總大小。下面這個例子給默認攔截器增加參數。

<request-contexts:interceptors
xmlns="http://www.alibaba.com/schema/services/request-contexts/basic/interceptors">
<response-header-security-filter maxSetCookieSize="5K" />
</request-contexts:interceptors>


set-locale會將默認的Locale和默認的字符集保存在ThreadLocal中,調用框架提供的StringEscapeUtils.escapeURL,TemplateService都會根據當前線程的Locale和字符集自動編碼解碼。TemplateService會根據Locale尋找對應的模板文件,比如TestPage_zh_CN.vm。


set-locale參數:

<set-locale defaultLocale="..."
defaultCharset="..."
inputCharsetParam="_input_charset"
outputCharsetParam="_output_charset"
paramKey="_lang" 持久Locale和字符集的query參數名
sessionKey="_lang" /> 持久Locale和字符集的session名


parser用於解析請求。通常情況下使用下面的配置就足夠了。

<services:request-contexts xmlns="http://www.alibaba.com/schema/services/request-contexts">
<parser />
...
</services:request-contexts>
<services:upload sizeMax="5M" fileSizeMax="2M" />


上面這段代碼能處理所有請求包括get請求,post請求,文件上傳post請求。


通過ParserRequestContext訪問。

@Autowired
ParserRequestContext parser;
...
String s = parser.getParameters().getString("myparam");
parser.getParameters().getBoolean("myparam");
parser.getParameters().getString("myparam", "default_value");
parser.getParameters().getFileItem("myfile");


訪問Cookie。

parser.getCookies().getString("mycookie");


upload標籤的參數。

<services:upload sizeMax="5M"
fileSizeMax="2M"
repository="/tmp"
sizeThreshold="10K"
keepFormFieldInMemory="true" />


repository上傳文件臨時文件夾,是ResourceLoader的裝載路徑而不是物理路徑。

sizeThreshold,小於這個大小的文件將保存在內存中。

keepFormFieldInMemory,是否將表單中的字段保存在內存中。默認爲false。


手動解析上傳文件的請求。首先關閉自動解析。

<parser autoUpload="false">

然後在需要的時候調用parseUpload。

parser.getParameters().parseUpload();

還可以指定不同的參數。

UploadParameters params = new UploadParameters();
params.applyDefaultValues();
params.setSizeMax(new HumanReadableSize("10M"));
params.setFileSizeMax(new HumanReadableSize("1M"));
params.setRepository(new File("mydir"));
parser.getParameters().parseUpload(params);


parser中獲取參數是非常靈活的,請看下面的例子,它們都爲了獲取MyProductId。

request.getParameter("MyProductId");
request.getParameter("myProductId");
request.getParameter("my_product_id");
request.getParameter("MY_PRODUCT_ID");
request.getParameter("MY_productID");

如果不需要這種靈活性,可以在配置中關閉:

<parser caseFolding="none">


trim。默認情況下parser會對所有的參數進行trim,如果不需要這種操作,可以在配置中關掉。

<parser trimming="false">


entity編碼。默認情況下,框架如果發現當前的charset無法表示請求中的字符時,就會將參數替換成entity編碼,比如&#20320;&#22909;

可以通過下面的代碼關閉:

<parser unescapeParameters="false">


獲取自定義類型的參數值。

MyEnum myEnum = params.getObjectOfType("myparam", MyEnum.class);


如果需要獲取日期參數,在配置中加入下面的代碼:

<parser>
<property-editor-registrar
class="com.alibaba.citrus.service.configuration.support.CustomDateRegistrar"
p:format="yyyy-MM-dd" p:locale="zh_CN" p:timeZone="GMT+8" />
</parser>


如果類型轉換出錯,有兩種處理方式,一種是保持安靜,另外一種是拋出異常。

保持安靜的情況下,如果類型轉傳出錯,會使用默認值。

下面這段配置目的是如果轉換出錯,則拋出異常。

<parser converterQuiet="false">


GET請求中的中文參數。標準規定URL中的中文字符必須使用UTF8進行編碼,但是並不是所有的廠商都遵循這種規定。

parser將URL中的參數使用input_charset進行解碼。

除了這種默認的解碼方式,還提供了另外的備用方法。


使用servlet原本的解碼機制。

<parser useServletEngineParser="true" />

servlet原本的解碼機制不會根據set-locale中指定的編碼,而是使用web容器的配置文件中指定的編碼方式。


使用固定解碼方式。

<parser URIEncoding="UTF-8" useBodyEncodingForURI="false" />


過濾請求參數。比如下面的代碼限制了上傳文件的擴展名。

<parser>
<filters>
<parser-filters:uploaded-file-whitelist extensions="jpg, gif, png" />
</filters>
</parser>


buffered。在webx中,有screen、control、layout,它們都代表輸出的頁面。layout可以嵌套screen和control,screen可以嵌套control,control可以嵌套另外的control。在嵌套的時候,內層頁面渲染的內容都會暫存在內存中,等到外層頁面需要的時候再輸出到自己的頁面中。webx中有buffer棧,每個組件在渲染之前都會在棧中增加新的buffer,渲染的內容都會保存在棧頂buffer中,渲染完成之後彈出buffer棧。


提交時框架會檢查buffer棧中是否只有一個緩衝區,如果不是就報錯。


buffered配置方法:

<buffered />

不需要任何參數。


操作BufferedRequestContext,下面的例子演示了push和pop兩種操作。

@Autowired
BufferedRequestContext buffered;
@Autowired
HttpServletResponse response;
...
PrintWriter out = response.getWriter();
buffered.pushBuffer(); // 創建新buffer,並壓入棧頂
out.print("world"); // 在新 buffer 中寫入
String content = buffered.popCharBuffer(); // 彈出頂層 buffer
out.print("hello, ");
out.print(content); // 寫入較低層的 buffer


如果調用了getWriter就要通過popCharBuffer獲取內容,如果調用了getOutputStream就要通過popByteBuffer獲取內容。


關閉buffer機制。一般情況下不需要關閉,不過有時候需要生成很大的一個文件比如pdf、excel等,並且生成所消耗的時間也很長,如果有buffer,那麼用戶需要等到文件生成完畢之後才能開始下載,這種延遲肯定是不能接受的。如果網站裏面沒有組件嵌套,那麼buffer也可以關閉以提高性能。


lazy-commit攔截response對象中的某些方法,阻止渲染內容被自動提交。當請求處理完成後再提交。和buffered配合使用效果更佳。如果沒有buffered那麼當緩衝區中的內容達到8K之後就會自動提交。


lazy-commit配置方法,沒有任何參數。

<lazy-commit />


LazyCommitRequestContext提供了一些接口可以獲取當前請求是否出錯,是否重定向,狀態碼是什麼


rewrite。非常類似於Apache中的mod_rewrite。它目的是將請求的URL映射到內部的URL。


rewrite是一個web應用,因此被匹配的URL是servletPath + pathInfo。


rewrite執行過程是先檢查pattern是否匹配,如果匹配,再各個檢查condition是否都滿足,如果不滿足就檢查下一條rule。如果都滿足,就執行substitution和handler。


servletPath和pathInfo。比如一個Servlet掛在/test/abc的URL上,那麼一個URL/test/abc/ddd的servletPath就是/test/abc,pathInfo就是/ddd。


規則匹配的時候使用正則表達式,有專門的書,這裏就不介紹了。


下面是rewite的配置例子。rule表示需要匹配的URL,condition表示匹配之後需要檢查的條件,substitution表示替換規則,handler表示使用自定義方法更靈活地替換URL。

<services:request-contexts xmlns="http://www.alibaba.com/schema/services/request-contexts">
<rewrite>
<!-- rule 1 -->
<rule pattern="...">
<condition test="..." pattern="..." flags="..." />
<condition test="..." pattern="..." flags="..." />
<substitution uri="..." flags="...">
<parameter key="..." value="..." />
<parameter key="..." value="..." />
<parameter key="..." value="..." />
</substitution>
<handlers>
<rewrite-handlers:handler class="..." />
</handlers>
</rule>
<!-- rule 2 -->
<rule pattern="...">
</rule>
<!-- rule 3 -->
<rule pattern="...">
</rule>
</rewrite>
...
</services:request-contexts>


condition用法例子。pattern屬性也是使用正則表達式。

<condition test="%{SERVER_NAME}:%{SERVER_PORT}" pattern="www.(\w+).com:8080" />
<condition test="%{QUERY:x}" pattern="!1" />
<condition test="%{QUERY:y}" pattern="2" />


substitution例子。

<substitution uri="/%1/new_$1\.htm" />
$1表示rule中regex group,%1表示最後一個匹配的condition中的regex group。


替換參數。替換之後,其他參數都會被刪除

<substitution>
<parameter key="ext" value="$1" />
<parameter key="host" value="%1" />
<parameter key="count">
<value>1</value>
<value>2</value>
<value>3</value>
</parameter>
</substitution>


當一條rule規則替換完成之後,默認會繼續執行後續的rule,使用已經替換的URL和參數繼續匹配。


substitution中的flag可以是下面的參數:

  • L last:停止匹配
  • C chain:串接rule,就是默認行爲
  • QSA qsappend:保留原來的請求參數
  • R=301:永久重定向
  • R=302:臨時重定向


301跳轉用於SEO。


多個參數使用逗號隔開。


重定向例子:

<substitution uri="/new_hello.htm" flags="L,R,QSA" />
<substitution uri="http://www.other-site.com/new_hello.htm" flags="L,R" />


handler。用於彌補正則表達式的不足。正則只能執行簡單的替換,對於更加複雜的功能就需要用到handler。

webx框架提供URL規格化的handler。

<rewrite-handlers:handler
class="com.alibaba.citrus.service.requestcontext.rewrite.support.UrlNormalizer"
/>
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章