nanohttpd:實現跨域(CORS)請求

NanoHTTPD是一個輕量級的HTTP服務器,可以很方便地嵌入到Java程序中。所以在android平臺上有廣泛的使用。
NanoHTTPD默認是不支持訪問跨域(CORS)請求的。如果希望自己的NanoHTTPD服務支持CORS,就要自己實現對CORS請求的響應。
關於什麼是CORS,這個文檔有非常詳細、清晰、全面的闡述:
《HTTP訪問控制(CORS)》,如果還不太清楚CORS機制的童鞋,建議先看這篇 文章補補課。

實現對跨域訪問的支持的關鍵就是要響應跨請求,跨域請求的METHOD爲OPTIONS,對收到的HTTP請求要先識別是否爲跨域請求,如果是就發送正確的響應。下面是nanohttpd響應CORS請求的基本邏輯

    @Override
    public Response serve(IHTTPSession session) {
    	// 判斷是否爲跨域預請求
    	if(isPreflightRequest(session)){
    		// 如果是則發送CORS響應告訴瀏覽HTTP服務支持的METHOD及HEADERS和請求源
    		return responseCORS(session);
    	}
    	// 業務邏輯
    	.....
    	/////
    	return wrapResponse(session,responseAck(ack));
    }

下面是上述代碼中調用的子方法的實現,
注意:因爲nanohttp的headers中所有的key都是全小寫,所以你會發現下面的代碼,從headers獲取header時,header的名字都是小寫的。

	/**
	 * 判斷是否爲CORS 預檢請求請求(Preflight)
	 * @param session
	 * @return
	 */
	private static boolean isPreflightRequest(IHTTPSession session) {
		Map<String, String> headers = session.getHeaders();
		return Method.OPTIONS.equals(session.getMethod()) 
				&& headers.containsKey("origin") 
				&& headers.containsKey("access-control-request-method") 
				&& headers.containsKey("access-control-request-headers");
	}
		/**
	 * 向響應包中添加CORS包頭數據
	 * @param session
	 * @return
	 */
	private Response responseCORS(IHTTPSession session) {
		Response resp = wrapResponse(session,newFixedLengthResponse(""));
		Map<String, String> headers = session.getHeaders();
		resp.addHeader("Access-Control-Allow-Methods","POST,GET,OPTIONS");

		String requestHeaders = headers.get("access-control-request-headers");
		String allowHeaders = MoreObjects.firstNonNull(requestHeaders, "Content-Type");
		resp.addHeader("Access-Control-Allow-Headers", allowHeaders);
		//resp.addHeader("Access-Control-Max-Age", "86400");
		resp.addHeader("Access-Control-Max-Age", "0");
		return resp;
	}
	/**
	 * 封裝響應包
	 * @param session http請求
	 * @param resp 響應包
	 * @return resp
	 */
	private Response wrapResponse(IHTTPSession session,Response resp) {
		if(null != resp){			
			Map<String, String> headers = session.getHeaders();
			resp.addHeader("Access-Control-Allow-Credentials", "true");
			// 如果請求頭中包含'Origin',則響應頭中'Access-Control-Allow-Origin'使用此值否則爲'*'
			// nanohttd將所有請求頭的名稱強制轉爲了小寫
			String origin = MoreObjects.firstNonNull(headers.get("origin", "*");
			resp.addHeader("Access-Control-Allow-Origin", origin);
			
			String  requestHeaders = headers.get("access-control-request-headers");
			if(requestHeaders != null){
				resp.addHeader("Access-Control-Allow-Headers", requestHeaders);
			}
		}
		return resp;
	}

完整的代碼參見碼雲倉庫代碼:
gu.dtalk.engine.DtalkHttpServer.java

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章