All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.xiaoleilu.hutool.HttpUtil Maven / Gradle / Ivy

There is a newer version: 2.7.0
Show newest version
package com.xiaoleilu.hutool;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.regex.Pattern;

import javax.servlet.http.HttpServletRequest;

import com.xiaoleilu.hutool.exceptions.UtilException;

/**
 * Http请求工具类
 * 
 * @author xiaoleilu
 */
public class HttpUtil {

	/** 未知的标识 */
	public final static String UNKNOW = "unknown";

	public final static Pattern CHARSET_PATTERN = Pattern.compile("charset=(.*?)\"");

	private static Map cookies = new HashMap();

	/**
	 * 编码字符为 application/x-www-form-urlencoded
	 * 
	 * @param content 被编码内容
	 * @return 编码后的字符
	 */
	public static String encode(String content, String charset) {
		if (StrUtil.isBlank(content)) return content;

		String encodeContent = null;
		try {
			encodeContent = URLEncoder.encode(content, charset);
		} catch (UnsupportedEncodingException e) {
			throw new UtilException(StrUtil.format("Unsupported encoding: [{}]", charset), e);
		}
		return encodeContent;
	}

	/**
	 * 解码application/x-www-form-urlencoded字符
	 * 
	 * @param content 被编码内容
	 * @return 编码后的字符
	 */
	public static String decode(String content, String charset) {
		if (StrUtil.isBlank(content)) return content;
		String encodeContnt = null;
		try {
			encodeContnt = URLDecoder.decode(content, charset);
		} catch (UnsupportedEncodingException e) {
			throw new UtilException(StrUtil.format("Unsupported encoding: [{}]", charset), e);
		}
		return encodeContnt;
	}

	/**
	 * 获取客户端IP
	 * 
	 * @param request 请求对象
	 * @return IP地址
	 */
	public static String getClientIP(HttpServletRequest request) {
		String ip = request.getHeader("X-Forwarded-For");
		if (isUnknow(ip)) {
			ip = request.getHeader("Proxy-Client-IP");
		}
		if (isUnknow(ip)) {
			ip = request.getHeader("WL-Proxy-Client-IP");
		}
		if (isUnknow(ip)) {
			ip = request.getHeader("X-Real-IP");
		}
		if (isUnknow(ip)) {
			ip = request.getRemoteAddr();
		}
		// 多级反向代理检测
		if (ip != null && ip.indexOf(",") > 0) {
			ip = ip.trim().split(",")[0];
		}
		return ip;
	}
	
	/**
	 * 请求
	 * @param method 方法
	 * @param urlString URL
	 * @param customCharset 自定义的编码
	 * @param isPassCodeError 是否跳过非200异常
	 * @param headers 请求头
	 * @return 返回内容
	 * @throws IOException
	 */
	public static String request(String method, String urlString, String customCharset, boolean isPassCodeError, Map headers) throws IOException {
		final URL url = new URL(urlString);
		final String host = url.getHost();
		HttpURLConnection conn = (HttpURLConnection) url.openConnection();
		
		//Add headers
		for (Entry entry : headers.entrySet()) {
			conn.addRequestProperty(entry.getKey(), StrUtil.str(entry.getValue()));
		}
		
		//Add Cookies
		final String cookie = cookies.get(host);
		if (cookie != null) conn.addRequestProperty("Cookie", cookie);
		
		conn.setRequestMethod(method);
		
		if (conn.getResponseCode() != 200) {
			if (!isPassCodeError) {
				throw new IOException("Status code not 200!");
			}
		}

		//Get Cookies
		final String setCookie = conn.getHeaderField("Set-Cookie");
		if (StrUtil.isBlank(setCookie) == false) {
			Log.debug("Set cookie: [{}]", setCookie);
			cookies.put(host, setCookie);
		}

		/* 获取内容 */
		String charset = getCharsetFromConn(conn);
		boolean isGetCharsetFromContent = false;
		if (StrUtil.isBlank(charset)) {
			charset = customCharset;
			isGetCharsetFromContent = true;
		}
		String content = getString(conn.getInputStream(), charset, isGetCharsetFromContent);
		conn.disconnect();

		return content;
	}

	/**
	 * 发送get请求
	 * 
	 * @param urlString 网址
	 * @param customCharset 自定义请求字符集,如果字符集获取不到,使用此字符集
	 * @param isPassCodeError 是否跳过非200异常
	 * @return 返回内容,如果只检查状态码,正常只返回 "",不正常返回 null
	 * @throws IOException
	 */
	public static String get(String urlString, String customCharset, boolean isPassCodeError) throws IOException {
		HashMap headers = new HashMap();
		headers.put("User-Agent", "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/21.0.1180.83 Safari/537.1");
		
		return request("GET", urlString, customCharset, isPassCodeError, headers);
	}

	/**
	 * 发送post请求
	 * 
	 * @param urlString 网址
	 * @param paramMap post表单数据
	 * @param customCharset 自定义请求字符集,发送时使用此字符集,获取返回内容如果字符集获取不到,使用此字符集
	 * @param isPassCodeError 是否跳过非200异常
	 * @return 返回数据
	 * @throws IOException
	 */
	public static String post(String urlString, Map paramMap, String customCharset, boolean isPassCodeError) throws IOException {
		return post(urlString, toParams(paramMap), customCharset, isPassCodeError);
	}

