在这里经常使用java.net.URLConnection,Oracle tutorial对它来说过于简洁 .
该教程基本上只显示了如何触发GET请求并读取响应 . 它没有解释如何使用它来执行POST请求,设置请求标头,读取响应标头,处理cookie,提交HTML表单,上传文件等 .
那么,我如何使用 java.net.URLConnection
来触发和处理"advanced" HTTP请求?
在这里经常使用java.net.URLConnection,Oracle tutorial对它来说过于简洁 .
该教程基本上只显示了如何触发GET请求并读取响应 . 它没有解释如何使用它来执行POST请求,设置请求标头,读取响应标头,处理cookie,提交HTML表单,上传文件等 .
那么,我如何使用 java.net.URLConnection
来触发和处理"advanced" HTTP请求?
11 回答
受到关于SO的这个问题和其他问题的启发,我创建了一个最小的开源basic-http-client,它体现了这里发现的大部分技术 .
google-http-java-client也是一个很好的开源资源 .
我也对这种反应非常鼓舞 .
我经常在我需要做一些HTTP的项目上,我可能不想引入很多第三方依赖(带来其他依赖等等)
我开始根据这些对话开始编写我自己的实用程序(不是任何完成的地方):
然后只有一堆或静态方法 .
然后发布...
反正你懂这个意思....
以下是测试:
你可以在这里找到其余的:
https://github.com/RichardHightower/boon
我的目标是以更简单的方式提供人们想要做的常见事情....
我建议你看一下kevinsawicki/http-request上的代码,它基本上是
HttpUrlConnection
之上的一个包装器,它提供了一个更简单的API,以防你现在只想提出请求或者你可以看一下这些来源(它不是太多了)大)看看如何处理连接 .示例:使用内容类型
application/json
和一些查询参数发出GET
请求:首先是免责声明:发布的代码片段都是基本示例 . 您需要处理普通的IOExceptions和RuntimeExceptions,如NullPointerException,ArrayIndexOutOfBoundsException并自行配合 .
准备
我们首先需要至少知道URL和字符集 . 参数是可选的,取决于功能要求 .
查询参数必须为
name=value
格式,并由&
连接 . 您通常也会使用URLEncoder#encode()使用指定的字符集URL-encode查询参数 .String#format()只是为了方便起见 . 当我需要String连接运算符两次以上时,我更喜欢它 .
使用(可选)查询参数触发HTTP GET请求
这是一项微不足道的任务 . 这是默认的请求方法 .
应使用
?
将任何查询字符串连接到URL . Accept-Charset标头可能会提示服务器参数的编码方式 . 如果您不发送任何查询字符串,则可以将Accept-Charset
标头留下 . 如果您不需要设置任何 Headers ,那么您甚至可以使用URL#openStream()快捷方法 .无论哪种方式,如果另一方是HttpServlet,那么它的doGet()方法将被调用,参数将由HttpServletRequest#getParameter()提供 .
出于测试目的,您可以将响应主体打印到stdout,如下所示:
使用查询参数触发HTTP POST请求
将URLConnection#setDoOutput()设置为
true
隐式将请求方法设置为POST . Web表单的标准HTTP POST的类型为application/x-www-form-urlencoded
,其中查询字符串将写入请求正文 .注意:每当你忘记将
name=value
对任何<input type="hidden">
元素放入查询字符串中时,当然还有name=value
对<input type="submit">
元素,你通常在服务器端使用它来区分是否按下了一个按钮如果是的话,哪一个) .您也可以将获得的URLConnection强制转换为HttpURLConnection并使用其HttpURLConnection#setRequestMethod()代替 . 但是,如果您尝试使用连接进行输出,则仍需要将URLConnection#setDoOutput()设置为
true
.无论哪种方式,如果另一方是HttpServlet,那么它的doPost()方法将被调用,参数将由HttpServletRequest#getParameter()提供 .
实际触发HTTP请求
您可以使用URLConnection#connect()显式触发HTTP请求,但是当您想要获取有关HTTP响应的任何信息(例如使用URLConnection#getInputStream()的响应正文等)时,将根据需要自动触发请求 . 上面的例子就是这样,所以
connect()
调用实际上是多余的 .收集HTTP响应信息
你需要一个HttpURLConnection . 如有必要,先把它扔掉 .
当
Content-Type
包含charset
参数时,响应主体很可能是基于文本的,我们希望用服务器端指定的字符编码处理响应主体 .维护会话
服务器端会话通常由cookie支持 . 某些Web表单要求您已登录和/或由会话跟踪 . 您可以使用CookieHandler API来维护cookie . 在发送所有HTTP请求之前,您需要使用CookiePolicy ACCEPT_ALL准备CookieManager .
请注意,众所周知,这并不总是在所有情况下都能正常工作 . 如果它失败了,那么最好是手动收集和设置cookie头 . 您基本上需要从登录响应或第一个
GET
请求中获取所有Set-Cookie
标头,然后通过后续请求传递此标头 .split(";", 2)[0]
是为了摆脱与服务器端无关的cookie属性,如expires
,path
等 . 或者,您也可以使用cookie.substring(0, cookie.indexOf(';'))
而不是split()
.流媒体模式
无论您是否使用
connection.setRequestProperty("Content-Length", contentLength);
设置了固定的内容长度,HttpURLConnection默认会在实际发送之前缓冲整个请求正文 . 每当您同时发送大型POST请求(例如上传文件)时,这可能会导致OutOfMemoryException
. 为避免这种情况,您需要设置HttpURLConnection#setFixedLengthStreamingMode() .但是如果事先确实不知道内容长度,则可以通过相应地设置HttpURLConnection#setChunkedStreamingMode()来利用分块流模式 . 这会将HTTP Transfer-Encoding标头设置为
chunked
,这将强制请求正文以块的形式发送 . 以下示例将以1KB的块发送正文 .User-Agent
它可能发生在a request returns an unexpected response, while it works fine with a real web browser . 服务器端可能会根据User-Agent请求标头阻止请求 .
URLConnection
默认情况下会将其设置为Java/1.6.0_19
,其中最后一部分显然是JRE版本 . 您可以按如下方式覆盖:使用recent browser中的User-Agent字符串 .
错误处理
如果HTTP响应代码是
4nn
(客户端错误)或5nn
(服务器错误),那么您可能需要阅读HttpURLConnection#getErrorStream()
以查看服务器是否已发送任何有用的错误信息 .如果HTTP响应代码为-1,则连接和响应处理出现问题 .
HttpURLConnection
实现在较旧的JRE中有点错误,保持连接活着 . 您可能希望通过将http.keepAlive
系统属性设置为false
来将其关闭 . 您可以在应用程序的开头以编程方式执行此操作:上传文件
您通常使用multipart/form-data编码来处理混合POST内容(二进制和字符数据) . 编码在RFC2388中有更详细的描述 .
如果另一侧是HttpServlet,则将调用其doPost()方法,并且HttpServletRequest#getPart()可以使用这些部件(注意,因此 not
getParameter()
等等!) . 然而,getPart()
方法相对较新,它在Servlet 3.0(Glassfish 3,Tomcat 7等)中引入 . 在Servlet 3.0之前,您最好的选择是使用Apache Commons FileUpload来解析multipart/form-data
请求 . 有关FileUpload和Servelt 3.0方法的示例,另请参阅this answer .处理不受信任或配置错误的HTTPS站点
有时您需要连接HTTPS URL,可能是因为您正在编写Web scraper . 在这种情况下,您可能会在某些HTTPS站点上面临
javax.net.ssl.SSLException: Not trusted server certificate
,这些站点不保持SSL证书的最新状态,或者在某些配置错误的HTTPS站点上保留java.security.cert.CertificateException: No subject alternative DNS name matching [hostname] found
或javax.net.ssl.SSLProtocolException: handshake alert: unrecognized_name
.您的Web scraper类中的以下一次性运行
static
初始化程序应该使这些HTTPS站点的HttpsURLConnection
更宽松,因此不再抛出这些异常 .最后的话
Apache HttpComponents HttpClient在这方面更加方便:)
HttpClient Tutorial
HttpClient Examples
解析和提取HTML
如果您只想从HTML解析和提取数据,那么最好使用像Jsoup这样的HTML解析器
What are the pros/cons of leading HTML parsers in Java
How to scan and extract a webpage in Java
最初我被article误导了
HttpClient
.后来我意识到
HttpURLConnection
将会离开这个articleAs per the Google blog :
在阅读this article以及其他一些关于流动问题的堆栈之后,我确信
HttpURLConnection
将会持续更长时间 .一些有利于_193745的SE问题:
On Android, make a POST request with URL Encoded Form data without using UrlEncodedFormEntity
HttpPost works in Java project, not in Android
HTTP URL Hits有两个选项:GET / POST
GET请求: -
POST请求: -
您也可以使用JdkRequest来自jcabi-http(我是开发人员),它可以为您完成所有这些工作,装饰HttpURLConnection,触发HTTP请求和解析响应,例如:
查看此博客文章了解更多信息:http://www.yegor256.com/2014/04/11/jcabi-http-intro.html
什么时候使用HTTP几乎总是更有用的是引用
HttpURLConnection
而不是基类URLConnection
(因为URLConnection
是一个抽象类,当你在HTTP URL上请求URLConnection.openConnection()
时,无论如何都会回来) .然后你可以而不是依赖
URLConnection#setDoOutput(true)
隐式地将请求方法设置为POST而不是httpURLConnection.setRequestMethod("POST")
,有些人可能会发现更自然(并且还允许您指定其他请求方法,如PUT,DELETE,...) .它还提供有用的HTTP常量,因此您可以执行以下操作:
如果您使用http get请删除此行
更新
在Java 9中,您可以发送
GET
请求,例如:然后你可以检查返回的
HttpResponse
:由于这个新的HTTP客户端在java.httpclient
jdk.incubator.httpclient
模块中,因此您应该在module-info.java
文件中声明此依赖项:还有OkHttp,这是一个默认有效的HTTP客户端:
首先创建
OkHttpClient
的实例:然后,准备你的
GET
请求:最后,使用
OkHttpClient
发送准备好的Request
:有关详细信息,请参阅OkHttp's documentation