Öncelikle bir feragatname: yayınlanan kod parçacıklarının hepsi temel örneklerdir. Sen önemsiz işlemek gerekir IOException
s ve RuntimeException
benzeri s NullPointerException
, ArrayIndexOutOfBoundsException
ve eşlerinden kendin.
hazırlama
Önce en azından URL'yi ve karakter kümesini bilmemiz gerekir. Parametreler isteğe bağlıdır ve fonksiyonel gereksinimlere bağlıdır.
String url = "http://example.com";
String charset = "UTF-8"; // Or in Java 7 and later, use the constant: java.nio.charset.StandardCharsets.UTF_8.name()
String param1 = "value1";
String param2 = "value2";
// ...
String query = String.format("param1=%s¶m2=%s",
URLEncoder.encode(param1, charset),
URLEncoder.encode(param2, charset));
Sorgu parametreleri name=value
formatta olmalı ve ile birleştirilmelidir &
. Ayrıca normalde kullanarak sorgu parametrelerini belirtilen karakter kümesiyle URL olarak kodlarsınız URLEncoder#encode()
.
String#format()
Sadece kolaylık içindir. Ben +
iki kez daha fazla String birleştirme operatörü gerektiğinde tercih ederim .
Bir Atış HTTP GET (isteğe bağlı olarak) sorgu parametreleriyle isteği
Bu önemsiz bir görev. Varsayılan istek yöntemidir.
URLConnection connection = new URL(url + "?" + query).openConnection();
connection.setRequestProperty("Accept-Charset", charset);
InputStream response = connection.getInputStream();
// ...
Herhangi bir sorgu dizesi kullanılarak URL ile birleştirilmelidir ?
. Accept-Charset
Eğer herhangi bir sorgu dizesi göndermezseniz başlığı. İçindedir parametreleri kodlama neyi sunucuyu ipucu olabilir o zaman bırakabilirsiniz Accept-Charset
uzakta başlığı. Üstbilgi ayarlamanız gerekmiyorsa, URL#openStream()
kısayol yöntemini bile kullanabilirsiniz .
InputStream response = new URL(url).openStream();
// ...
Her iki durumda da, diğer taraf a ise HttpServlet
, doGet()
yöntemi çağrılır ve parametreler tarafından kullanılabilir HttpServletRequest#getParameter()
.
Test amacıyla, yanıt gövdesini stdout'a aşağıdaki gibi yazdırabilirsiniz:
try (Scanner scanner = new Scanner(response)) {
String responseBody = scanner.useDelimiter("\\A").next();
System.out.println(responseBody);
}
Sorgu parametreleriyle bir HTTP POST isteği tetikleme
Ayarlama URLConnection#setDoOutput()
için true
örtülü POST için istek yöntemini ayarlar. Web formları gibi standart HTTP POST application/x-www-form-urlencoded
, sorgu dizesinin istek gövdesine yazıldığı türdedir.
URLConnection connection = new URL(url).openConnection();
connection.setDoOutput(true); // Triggers POST.
connection.setRequestProperty("Accept-Charset", charset);
connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded;charset=" + charset);
try (OutputStream output = connection.getOutputStream()) {
output.write(query.getBytes(charset));
}
InputStream response = connection.getInputStream();
// ...
Not: Programlı olarak bir HTML formu göndermek istediğinizde name=value
, herhangi bir <input type="hidden">
öğenin çiftini sorgu dizesine ve elbette programlı olarak "basmak" istediğiniz öğenin name=value
çiftini de almayı unutmayın <input type="submit">
(çünkü genellikle sunucu tarafında bir düğmeye basıldığını ve varsa hangisini) ayırt etmek için kullanılır.
Ayrıca elde edilen yayın yapabiliyorsa URLConnection
için HttpURLConnection
ve kullanımını HttpURLConnection#setRequestMethod()
yerine. Ancak bağlantıyı çıkış için kullanmaya çalışıyorsanız, ayarlamanız URLConnection#setDoOutput()
gerekir true
.
HttpURLConnection httpConnection = (HttpURLConnection) new URL(url).openConnection();
httpConnection.setRequestMethod("POST");
// ...
Her iki durumda da, diğer taraf a ise HttpServlet
, doPost()
yöntemi çağrılır ve parametreler tarafından kullanılabilir HttpServletRequest#getParameter()
.
Aslında HTTP isteğini tetikliyor
HTTP isteğini açıkça kullanarak tetikleyebilirsiniz URLConnection#connect()
, ancak HTTP yanıtıyla ilgili yanıt gövdesi gibi bilgileri almak istediğinizde, istek otomatik olarak istek üzerine tetiklenir URLConnection#getInputStream()
. Yukarıdaki örnekler tam olarak bunu yapar, bu nedenle connect()
çağrı aslında gereksizdir.
HTTP yanıt bilgilerini toplama
HTTP yanıt durumu :
HttpURLConnection
Burada bir yere ihtiyacın var. Gerekirse önce yayınlayın.
int status = httpConnection.getResponseCode();
HTTP yanıt başlıkları :
for (Entry<String, List<String>> header : connection.getHeaderFields().entrySet()) {
System.out.println(header.getKey() + "=" + header.getValue());
}
HTTP yanıt kodlaması :
Bir parametre Content-Type
içerdiğinde charset
, yanıt gövdesi büyük olasılıkla metin tabanlıdır ve yanıt gövdesini sunucu tarafında belirtilen karakter kodlamasıyla işlemek istiyoruz.
String contentType = connection.getHeaderField("Content-Type");
String charset = null;
for (String param : contentType.replace(" ", "").split(";")) {
if (param.startsWith("charset=")) {
charset = param.split("=", 2)[1];
break;
}
}
if (charset != null) {
try (BufferedReader reader = new BufferedReader(new InputStreamReader(response, charset))) {
for (String line; (line = reader.readLine()) != null;) {
// ... System.out.println(line) ?
}
}
} else {
// It's likely binary content, use InputStream/OutputStream.
}
Oturumun sürdürülmesi
Sunucu tarafı oturumu genellikle bir çerez tarafından desteklenir. Bazı web formları, oturum açmanızı ve / veya bir oturum tarafından izlenmenizi gerektirir. CookieHandler
Çerezleri korumak için API'yı kullanabilirsiniz . Bir hazırlamak için gereken CookieManager
bir ile CookiePolicy
ait ACCEPT_ALL
tüm HTTP isteklerini göndermeden önce.
// First set the default cookie manager.
CookieHandler.setDefault(new CookieManager(null, CookiePolicy.ACCEPT_ALL));
// All the following subsequent URLConnections will use the same cookie manager.
URLConnection connection = new URL(url).openConnection();
// ...
connection = new URL(url).openConnection();
// ...
connection = new URL(url).openConnection();
// ...
Bunun her koşulda her zaman düzgün çalışmadığı biliniyor. Sizin için başarısız olursa, en iyisi çerez başlıklarını manuel olarak toplamak ve ayarlamaktır. Temel Set-Cookie
olarak, oturum açma veya ilk GET
isteğin yanıtından tüm başlıkları almanız ve bunu sonraki isteklerden geçirmeniz gerekir.
// Gather all cookies on the first request.
URLConnection connection = new URL(url).openConnection();
List<String> cookies = connection.getHeaderFields().get("Set-Cookie");
// ...
// Then use the same cookies on all subsequent requests.
connection = new URL(url).openConnection();
for (String cookie : cookies) {
connection.addRequestProperty("Cookie", cookie.split(";", 2)[0]);
}
// ...
split(";", 2)[0]
Gibi sunucu tarafı için alakasız çerez niteliklerin orada kurtulmak için olduğu expires
, path
vb Alternatif olarak, kullanabilirsiniz cookie.substring(0, cookie.indexOf(';'))
yerine split()
.
Akış modu
HttpURLConnection
Varsayılan olarak irade tampon tüm aslında kullanıyor olursanız sabit içerik uzunluk kendinizi ayarladığınız bakılmaksızın, göndermeden önce istek gövdesini connection.setRequestProperty("Content-Length", contentLength);
. OutOfMemoryException
Eşzamanlı olarak büyük POST istekleri (örneğin dosya yükleme) gönderdiğinizde bu durum neden olabilir . Bundan kaçınmak için HttpURLConnection#setFixedLengthStreamingMode()
,.
httpConnection.setFixedLengthStreamingMode(contentLength);
Ancak içerik uzunluğu önceden önceden bilinmiyorsa, HttpURLConnection#setChunkedStreamingMode()
buna göre ayarlayarak yığınlanmış akış modundan yararlanabilirsiniz . Bu , istek gövdesinin parçalar halinde gönderilmesini zorlayacak HTTP Transfer-Encoding
üstbilgisini ayarlar chunked
. Aşağıdaki örnek, gövdeyi 1 KB boyutlarında gönderecektir.
httpConnection.setChunkedStreamingMode(1024);
User-Agent
O gerçekleşebilir gerçek bir web tarayıcısı ile para cezası çalışırken, bir istek beklenmeyen bir yanıt verir . Sunucu tarafı büyük olasılıkla User-Agent
istek başlığına göre istekleri engelliyor . URLConnection
Varsayılan olarak irade olarak ayarlayın Java/1.6.0_19
son bölüm besbelli JRE sürümü olduğu. Bunu aşağıdaki gibi geçersiz kılabilirsiniz:
connection.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36"); // Do as if you're using Chrome 41 on Windows 7.
Son kullanılan bir tarayıcıdan User-Agent dizesini kullanın .
Hata yönetimi
HTTP yanıt kodu 4nn
(İstemci Hatası) veya 5nn
(Sunucu Hatası) HttpURLConnection#getErrorStream()
ise, sunucunun yararlı bir hata bilgisi gönderip göndermediğini görmek için simgesini okumak isteyebilirsiniz .
InputStream error = ((HttpURLConnection) connection).getErrorStream();
HTTP yanıt kodu -1 ise, bağlantı ve yanıt işleme ile ilgili bir sorun oluştu. HttpURLConnection
Uygulama eski JRE canlı bağlantıları tutmak ile biraz adamcağız. http.keepAlive
System özelliğini olarak ayarlayarak kapatmak isteyebilirsiniz false
. Bunu uygulamanızın başlangıcında programlı olarak şu şekilde yapabilirsiniz:
System.setProperty("http.keepAlive", "false");
Dosya yükleme
Normalde multipart/form-data
karışık POST içeriği (ikili ve karakter verileri) için kodlama kullanırsınız . Kodlama, RFC2388'de daha ayrıntılı olarak açıklanmaktadır .
String param = "value";
File textFile = new File("/path/to/file.txt");
File binaryFile = new File("/path/to/file.bin");
String boundary = Long.toHexString(System.currentTimeMillis()); // Just generate some unique random value.
String CRLF = "\r\n"; // Line separator required by multipart/form-data.
URLConnection connection = new URL(url).openConnection();
connection.setDoOutput(true);
connection.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary);
try (
OutputStream output = connection.getOutputStream();
PrintWriter writer = new PrintWriter(new OutputStreamWriter(output, charset), true);
) {
// Send normal param.
writer.append("--" + boundary).append(CRLF);
writer.append("Content-Disposition: form-data; name=\"param\"").append(CRLF);
writer.append("Content-Type: text/plain; charset=" + charset).append(CRLF);
writer.append(CRLF).append(param).append(CRLF).flush();
// Send text file.
writer.append("--" + boundary).append(CRLF);
writer.append("Content-Disposition: form-data; name=\"textFile\"; filename=\"" + textFile.getName() + "\"").append(CRLF);
writer.append("Content-Type: text/plain; charset=" + charset).append(CRLF); // Text file itself must be saved in this charset!
writer.append(CRLF).flush();
Files.copy(textFile.toPath(), output);
output.flush(); // Important before continuing with writer!
writer.append(CRLF).flush(); // CRLF is important! It indicates end of boundary.
// Send binary file.
writer.append("--" + boundary).append(CRLF);
writer.append("Content-Disposition: form-data; name=\"binaryFile\"; filename=\"" + binaryFile.getName() + "\"").append(CRLF);
writer.append("Content-Type: " + URLConnection.guessContentTypeFromName(binaryFile.getName())).append(CRLF);
writer.append("Content-Transfer-Encoding: binary").append(CRLF);
writer.append(CRLF).flush();
Files.copy(binaryFile.toPath(), output);
output.flush(); // Important before continuing with writer!
writer.append(CRLF).flush(); // CRLF is important! It indicates end of boundary.
// End of multipart/form-data.
writer.append("--" + boundary + "--").append(CRLF).flush();
}
Diğer taraf a ise HttpServlet
, doPost()
yöntemi çağrılır ve parçalar tarafından kullanılabilir HttpServletRequest#getPart()
(not, bu yüzden değil getParameter()
vb!). Ancak getPart()
yöntem nispeten yenidir, Servlet 3.0'da (Glassfish 3, Tomcat 7, vb.) Servlet 3.0'dan önce, en iyi seçiminiz bir isteği ayrıştırmak için Apache Commons FileUpload'u kullanmaktır multipart/form-data
. Ayrıca , FileUpload ve Servelt 3.0 yaklaşımlarının örnekleri için bu yanıta bakın .
Güvenilmeyen veya yanlış yapılandırılmış HTTPS siteleriyle ilgilenme
Bazen bir HTTPS URL'si bağlamanız gerekebilir, belki de bir web kazıyıcı yazdığınız için. Bu durumda, javax.net.ssl.SSLException: Not trusted server certificate
SSL sertifikalarını güncel tutmayan bazı HTTPS sitelerinde veya bir java.security.cert.CertificateException: No subject alternative DNS name matching [hostname] found
veya javax.net.ssl.SSLProtocolException: handshake alert: unrecognized_name
yanlış yapılandırılmış HTTPS sitelerinde bir ile karşılaşabilirsiniz.
static
Web kazıyıcı sınıfınızdaki aşağıdaki bir kerelik çalıştırıcı başlatıcısı, HttpsURLConnection
bu HTTPS siteleri için daha yumuşak olmalıdır ve bu nedenle artık bu istisnaları atmamalıdır .
static {
TrustManager[] trustAllCertificates = new TrustManager[] {
new X509TrustManager() {
@Override
public X509Certificate[] getAcceptedIssuers() {
return null; // Not relevant.
}
@Override
public void checkClientTrusted(X509Certificate[] certs, String authType) {
// Do nothing. Just allow them all.
}
@Override
public void checkServerTrusted(X509Certificate[] certs, String authType) {
// Do nothing. Just allow them all.
}
}
};
HostnameVerifier trustAllHostnames = new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
return true; // Just allow them all.
}
};
try {
System.setProperty("jsse.enableSNIExtension", "false");
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, trustAllCertificates, new SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
HttpsURLConnection.setDefaultHostnameVerifier(trustAllHostnames);
}
catch (GeneralSecurityException e) {
throw new ExceptionInInitializerError(e);
}
}
Son sözler
Apaçi HttpComponents HttpClient olduğunu çok daha uygun bu tümünde :)
HTML'yi ayrıştırma ve ayıklama
İstediğiniz tek şey HTML'den veri ayrıştırmak ve çıkarmaksa, Jsoup gibi bir HTML ayrıştırıcısını daha iyi kullanın