	/**
	 * 发送post请求
	 * 
	 * @param urlString 网址
	 * @param params post表单数据
	 * @param customCharset 自定义请求字符集,发送时使用此字符集,获取返回内容如果字符集获取不到,使用此字符集
	 * @param isPassCodeError 是否跳过非200异常
	 * @return 返回数据
	 * @throws IOException
	 */
	public static String post(String urlString, String params, String customCharset, boolean isPassCodeError) throws IOException {
		URL url = new URL(urlString);
		HttpURLConnection conn = (HttpURLConnection) url.openConnection();

		conn.setRequestMethod("POST");
		conn.setDoOutput(true);
		conn.setDoInput(true);
		conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");

		IoUtil.write(conn.getOutputStream(), customCharset, true, params);

		if (conn.getResponseCode() != 200) {
			if (!isPassCodeError) {
				throw new IOException("Status code not 200!");
			}
		}

		/* 获取内容 */
		String charset = getCharsetFromConn(conn);
		String content = IoUtil.getString(conn.getInputStream(), StrUtil.isBlank(charset) ? customCharset : charset);
		conn.disconnect();

		return content;
	}

	/**
	 * 获得远程String
	 * 
	 * @param url 请求的url
	 * @param customCharset 自定义的字符集
	 * @return 文本
	 * @throws IOException
	 */
	public static String downloadString(String url, String customCharset) throws IOException {
		InputStream inputStream = new URL(url).openStream();
		return IoUtil.getString(inputStream, customCharset);
	}

	/**
	 * 将Map形式的Form表单数据转换为Url参数形式
	 * 
	 * @param paramMap 表单数据
	 * @return url参数
	 */
	public static String toParams(Map paramMap) {
		return CollectionUtil.join(paramMap.entrySet(), "&");
	}

	/**
	 * 将URL参数解析为Map(也可以解析Post中的键值对参数)
	 * @param paramsStr 参数字符串(或者带参数的Path)
	 * @param charset 字符集
	 * @return 参数Map
	 */
	public static Map> decodeParams(String paramsStr, String charset) {
		if (StrUtil.isBlank(paramsStr)) {
			return Collections.emptyMap();
		}

		// 去掉Path部分
		int pathEndPos = paramsStr.indexOf('?');
		if (pathEndPos > 0) {
			paramsStr = StrUtil.subSuf(paramsStr, pathEndPos + 1);
		}
		paramsStr = decode(paramsStr, charset);

		final Map> params = new LinkedHashMap>();
		String name = null;
		int pos = 0; // 未处理字符开始位置
		int i; // 未处理字符结束位置
		char c; // 当前字符
		for (i = 0; i < paramsStr.length(); i++) {
			c = paramsStr.charAt(i);
			if (c == '=' && name == null) { // 键值对的分界点
				if (pos != i) {
					name = paramsStr.substring(pos, i);
				}
				pos = i + 1;
			} else if (c == '&' || c == ';') { // 参数对的分界点
				if (name == null && pos != i) {
					// 对于像&a&这类无参数值的字符串,我们将name为a的值设为""
					addParam(params, paramsStr.substring(pos, i), StrUtil.EMPTY);
				} else if (name != null) {
					addParam(params, name, paramsStr.substring(pos, i));
					name = null;
				}
				pos = i + 1;
			}
		}

		if (pos != i) {
			if (name == null) {
				addParam(params, paramsStr.substring(pos, i), StrUtil.EMPTY);
			} else {
				addParam(params, name, paramsStr.substring(pos, i));
			}
		} else if (name != null) {
			addParam(params, name, StrUtil.EMPTY);
		}

		return params;
	}

	// ----------------------------------------------------------------------------------------- Private method start
	/**
	 * 从Http连接的头信息中获得字符集
	 * 
	 * @param conn HTTP连接对象
	 * @return 字符集
	 */
	private static String getCharsetFromConn(HttpURLConnection conn) {
		String charset = conn.getContentEncoding();
		if (charset == null || "".equals(charset.trim())) {
			String contentType = conn.getContentType();
			charset = ReUtil.get("charset=(.*)", contentType, 1);
		}
		return charset;
	}

	/**
	 * 检测给定字符串是否为未知,多用于检测HTTP请求相关
* * @param checkString 被检测的字符串 * @return 是否未知 */ private static boolean isUnknow(String checkString) { return StrUtil.isBlank(checkString) || UNKNOW.equalsIgnoreCase(checkString); } /** * 从流中读取内容 * * @param in 输入流 * @param charset 字符集 * @return 内容 * @throws IOException */ private static String getString(InputStream in, String charset, boolean isGetCharsetFromContent) throws IOException { StringBuilder content = new StringBuilder(); // 存储返回的内容 // 从返回的内容中读取所需内容 BufferedReader reader = new BufferedReader(new InputStreamReader(in, charset)); String line = null; while ((line = reader.readLine()) != null) { content.append(line).append('\n'); if (isGetCharsetFromContent) { String charsetInContent = ReUtil.get(CHARSET_PATTERN, line, 1); if (StrUtil.isBlank(charsetInContent) == false) { charset = charsetInContent; reader = new BufferedReader(new InputStreamReader(in, charset)); isGetCharsetFromContent = true; } } } return content.toString(); } /** * 将键值对加入到值为List类型的Map中 * * @param params 参数 * @param name key * @param value value * @return 是否成功 */ private static boolean addParam(Map> params, String name, String value) { List values = params.get(name); if (values == null) { values = new ArrayList(1); // 一般是一个参数 params.put(name, values); } values.add(value); return true; } // ----------------------------------------------------------------------------------------- Private method start end }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy