如何在Java中发送HTTP请求?

443
在Java中,如何组合HTTP请求消息并将其发送到HTTP Web服务器?

http://java.sun.com/javase/6/docs/api/java/net/HttpURLConnection.html 特别是,getHeaderField、getHeaderFieldKey 和 getContent。 - Federico klez Culloca
33
这里有一个小教程在SO(Stack Overflow)上:https://dev59.com/vHE85IYBdhLWcg3wZyqt。 - BalusC
你可以使用JSoup库(http://jsoup.org)。它完全符合您的要求! Document doc = Jsoup.connect("http://en.wikipedia.org").get(); (来自该网站)。 这是一种更Pythonic的Java方式。 - user2007447
10个回答

336
你可以使用 java.net.HttpUrlConnection。示例(来自此处),带有改进。在链接失效的情况下包含此内容。
public static String executePost(String targetURL, String urlParameters) {
  HttpURLConnection connection = null;

  try {
    //Create connection
    URL url = new URL(targetURL);
    connection = (HttpURLConnection) url.openConnection();
    connection.setRequestMethod("POST");
    connection.setRequestProperty("Content-Type", 
        "application/x-www-form-urlencoded");

    connection.setRequestProperty("Content-Length", 
        Integer.toString(urlParameters.getBytes().length));
    connection.setRequestProperty("Content-Language", "en-US");  

    connection.setUseCaches(false);
    connection.setDoOutput(true);

    //Send request
    DataOutputStream wr = new DataOutputStream (
        connection.getOutputStream());
    wr.writeBytes(urlParameters);
    wr.close();

    //Get Response  
    InputStream is = connection.getInputStream();
    BufferedReader rd = new BufferedReader(new InputStreamReader(is));
    StringBuilder response = new StringBuilder(); // or StringBuffer if Java version 5+
    String line;
    while ((line = rd.readLine()) != null) {
      response.append(line);
      response.append('\r');
    }
    rd.close();
    return response.toString();
  } catch (Exception e) {
    e.printStackTrace();
    return null;
  } finally {
    if (connection != null) {
      connection.disconnect();
    }
  }
}

2
这是另一个不错的代码片段,可以替代Java Almanac:HttpUrlConnection-Example - GreenTurtle
25
在这个回答中加入一些实际的代码将有助于避免链接失效... - Cypher
3
自Java 9以来,创建HTTP请求变得更加容易。 - Anton Sorokin
是的,自从这个答案发布以来十年间发生了很多变化。并不是每个人都已经从JDK8转移到了9及以上版本。 - duffymo
请问如何通过这种方式向请求添加一些头部内容? - nonozor

247

来自Oracle的Java教程

import java.net.*;
import java.io.*;

public class URLConnectionReader {
    public static void main(String[] args) throws Exception {
        URL yahoo = new URL("http://www.yahoo.com/");
        URLConnection yc = yahoo.openConnection();
        BufferedReader in = new BufferedReader(
                                new InputStreamReader(
                                yc.getInputStream()));
        String inputLine;

        while ((inputLine = in.readLine()) != null) 
            System.out.println(inputLine);
        in.close();
    }
}

1
@Gorky:提一个新问题。 - Janus Troelsen
101
这里的线路噪音太多了,我认为不适合发送HTTP请求。与Python的requests库相比:response = requests.get('http://www.yahoo.com/');在Java中应该也可以实现类似简洁的代码。 - Dan Passaro
24
@leo-the-manic,这是因为Java被认为是一种比Python更低级别的语言,它允许(甚至强制)程序员处理底层细节,而不是假设“合理”的默认值(例如缓冲,字符编码等)。虽然有可能编写简洁的代码,但这样做会失去更基础方法的灵活性。 - fortran
14
Python有同样低级的选项来完成与上述相同的事情。 - User
11
那是因为Java被认为是一种较低级别的语言。X'D - hoodaticus
显示剩余6条评论

73

我知道其他人会推荐使用Apache的http-client,但它增加了复杂性(即更容易出现错误),而这很少是必要的。对于简单的任务,java.net.URL足够了。

URL url = new URL("http://www.y.com/url");
InputStream is = url.openStream();
try {
  /* Now read the retrieved document from the stream. */
  ...
} finally {
  is.close();
}

6
如果你想要更改请求头,这并没有什么帮助。在处理那些只对流行浏览器做出特定响应的网站时,更改请求头尤其有用。 - Jherico
43
使用URLConnection可以更改请求头,但根据提问者的问题,他并没有要求这个;从问题来看,简单的答案很重要。 - erickson

57

Apache HttpComponents。这两个模块的示例 - HttpCoreHttpClient 可以让您立即开始。

并不是说HttpUrlConnection是一个不好的选择,HttpComponents将抽象掉很多繁琐的编码。如果您真的想通过最少的代码支持许多HTTP服务器/客户端,我会推荐使用它。顺便说一下,HttpCore可用于拥有最少功能的应用程序(客户端或服务器),而HttpClient则用于需要支持多种身份验证方案、cookie支持等功能的客户端。


3
我们的代码最初是使用java.net.HttpURLConnection编写的,但当我们不得不添加SSL并解决一些奇怪的用例时,在我们内部有问题的网络中工作变得非常棘手。Apache HttpComponents挽救了这个局面。目前我们的项目仍然使用一个丑陋的混合方案,其中包含一些不可靠的适配器将java.net.URL转换为HttpComponents使用的URI。我经常对其进行重构。唯一一次HttpComponents代码变得显着复杂的场景是从标头中解析日期。但是,解决方案仍然很简单。 - Michael Scheper
1
在这里添加一段代码片段会很有帮助。 - Vic Seedoubleyew

30

这里是一个完整的 Java 7 程序:

class GETHTTPResource {
  public static void main(String[] args) throws Exception {
    try (java.util.Scanner s = new java.util.Scanner(new java.net.URL("http://example.com/").openStream())) {
      System.out.println(s.useDelimiter("\\A").next());
    }
  }
}
新的 try-with-resources 会自动关闭 Scanner,进而自动关闭 InputStream。

@Ska 没有未处理的异常。main() 抛出 Exception,其中包括 MalformedURLException 和 IOException。 - jerzy
扫描器在性能方面实际上并不是非常优化。 - WesternGun

16

谷歌Java HTTP客户端拥有良好的API,可以进行HTTP请求。你可以轻松地添加JSON支持等等。尽管对于简单的请求来说可能会过于繁琐。

import com.google.api.client.http.GenericUrl;
import com.google.api.client.http.HttpRequest;
import com.google.api.client.http.HttpResponse;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.http.javanet.NetHttpTransport;
import java.io.IOException;
import java.io.InputStream;

public class Network {

    static final HttpTransport HTTP_TRANSPORT = new NetHttpTransport();

    public void getRequest(String reqUrl) throws IOException {
        GenericUrl url = new GenericUrl(reqUrl);
        HttpRequest request = HTTP_TRANSPORT.createRequestFactory().buildGetRequest(url);
        HttpResponse response = request.execute();
        System.out.println(response.getStatusCode());

        InputStream is = response.getContent();
        int ch;
        while ((ch = is.read()) != -1) {
            System.out.print((char) ch);
        }
        response.disconnect();
    }
}

“transport”是什么意思? - Thilo
抱歉,应该是 HTTP_TRANSPORT,我已经编辑了答案。 - Tombart
为什么HttpResponse不是AutoClosable的?与使用Apache的CloseableHttpClient有什么区别? - Janus Troelsen
好处在于API,这使得它成为个人偏好。谷歌的库在内部使用Apache的库。话虽如此,我喜欢谷歌的库。 - Jeff Fairley
无论是简单的请求还是复杂的请求,都没有关系,可读性才是最重要的。 - Johan

15

这会对你有所帮助。别忘了将HttpClient.jar JAR包添加到类路径中。

import java.io.FileOutputStream;
import java.io.IOException;

import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.NameValuePair;
import org.apache.commons.httpclient.methods.PostMethod;

public class MainSendRequest {

     static String url =
         "http://localhost:8080/HttpRequestSample/RequestSend.jsp";

    public static void main(String[] args) {

        //Instantiate an HttpClient
        HttpClient client = new HttpClient();

        //Instantiate a GET HTTP method
        PostMethod method = new PostMethod(url);
        method.setRequestHeader("Content-type",
                "text/xml; charset=ISO-8859-1");

        //Define name-value pairs to set into the QueryString
        NameValuePair nvp1= new NameValuePair("firstName","fname");
        NameValuePair nvp2= new NameValuePair("lastName","lname");
        NameValuePair nvp3= new NameValuePair("email","email@email.com");

        method.setQueryString(new NameValuePair[]{nvp1,nvp2,nvp3});

        try{
            int statusCode = client.executeMethod(method);

            System.out.println("Status Code = "+statusCode);
            System.out.println("QueryString>>> "+method.getQueryString());
            System.out.println("Status Text>>>"
                  +HttpStatus.getStatusText(statusCode));

            //Get data as a String
            System.out.println(method.getResponseBodyAsString());

            //OR as a byte array
            byte [] res  = method.getResponseBody();

            //write to file
            FileOutputStream fos= new FileOutputStream("donepage.html");
            fos.write(res);

            //release connection
            method.releaseConnection();
        }
        catch(IOException e) {
            e.printStackTrace();
        }
    }
}

1
说真的,我真的很喜欢Java,但是那个愚蠢的NameValuePair列表或数组有什么问题呢?为什么不用一个简单的Map<String, String>呢?对于这样简单的用例来说,有太多样板代码了... - Joffrey
6
定义上来说,映射表中每个值都对应唯一的一个键,也就是说,“一个映射表中不能包含重复的键”!但是HTTP参数可以具有重复的键。 - Ben

14

您可以像这样使用Socket

String host = "www.yourhost.com";
Socket socket = new Socket(host, 80);
String request = "GET / HTTP/1.0\r\n\r\n";
OutputStream os = socket.getOutputStream();
os.write(request.getBytes());
os.flush();

InputStream is = socket.getInputStream();
int ch;
while( (ch=is.read())!= -1)
    System.out.print((char)ch);
socket.close();    

@laksys 为什么应该使用\r\n而不是\n - CuriousGuy
@CuriousGuy 看看这个链接 http://programmers.stackexchange.com/questions/29075/difference-between-n-and-r-n - laksys
3
这个解决方案似乎比其他方案更容易和直接。Java让事情变得比必要的更加复杂。 - SwiftMango

7

这里有一个非常好的关于发送POST请求的链接,由Example Depot提供:点击这里

try {
    // Construct data
    String data = URLEncoder.encode("key1", "UTF-8") + "=" + URLEncoder.encode("value1", "UTF-8");
    data += "&" + URLEncoder.encode("key2", "UTF-8") + "=" + URLEncoder.encode("value2", "UTF-8");

    // Send data
    URL url = new URL("http://hostname:80/cgi");
    URLConnection conn = url.openConnection();
    conn.setDoOutput(true);
    OutputStreamWriter wr = new OutputStreamWriter(conn.getOutputStream());
    wr.write(data);
    wr.flush();

    // Get the response
    BufferedReader rd = new BufferedReader(new InputStreamReader(conn.getInputStream()));
    String line;
    while ((line = rd.readLine()) != null) {
        // Process line...
    }
    wr.close();
    rd.close();
} catch (Exception e) {
}

如果您想发送GET请求,您可以稍微修改代码以满足您的需求。具体来说,您必须在URL的构造函数中添加参数。然后,还要注释掉这个wr.write(data);
有一件事没有写出来,您应该注意超时。特别是如果您想在WebServices中使用它,您必须设置超时时间,否则上面的代码将无限期地等待或至少很长时间,这显然不是您想要的。
超时时间设置如下:conn.setReadTimeout(2000);,输入参数以毫秒为单位。

7

如果您使用的是Java 11或更新版本(Android除外),则可以使用Java 11新的HTTP Client API,而不是传统的HttpUrlConnection类。

一个GET请求的示例:

var uri = URI.create("https://httpbin.org/get?age=26&isHappy=true");
var client = HttpClient.newHttpClient();
var request = HttpRequest
        .newBuilder()
        .uri(uri)
        .header("accept", "application/json")
        .GET()
        .build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.statusCode());
System.out.println(response.body());

异步执行相同的请求:

var responseAsync = client
        .sendAsync(request, HttpResponse.BodyHandlers.ofString())
        .thenApply(HttpResponse::body)
        .thenAccept(System.out::println);
// responseAsync.join(); // Wait for completion

一个示例POST请求:

var request = HttpRequest
        .newBuilder()
        .uri(uri)
        .version(HttpClient.Version.HTTP_2)
        .timeout(Duration.ofMinutes(1))
        .header("Content-Type", "application/json")
        .header("Authorization", "Bearer fake")
        .POST(BodyPublishers.ofString("{ title: 'This is cool' }"))
        .build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());

如果要以multipart (multipart/form-data)或者url-encoded (application/x-www-form-urlencoded)格式发送表单数据,请参考这个解决方案

有关HTTP Client API的示例和更多信息,请参见此文章

如需Java标准库HTTP 服务器,请参阅此帖子


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接