[Servlet]Response.sendRedirect() 三兩事
sendRedirectvoid sendRedirect(java.lang.String location) throws java.io.IOException
|
然後我們再來來看 spring mvc "org.springframework.web.servlet.view.RedirectView" 實作的內容
1: /**
2: * Send a redirect back to the HTTP client
3: * @param request current HTTP request (allows for reacting to request method)
4: * @param response current HTTP response (for sending response headers)
5: * @param targetUrl the target URL to redirect to
6: * @param http10Compatible whether to stay compatible with HTTP 1.0 clients
7: * @throws IOException if thrown by response methods
8: */
9: protected void sendRedirect(
10: HttpServletRequest request, HttpServletResponse response, String targetUrl, boolean http10Compatible)
11: throws IOException {
12:
13: if (http10Compatible) {
14: // Always send status code 302.
15: response.sendRedirect(response.encodeRedirectURL(targetUrl));
16: }
17: else {
18: HttpStatus statusCode = getHttp11StatusCode(request, response, targetUrl);
19: response.setStatus(statusCode.value());
20: response.setHeader("Location", response.encodeRedirectURL(targetUrl));
21: }
22: }
撇開"Status Code" 不同外(註一) , 其實 "Location" 也是不同的.
因為當你呼 response.sendRedirect( 相對路徑 ), servlet container 會將它組合成絕對路徑(如: http://www.example.com.tw/test/index.jsp) 放在回應標頭.Location , 再交給瀏覽器處理.
而如果是用response.setHeader("Location", 相對路徑 ), 在回應標頭.Location 則是相對路徑, 再交給瀏覽器處理.
感覺上好像是無差異的過程, 但如果發生在較複雜的分散式系統架構, 如: 前面有個SSL Web Server (apache) 走HTTPS, 但後面的AP Server 則走HTTP,
這時如果 plug-in 的 ap module 沒有聰明到幫你Replace 回應標頭 Location 到真實世界的 https://www.exapmle.com.tw/test/index.jsp 而是 http:// ...
就會出現迷途的羊了 ...
註一:
前: HTTP/1.0 302 Moved Temporarily
後: HTTP/1.1 303 See Other
ps. 這題來自 Spring MVC “redirect:” prefix always redirects to http — how do I make it stay on https?
update 2011/12/24 上述推論, 其實證據還是太弱了, 還是把Tomcat 的底給打開來抓吧
1: /**
2: * Send a temporary redirect to the specified redirect location URL.
3: *
4: * @param location Location URL to redirect to
5: *
6: * @exception IllegalStateException if this response has
7: * already been committed
8: * @exception IOException if an input/output error occurs
9: */
10: @Override
11: public void sendRedirect(String location)
12: throws IOException {
13:
14: if (isCommitted())
15: throw new IllegalStateException
16: (sm.getString("coyoteResponse.sendRedirect.ise"));
17:
18: // Ignore any call from an included servlet
19: if (included)
20: return;
21:
22: // Clear any data content that has been buffered
23: resetBuffer();
24:
25: // Generate a temporary redirect to the specified location
26: try {
27: String absolute = toAbsolute(location);
28: setStatus(SC_FOUND);
29: setHeader("Location", absolute);
30: } catch (IllegalArgumentException e) {
31: setStatus(SC_NOT_FOUND);
32: }
33:
34: // Cause the response to be finished (from the application perspective)
35: setSuspended(true);
36:
37: }
第27 行超可疑的, 再向下看
1: boolean leadingSlash = location.startsWith("/");
2:
3: if (leadingSlash || !hasScheme(location)) {
4:
5: redirectURLCC.recycle();
6:
7: String scheme = request.getScheme();
8: String name = request.getServerName();
9: int port = request.getServerPort();
10:
11: try {
12: redirectURLCC.append(scheme, 0, scheme.length());
13: redirectURLCC.append("://", 0, 3);
14: redirectURLCC.append(name, 0, name.length());
15: if ((scheme.equals("http") && port != 80)
16: || (scheme.equals("https") && port != 443)) {
17: redirectURLCC.append(':');
18: String portS = port + "";
19: redirectURLCC.append(portS, 0, portS.length());
20: }
果然在第5~20行 被重置了 ... 這時第7~9行就是羊迷路的重點了, 如果被proxy rewrite過, 此時得到的可能被變動, 由其是 scheme( https to http), 這種問題在Google 上還真多解法,
1) 如果前頭有開80 port 則強制轉向443 , 所以在前頭的web or proxy server就會rewrite 到 443 port.
可以參考
http://www.cyberciti.biz/tips/howto-apache-force-https-secure-connections.html
http://wiki.apache.org/httpd/RewriteSSL
2) 如果前頭沒開80 port者, 可以在後頭設定第二組Connector 來 override HttpServletRequest 的scheme (http to https)
參考
http://forum.springsource.org/archive/index.php/t-108494.html
ps. 沒有環境可測其真實性.
3) 更絕的做法是, 自己硬改 (Open source 不就是愛來這招, spring 更讓人有許多發展空間 :D)
參考
http://forum.springsource.org/archive/index.php/t-38887.html
結語: 因為沒有時間去建立這麼多的環境來測試, 如果有人有興趣就試試看吧, 記得告訴我結果, 感慍
update 2011/12/26 ASP.NET 會不會有同樣問題呢?
剛試了一下, 答案:不會, 它不會轉成絕對路徑.
update 2011/12/27 玩了一個小把戲
edit ssl.conf
apache >= 2.2.4
<VirtualHost _default_:443>
<IfModule mod_headers.c>
Header edit Location "^http://example.com.tw" "https://example.com.tw"
</IfModule>
</VirtualHost>