cn.hutool.http.server.HttpServerRequest Maven / Gradle / Ivy
package cn.hutool.http.server;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.io.LimitedInputStream;
import cn.hutool.core.map.CaseInsensitiveMap;
import cn.hutool.core.map.multi.ListValueMap;
import cn.hutool.core.net.NetUtil;
import cn.hutool.core.net.multipart.MultipartFormData;
import cn.hutool.core.net.multipart.UploadSetting;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.Header;
import cn.hutool.http.HttpUtil;
import cn.hutool.http.Method;
import cn.hutool.http.useragent.UserAgent;
import cn.hutool.http.useragent.UserAgentUtil;
import com.sun.net.httpserver.Headers;
import com.sun.net.httpserver.HttpExchange;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpCookie;
import java.net.URI;
import java.nio.charset.Charset;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
/**
* Http请求对象,对{@link HttpExchange}封装
*
* @author looly
* @since 5.2.6
*/
public class HttpServerRequest extends HttpServerBase {
private Map cookieCache;
private ListValueMap paramsCache;
private MultipartFormData multipartFormDataCache;
private Charset charsetCache;
private byte[] bodyCache;
/**
* 构造
*
* @param httpExchange {@link HttpExchange}
*/
public HttpServerRequest(HttpExchange httpExchange) {
super(httpExchange);
}
/**
* 获得Http Method
*
* @return Http Method
*/
public String getMethod() {
return this.httpExchange.getRequestMethod();
}
/**
* 是否为GET请求
*
* @return 是否为GET请求
*/
public boolean isGetMethod() {
return Method.GET.name().equalsIgnoreCase(getMethod());
}
/**
* 是否为POST请求
*
* @return 是否为POST请求
*/
public boolean isPostMethod() {
return Method.POST.name().equalsIgnoreCase(getMethod());
}
/**
* 获得请求URI
*
* @return 请求URI
*/
public URI getURI() {
return this.httpExchange.getRequestURI();
}
/**
* 获得请求路径Path
*
* @return 请求路径
*/
public String getPath() {
return getURI().getPath();
}
/**
* 获取请求参数
*
* @return 参数字符串
*/
public String getQuery() {
return getURI().getQuery();
}
/**
* 获得请求header中的信息
*
* @return header值
*/
public Headers getHeaders() {
return this.httpExchange.getRequestHeaders();
}
/**
* 获得请求header中的信息
*
* @param headerKey 头信息的KEY
* @return header值
*/
public String getHeader(Header headerKey) {
return getHeader(headerKey.toString());
}
/**
* 获得请求header中的信息
*
* @param headerKey 头信息的KEY
* @return header值
*/
public String getHeader(String headerKey) {
return getHeaders().getFirst(headerKey);
}
/**
* 获得请求header中的信息
*
* @param headerKey 头信息的KEY
* @param charset 字符集
* @return header值
*/
public String getHeader(String headerKey, Charset charset) {
final String header = getHeader(headerKey);
if (null != header) {
return CharsetUtil.convert(header, CharsetUtil.CHARSET_ISO_8859_1, charset);
}
return null;
}
/**
* 获取Content-Type头信息
*
* @return Content-Type头信息
*/
public String getContentType() {
return getHeader(Header.CONTENT_TYPE);
}
/**
* 获取编码,获取失败默认使用UTF-8,获取规则如下:
*
*
* 1、从Content-Type头中获取编码,类似于:text/html;charset=utf-8
*
*
* @return 编码,默认UTF-8
*/
public Charset getCharset() {
if(null == this.charsetCache){
final String contentType = getContentType();
final String charsetStr = HttpUtil.getCharset(contentType);
this.charsetCache = CharsetUtil.parse(charsetStr, DEFAULT_CHARSET);
}
return this.charsetCache;
}
/**
* 获得User-Agent
*
* @return User-Agent字符串
*/
public String getUserAgentStr() {
return getHeader(Header.USER_AGENT);
}
/**
* 获得User-Agent,未识别返回null
*
* @return User-Agent字符串,未识别返回null
*/
public UserAgent getUserAgent() {
return UserAgentUtil.parse(getUserAgentStr());
}
/**
* 获得Cookie信息字符串
*
* @return cookie字符串
*/
public String getCookiesStr() {
return getHeader(Header.COOKIE);
}
/**
* 获得Cookie信息列表
*
* @return Cookie信息列表
*/
public Collection getCookies() {
return getCookieMap().values();
}
/**
* 获得Cookie信息Map,键为Cookie名,值为HttpCookie对象
*
* @return Cookie信息Map
*/
public Map getCookieMap() {
if (null == this.cookieCache) {
cookieCache = Collections.unmodifiableMap(CollUtil.toMap(
NetUtil.parseCookies(getCookiesStr()),
new CaseInsensitiveMap<>(),
HttpCookie::getName));
}
return cookieCache;
}
/**
* 获得指定Cookie名对应的HttpCookie对象
*
* @param cookieName Cookie名
* @return HttpCookie对象
*/
public HttpCookie getCookie(String cookieName) {
return getCookieMap().get(cookieName);
}
/**
* 是否为Multipart类型表单,此类型表单用于文件上传
*
* @return 是否为Multipart类型表单,此类型表单用于文件上传
*/
public boolean isMultipart() {
if (false == isPostMethod()) {
return false;
}
final String contentType = getContentType();
if (StrUtil.isBlank(contentType)) {
return false;
}
return contentType.toLowerCase().startsWith("multipart/");
}
/**
* 获取请求体文本,可以是form表单、json、xml等任意内容
* 使用{@link #getCharset()}判断编码,判断失败使用UTF-8编码
*
* @return 请求
*/
public String getBody() {
return getBody(getCharset());
}
/**
* 获取请求体文本,可以是form表单、json、xml等任意内容
*
* @param charset 编码
* @return 请求
*/
public String getBody(Charset charset) {
return StrUtil.str(getBodyBytes(), charset);
}
/**
* 获取body的bytes数组
*
* @return body的bytes数组
*/
public byte[] getBodyBytes(){
if(null == this.bodyCache){
this.bodyCache = IoUtil.readBytes(getBodyStream(), true);
}
return this.bodyCache;
}
/**
* 获取请求体的流,流中可以读取请求内容,包括请求表单数据或文件上传数据
*
* @return 流
*/
public InputStream getBodyStream() {
InputStream bodyStream = this.httpExchange.getRequestBody();
//issue#I6Q30X,读取body长度,避免读取结束后无法正常结束问题
final String contentLengthStr = getHeader(Header.CONTENT_LENGTH);
long contentLength = 0;
if(StrUtil.isNotBlank(contentLengthStr)){
try{
contentLength = Long.parseLong(contentLengthStr);
} catch (final NumberFormatException ignore){
// ignore
}
}
if(contentLength > 0){
bodyStream = new LimitedInputStream(bodyStream, contentLength);
}
return bodyStream;
}
/**
* 获取指定名称的参数值,取第一个值
* @param name 参数名
* @return 参数值
* @since 5.5.8
*/
public String getParam(String name){
return getParams().get(name, 0);
}
/**
* 获取指定名称的参数值
*
* @param name 参数名
* @return 参数值
* @since 5.5.8
*/
public List getParams(String name){
return getParams().get(name);
}
/**
* 获取参数Map
*
* @return 参数map
*/
public ListValueMap getParams() {
if (null == this.paramsCache) {
this.paramsCache = new ListValueMap<>();
final Charset charset = getCharset();
//解析URL中的参数
final String query = getQuery();
if(StrUtil.isNotBlank(query)){
this.paramsCache.putAll(HttpUtil.decodeParams(query, charset, false));
}
// 解析multipart中的参数
if(isMultipart()){
this.paramsCache.putAll(getMultipart().getParamListMap());
} else{
// 解析body中的参数
final String body = getBody();
if(StrUtil.isNotBlank(body)){
this.paramsCache.putAll(HttpUtil.decodeParams(body, charset, true));
}
}
}
return this.paramsCache;
}
/**
* 获取客户端IP
*
*
* 默认检测的Header:
*
*
* 1、X-Forwarded-For
* 2、X-Real-IP
* 3、Proxy-Client-IP
* 4、WL-Proxy-Client-IP
*
*
*
* otherHeaderNames参数用于自定义检测的Header
* 需要注意的是,使用此方法获取的客户IP地址必须在Http服务器(例如Nginx)中配置头信息,否则容易造成IP伪造。
*
*
* @param otherHeaderNames 其他自定义头文件,通常在Http服务器(例如Nginx)中配置
* @return IP地址
*/
public String getClientIP(String... otherHeaderNames) {
String[] headers = {"X-Forwarded-For", "X-Real-IP", "Proxy-Client-IP", "WL-Proxy-Client-IP", "HTTP_CLIENT_IP", "HTTP_X_FORWARDED_FOR"};
if (ArrayUtil.isNotEmpty(otherHeaderNames)) {
headers = ArrayUtil.addAll(headers, otherHeaderNames);
}
return getClientIPByHeader(headers);
}
/**
* 获取客户端IP
*
*
* headerNames参数用于自定义检测的Header
* 需要注意的是,使用此方法获取的客户IP地址必须在Http服务器(例如Nginx)中配置头信息,否则容易造成IP伪造。
*
*
* @param headerNames 自定义头,通常在Http服务器(例如Nginx)中配置
* @return IP地址
* @since 4.4.1
*/
public String getClientIPByHeader(String... headerNames) {
String ip;
for (String header : headerNames) {
ip = getHeader(header);
if (false == NetUtil.isUnknown(ip)) {
return NetUtil.getMultistageReverseProxyIp(ip);
}
}
ip = this.httpExchange.getRemoteAddress().getHostName();
return NetUtil.getMultistageReverseProxyIp(ip);
}
/**
* 获得MultiPart表单内容,多用于获得上传的文件
*
* @return MultipartFormData
* @throws IORuntimeException IO异常
* @since 5.3.0
*/
public MultipartFormData getMultipart() throws IORuntimeException {
if(null == this.multipartFormDataCache){
this.multipartFormDataCache = parseMultipart(new UploadSetting());
}
return this.multipartFormDataCache;
}
/**
* 获得multipart/form-data 表单内容
* 包括文件和普通表单数据
* 在同一次请求中,此方法只能被执行一次!
*
* @param uploadSetting 上传文件的设定,包括最大文件大小、保存在内存的边界大小、临时目录、扩展名限定等
* @return MultiPart表单
* @throws IORuntimeException IO异常
* @since 5.3.0
*/
public MultipartFormData parseMultipart(UploadSetting uploadSetting) throws IORuntimeException {
final MultipartFormData formData = new MultipartFormData(uploadSetting);
try {
formData.parseRequestStream(getBodyStream(), getCharset());
} catch (IOException e) {
throw new IORuntimeException(e);
}
return formData;
}
}