如何写一个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的可能换成http,www.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){
...
}
这三个方法分别用来设置scheme,host和path。为了叙述方便,没有对参数进行检查。仅仅提供这三个设置参数的方法是不能过实现置换的,因为对于传入的URI,必须先分割成scheme,host和path三部分。当然,我们可以的再添加一个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。
本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。本文地址:https://program-dog.blogspot.com/2016/06/HowToWriteAHttpClientAboutURI.html
如何写一个HttpClient[1]——URI的处理的更多相关文章
- 如何写一个简单的http服务器
最近几天用C++写了一个简单的HTTP服务器,作为学习网络编程和Linux环境编程的练手项目,这篇文章记录我在写一个HTTP服务器过程中遇到的问题和学习到的知识. 服务器的源代码放在Github. H ...
- Java Web 开发利用Struts2+Spring+mybatis写一个用户登录界面以及简单的数据交互
框架的东西太复杂也难以讲通,直接上代码: 一.首先得配置环境 和导入必要的jar包 有一些重要的如下: Filter文件夹下的SafetyFilter.java model文件夹下的 Global ...
- 【Filter 不登陆无法访问】web项目中写一个过滤器实现用户不登陆,直接给链接,无法进入页面的功能
在web项目中写一个过滤器实现用户不登陆,直接给链接,无法进入页面,而重定向到登陆界面的功能. 项目是用springMVC+spring+hibernate实现 (和这个没有多大关系) 第一步: 首先 ...
- 【Filter 页面重定向循环】写一个过滤器造成的页面重定向循环的问题
今天做一个过滤器,碰上页面重定向循环的情况: 浏览器的访问路径是:http://192.168.16.104:8080/biologyInfo/login/login/login/login/logi ...
- 与众不同 windows phone (38) - 8.0 关联启动: 使用外部程序打开一个文件或URI, 关联指定的文件类型或协议
[源码下载] 与众不同 windows phone (38) - 8.0 关联启动: 使用外部程序打开一个文件或URI, 关联指定的文件类型或协议 作者:webabcd 介绍与众不同 windows ...
- (2)自己写一个简单的servle容器
自己写一个简单的servlet,能够跑一个简单的servlet,说明一下逻辑. 首先是写一个简单的servlet,这就关联到javax.servlet和javax.servlet.http这两个包的类 ...
- 用c++写一个 “hello,world” 的 FastCGI程序
原文:http://chriswu.me/blog/writing-hello-world-in-fcgi-with-c-plus-plus/ 上面的连接地址给出的是作者的原文地址. 另外一个作者稍微 ...
- 手动的写一个structs
为了更好的学习框架的运行机制,这里开始学习框架之前,介绍一个简单的自定义的框架. 需求: 登录:id:aaa,pwd:888登录成功之后,跳转到,index.jsp页面并显示,欢迎你,aaa 注册,页 ...
- 爬虫入门 手写一个Java爬虫
本文内容 涞源于 罗刚 老师的 书籍 << 自己动手写网络爬虫一书 >> ; 本文将介绍 1: 网络爬虫的是做什么的? 2: 手动写一个简单的网络爬虫; 1: 网络爬虫是做 ...
随机推荐
- centos关闭防火墙
Centos7 关闭防火墙 CentOS 7.0默认使用的是firewall作为防火墙,使用iptables必须重新设置一下 1.直接关闭防火墙 systemctl stop firewalld.se ...
- json字符串转泛型集合对象
Dictionary<string, object> jd = js.Deserialize<Dictionary<string, object>>(item); ...
- 针对功能权限(url访问)如何避免越权访问
你可以用request获得之前的页面路径:Request.getHeader("Referer");然后你可以判断一下,这个是字符串类型的. 如果是需要登录的,你可以从sessio ...
- 常见的JAVA包
java.lang 提供利用 Java 编程语言进行程序设计的基础类.最重要的类是 Object(它是类层次结构的根)和 Class(它的实例表示正在运行的应用程序中的类). java.util 包含 ...
- maven---install报错
若maven项目在install或者run的时候出现莫名奇妙的问题,应该考虑是否在pom.xml中引入的包是否为最新的包: 因为在本地maven仓库中已经存在的包就不会再下载,所以可以从这方面排查问题 ...
- springmvc配置多视图 - tiles, velocity, freeMarker, jsp
转自: http://www.cnblogs.com/shanheyongmu/p/5684595.html <!-- Velocity --> <bean id="vel ...
- 关于apache做301的问题
http://www.internetmarketingninjas.com/blog/search-engine-optimization/301-redirects/ RedirectMatch ...
- Oracle中PL/SQL的执行部分和各种流程控制
Oracle中PL/SQL的执行部分和异常部分 一.PL/SQL的执行部分. 赋值语句. 赋值语句分两种,一种是定义一个变量,然后接收用户的IO赋值:另一种是通过SQL查询结果赋值. 用户赋值举例: ...
- utils.js
/** * //2.0检测方式(目测,测量,专用仪器测试等) function GetCheckType() { $.ajax({ url: '@Url.Action("GetCheckTy ...
- Oracle --获取绑定变量的值.
SELECT * FROM DBA_HIST_SQLBIND WHERE SNAP_ID>67073 AND SNAP_ID<=67079 AND SQL_ID='3DR3410F086P ...
