如何寫一個HttpClient[1]——URI的處理


如何寫一個HttpClient[1]——URI的處理

在翻閱apache的http client的代碼的時候,看到org.apache.http.client.utils.URIBuilder.java的寫法,感覺甚妙。特意分析一下源碼,並且對比幾種不同的URI寫法。

本文目錄


Apache的HttpClient

作為字符串的URI

假設我們有一個HttpClient類,這個類用來發起http請求並且做出響應。如下:

class HttpClient{

	...
}

現在要把URI作為參數,傳遞給HttpClient類,好讓它知道對誰發起http請求。URI可以是諸如 https://www.google.com/#q=編程狗的博客 這樣一個簡單的string,一個簡單的實現就是給HttpClient類提供一個設置URL的方法:

class HttpClient{
	...
	public void setURI(String uri){
    	...
    }
	...
}

這應該是最簡單的但卻是最不成熟的做法了。它有很多弊端,但我們暫時不講出來,且往下看。

作為類對象的URI

現在我們需要更換URI,但又不是全部更換,什么意思呢?不妨分幾個部分來看看URI:

Scheme -> https
Host -> www.google.com
Path -> /#q=編程狗的博客

https的可能換成httpwww.google.com可能會換成www.google.com.hk,/#q=編程狗的博客可能換成其他的關鍵詞。我們暫且把這個過程稱為參數的置換。如果我們僅僅給客戶端類HttpClient提供一個setURI(String uri)方法,上面的置換只能通過換掉整個URI實現了。比如把https://www.google.com/#q=編程狗的博客 換成 http://www.google.com/#q=編程狗的博客,而沒有辦法直接把"https"換成"http"

於是,我們不妨提供再一下幾個方法,以使我們很方便的做出上面的置換

public void setScheme(String scheme){
	...
}

public void setHost(String host){
	...
}

public void setPath(String path){
	...
}

這三個方法分別用來設置schemehostpath。為了敘述方便,沒有對參數進行檢查。僅僅提供這三個設置參數的方法是不能過實現置換的,因為對於傳入的URI,必須先分割schemehostpath三部分。當然,我們可以的再添加一個splitURI方法,實現分割;但是,我們已然發現如果這樣做了,HttpClient類就有至少4個方法處理URI了,將來可能還會增加,HttpClient類可能會有數十個方法的作用與http信息收發不相關,這樣勢必是一種混亂,並且影響擴展性,因此我們不這樣做。我們應當有一個URI類,專門處理URI的置換分割

class URI{
	//構造函數,默認把uri分割好。
    public URI(String uri){
    ...
    	splitURI();
    ...
    }

	//分割方法
    private void splitURI(){
    ...
    }

    //set方法
	public void setScheme(String scheme){
	...
	}

	public void setHost(String host){
	...
	}

	public void setPath(String path){
	...
	}


    //get方法
    public String getScheme(){
	...
    	return scheme;
	}

	public String setHost(){
	...
        return host;
	}

	public String setPath(){
	...
        return path;
	}
}

同時我們也更新HttpClient類

class HttpClient{
	...
	public void setURI(URI uri){
    	...
    }
	...
}

我們可以這樣使用它們:

URI uri = new URI("https://www.google.com/#q=編程狗的博客");
uri.getScheme();// https
uri.getHost();// www.google.com
uri.getPath();// /#q=編程狗的博客
...
uri.setScheme();// https(如果你確實需要重新設置)
...
HttpClient client = new HttpClient();
client.setURI(uri);

作為Builder的URI

到現在為止已經可以應付關於URI的很多需求了,但是我們如果要求URI類是一個不可變的類,為什么不可變呢?因為不可變更加安全。但那些set方法就沒有用處了。apache把URI的set方法去掉了,如果想設置參數,現在只能通過構造函數:

//重載構造函數
public URI(String str){
...
}
/**
     * @param   scheme    Scheme name
     * @param   userInfo  User name and authorization information
     * @param   host      Host name
     * @param   port      Port number
     * @param   path      Path
     * @param   query     Query
     * @param   fragment  Fragment
*/
public URI(String scheme,
               String userInfo, String host, int port,
               String path, String query, String fragment){
...
}

...
//用法如
URI uri = new URI("http","username:program-dog","program-dog.blogspot.com","/","","");

我們至少有7個參數,如果要滿足各種需求的組合,恐怕總共要提供∑(C^7^~i~)(i=1~7)種構造函數,顯然不現實。然而,URIBuilder既可以造出一個不可變的URI,又可以兼顧N種參數。URIBuilder可以這樣用:

//  http://www.google.com/search?q=編程狗的博客&btnG=Google+Search&aq=f&oq=
URI uri = new URIBuilder()
	.setScheme("http")
	.setHost("www.google.com")
	.setPath("/search")
	.setParameter("q", "編程狗的博客")
	.setParameter("btnG", "Google Search")
	.setParameter("aq", "f")
	.setParameter("oq", "")
	.build();

URIBuilder正是采用了Builder Pattern(建造者模式)。等號右邊實際上是一行,先創建一個URIBuilder對象實例,調用實例的setScheme方法,此方法順便返回URIBuilder對象實例,剛剛返回的這個實例調用setHost方法,...,最后一個返回的URIBuilder對象實例調用build方法,返回URI對象。它是如何實現的呢?

原來的URI類的set方法的基礎上,添加一個返回值,返回URIBuilder自己就夠了:

class URIBuilder{
	public URIBuilder setScheme(String scheme){
	...
    	return this;
	}

	public URIBuilder setHost(String host){
	...
    	return this;
	}

	public URIBuilder setPath(String path){
	...
    	return this;
	}

    //built 方法,把參數拼接,然后返回一個URI類
	public URI built(){
    ...
    	return uri;
    }
}

由於URIBuilder每次都返回它自己,所以可以連續的執行 set方法,最后通過built方法返回URI類。

結束

到此為止,一個簡單的URI類的寫法已經介紹完畢了。我們還是盡量把URI寫成類對象,並使它不可變,並且提供相應的Builder。



知識共享許可協議
本作品采用知識共享署名-非商業性使用-相同方式共享 4.0 國際許可協議進行許可。


本文地址:https://program-dog.blogspot.com/2016/06/HowToWriteAHttpClientAboutURI.html



免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM