标准的 URL 格式:

协议模式: // 主机名 : 可选的端口 / 资源路径?可选查询 # 可选的片段

即: s heme://authority:port/path?query#fragment

完整的 URL 格式:

协议模式: // 用户名 : 密码 @ 主机名 : 可选的端口 / 资源路径?可选查询 # 可选的片段

即: Scheme://userid:password@authority:port/path?query#fragment

注:用户信息一般使用,主要作用是在 URL 中添加用户信息的写法,这样可以省去访问 SVN 时要求输入用户登录信息

URL 各组成部分在 Header 消息头的位置如下图

注: fragment( 信息片段 ) :用于指定网络资源中的片段。

详细资料可参考:

(1)Things YouShould Know About Fragment URLs

http://blog.httpwatch.com/2011/03/01/6-things-you-should-know-about-fragment-urls/

(2) QQ 密码修改的小问题回顾下 URL Fragment

http://www.cnblogs.com/syf/archive/2013/04/02/2995903.html

二、 URI URL 区别

URI ,是 uniformresource identifier ,统一资源标识符,用来唯一的标识一个资源。 URI 有绝对和相对之分,绝对的 URI 指以 scheme (后面跟着冒号)开头的 URI 。例如 http://www.cnn.com/articles/articles.html 就是绝对的 URI ;相对的 URI 不是以 scheme (后面跟着冒号)开始的 URI ,例如 articles/articles.html 就是相对的 URI

URL uniformresource locator ,统一资源定位器,它是一种具体的 URI ,即 URL 可以用来标识一个资源。 URL URI 的一个子集,指明了如何 locate 这个资源。也即是说 URL 是一种具体的 URI ,它不仅唯一标识资源,而且还提供了定位该资源的信息。需要注意 URL 必须提供足够的信息来定位,是绝对的,而通常说的 relative URL ,则是针对 absolute URL 来说的,本质上还是绝对的。

注: URI 的研究出现就是为了弥补 URL 的一些缺点,例如当资源改变时 URL 也需要变化的问题等

详细资料可参考:

http://docs.oracle.com/javase/1.5.0/docs/api/java/net/URI.html

http://en.wikipedia.org/wiki/Uniform_Resource_Identifier

三、 HttpClient 中的 URI

HttpClient 支持的 URL 格式为 ( 默认忽略不处理 fragment)

Scheme://[userid:password@]authority:port/path?query

一些知识的整理

URI-reference = [absoluteURI | relativeURI ] [ "#" fragment ]

absoluteURI   = scheme ":"( hier_part | opaque_part )

relativeURI   = ( net_path |abs_path | rel_path ) [ "?" query ]

hier_part     = ( net_path |abs_path ) [ "?" query ]

net_path      ="//" authority [ abs_path ]

abs_path     ="/" path_segments

rel_path       = [path ] [ ";" params ] [ "?" query ]

authority     =server | reg_name

host          =hostname | IPv4address | IPv6reference

一些名词解释:

reg_name指服务器注册的域名,如www.baidu.com

hier_part是指需要有分隔符对不同等级的组件进行分割

opaque_part指不需要有分隔符对不同组件进行分割

例如:完整的URL地址

http://userName:password@www.testpage.com/otengyue/article/search?ie=utf8&oe=utf8

URI-reference: http://userName:password@www.testpage.com/otengyue/article/search?ie=utf8&oe=utf8

Path: /otengyue/article/search

Host:       www.testpage.com

Authority: userName:password@www.testpage.com

abs_path:   /otengyue/article/search

net_path:   //userName:password@www.testpage.com/otengyue/article/search

更多资料可查阅

URL 规范 (RFC1738) http://tools.ietf.org/html/rfc1738

介绍 RFC1738( 中文 ) 博文: http://blog.csdn.net/msgsnd/article/details/2172306

HttpClient 中有关 URL 处理的类图如下:

其中 URI 类是对 URL 元数据的封装。 URI 中元数据的存储默认采用给定的编码类型编码后存储,若要改变需要实例化时设置不编码实例。

(1)URI 中元数据的默认编码方式为 "UTF-8" ,若要更改默认的 charset 类型可以采用下面两种方法

<1> 永久更改。调用 URI 的静态函数 setDefaultProtocolCharset(Stringcharset) ( 不推荐 )

如下代码 ( 放在所有代码前面 )

	try {
	     URI.setDefaultProtocolCharset("gbk");
	} catch (DefaultCharsetChanged cc) {
	     // CASE 1: the exception could be ignored, when it is set by user
	     if (cc.getReasonCode() == DefaultCharsetChanged.PROTOCOL_CHARSET) {
	     // CASE 2: let user know the default protocol charset changed
	     } else {
	     // CASE 2: let user know the default document charset changed

<2>临时更改。在构造函数中charset作为参数,只能更改当前实例化类的编码类型。

(2)URI元数据的设置/获取

getRaw*/getEscaped*  获得原始的URI对应元数据

get*                 获得对应的已解码得对应元数据

setRaw*/setEscaped*  设置URI对应元数据值(参数未经过编码直接设置)

set*                 设置URI对应元数据值(参数会经过编码后再设置)

(3)URI实例化

	boolean escaped=false;
	//使用绝对路径和选择是否编码为false实例化,注不设置则默认采用是否编码为true实例化,如下面集中实例化
	URI uri=new URI("http://www.baidu.com/search/s?ie=utf8&oe=utf8&wd=HttpClient&tn=98010089_dg&ch=1",escaped);
	//使用绝对路径
	URI uri1=new URI("http://www.baidu.com/search/s?ie=utf8&oe=utf8&wd=HttpClient&tn=98010089_dg&ch=1");
	//实例化时设置URI编码类型
	URI URI2=new URI("http://www.baidu.com","gbk");
	//使用相对路径,注不能忘记以"/"开头
	URI uri3=new URI("/ms?ie=utf8&oe=utf8&wd=HttpClient&tn=98010089_dg&ch=1");
	//使用baseURI和RelativeURI
	//uri4.getURI():<a target=_blank href="http://www.baidu.com/ms?ie=utf8&oe=utf8&wd=HttpClient&tn=98010089_dg&ch=1">http://www.baidu.com/ms?ie=utf8&oe=utf8&wd=HttpClient&tn=98010089_dg&ch=1</a>
	URI uri4=new URI(uri1,uri3);

(4)具体协议的使用

      针对具体的协议(http),则使用对应的URI的子类。例如针对Http协议的HttpUrl类封装了一系列的方法简化uri的操作 (HttpUrl是采用自动编码方式存储元数据)。另外HttpUrl无法调用无参构造函数(类型为protected)。下图为HttpUrl的构造函数图

四、编码问题

HttpClient处理字符编码大致包括URLHeader、和请求/响应体三个部分。

URL编码标准遵循RFC1738标准,标准为US-ASCII编码,但其不支持双字节,在HttpClientURI中采用UTF-8为默认的编码格式。HeaderContent-Type字段可能会包含字符编码信息。例如字符的设置:Content-Type: text/html; charset=UTF-8

GET的请求参数在QueryString中,是URI的一部分。因此,对于GET请求,关注于请求参数的中文编码。

POST的请求参数在Body中,因此,对于POST请求,关注Body的编码问题。

解决方案:

(1)GET请求参数编码

<1>设置URI编码类型

查看URI类代码,不设置编码则默认为UTF-8

* The charset of the protocol used by this URI instance. protected String protocolCharset = null; * The default charset of the protocol. RFC 2277, 2396 protected static String defaultProtocolCharset = "UTF-8";

需要设置编码时,在构造函数中设置编码类型

 public URI(char[] escaped, String charset) 
        throws URIException, NullPointerException {
        protocolCharset = charset;
        parseUriReference(new String(escaped), true);

<2>设置QueryString编码类型

一般中文编码问题只存在请求参数QueryString中,因此只需对请求参数设置编码即可。

queryString =EncodingUtil.formUrlEncode(params, "UTF-8"); //NameValuePair[] params

queryString = URIUtil.decode (params, "UTF-8"); //String params

(2)POST请求参数编码

主要是在Post请求头的Header中添加Content-Type: text/html; charset=UTF-8,

HttpMethodParams类中设置编码的源码如下(如果不设置则默认为ISO-8859-1)

* Sets the default charset to be used for writing content body, * when no charset explicitly specified. * @param charset The charset public void setContentCharset(String charset) { setParameter(HTTP_CONTENT_CHARSET, charset); * Returns the default charset to be used for writing content body, * when no charset explicitly specified. * @return The charset public String getContentCharset() { String charset = (String) getParameter(HTTP_CONTENT_CHARSET); if (charset == null) { LOG.warn("Default content charset not configured, using ISO-8859-1"); charset = "ISO-8859-1"; return charset;

添加方法如下:

<1>在POST请求中的Header中设置Content-Type

PostMethod method = new PostMethod();

method.addRequestHeader("Content-Type","text/html;charset=UTF-8");

<2>设置HttpClientParam的ContentCharset

HttpClient httpClient = new HttpClient();

HttpClientParam params =httpClient.getParams();

params.setContentCharset("UTF-8");

<3>设置HttpMethodParams的ContentCharse

PostMethod method = new PostMethod();

HttpMethodParams params = method.getParams();

params.setContentCharset("UTF-8");

(3) 请求/响应体中body编码

HttpMethodBase类源码中获得响应Body的源码如下

* Returns the response body of the HTTP method, if any, as a {@link String}. * If response body is not available or cannot be read, returns <tt>null</tt> * The string conversion on the data is done using the character encoding specified * in <tt>Content-Type</tt> header. Buffers the response and this method can be * called several times yielding the same result each time. * Note: This will cause the entire response body to be buffered in memory. A * malicious server may easily exhaust all the VM memory. It is strongly * recommended, to use getResponseAsStream if the content length of the response * is unknown or resonably large. * @return The response body or <code>null</code>. * @throws IOException If an I/O (transport) problem occurs while obtaining the * response body. public String getResponseBodyAsString() throws IOException { byte[] rawdata = null; if (responseAvailable()) { rawdata = getResponseBody(); if (rawdata != null) { return EncodingUtil.getString(rawdata, getResponseCharSet()); } else { return null; * Returns the character encoding of the request from the <tt>Content-Type</tt> header. * @return String The character set. public String getRequestCharSet() { return getContentCharSet(getRequestHeader("Content-Type"));

可见,getResponseCharSet方法Content-Type Header获取响应数据的charset。这要求Servlet必须正确设置responseContent-Type Header

通过查看源码可以看到在获得Response后,HttpClient识别编码的顺序是:http头信息的charset,如果头信息中没有charset,则查找HttpClientParamscontentCharset,如果没有指定编码,则是ISO-8859-1。其源码可在HttpMethodBase中查看

* Returns the character encoding of the request from the <tt>Content-Type</tt> header. * @return String The character set. public String getRequestCharSet() { return getContentCharSet(getRequestHeader("Content-Type")); * Returns the character set from the <tt>Content-Type</tt> header. * @param contentheader The content header. * @return String The character set. protected String getContentCharSet(Header contentheader) { LOG.trace("enter getContentCharSet( Header contentheader )"); String charset = null; if (contentheader != null) { HeaderElement values[] = contentheader.getElements(); // I expect only one header element to be there // No more. no less if (values.length == 1) { NameValuePair param = values[0].getParameterByName("charset"); if (param != null) { // If I get anything "funny" // UnsupportedEncondingException will result charset = param.getValue(); if (charset == null) { charset = getParams().getContentCharset(); if (LOG.isDebugEnabled()) { LOG.debug("Default charset used: " + charset); return charset;

因此,在解决响应头中没有Content-Type:text/html; charset=utf-8而引起乱码时,可以通过设置发送请求头时设置

httpClient.getParams().setContentCharset("gbk");

一、URL简介标准的URL格式:协议模式://主机名:可选的端口/资源路径?可选查询#可选的片段即:Scheme://authority:port/path?query#fragment完整的URL格式:协议模式://用户名:密码@主机名:可选的端口/资源路径?可选查询#可选的片段即:Scheme://userid:password@authority:port/path?