如何写一个HttpClient[1]——URI的处理

时间:2021-12-26 00:56:58

如何写一个HttpClient[1]——URI的处理

在翻阅apache的http client的代码的时候,看到org.apache.http.client.utils.URIBuilder.java的写法,感觉甚妙。特意分析一下源码,并且对比几种不同的URI写法。

本文目录

如何写一个HttpClient[1]——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个参数,如果要满足各种需求的组合,恐怕总共要提供∑(C7i)(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。


如何写一个HttpClient[1]——URI的处理
本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。

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


相关文章