天天看點

nanohttpd:實作跨域(CORS)請求

版權聲明:本文為部落客原創文章,遵循 CC 4.0 BY-SA 版權協定,轉載請附上原文出處連結和本聲明。

本文連結:https://blog.csdn.net/10km/article/details/102568909

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