
com.feilong.servlet.http.RequestUtil Maven / Gradle / Ivy
Show all versions of feilong Show documentation
/*
* Copyright (C) 2008 feilong
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.feilong.servlet.http;
import static com.feilong.core.CharsetType.ISO_8859_1;
import static com.feilong.core.URIComponents.QUESTIONMARK;
import static com.feilong.core.URIComponents.SCHEME_HTTP;
import static com.feilong.core.URIComponents.SCHEME_HTTPS;
import static com.feilong.core.Validator.isNotNullOrEmpty;
import static com.feilong.core.Validator.isNullOrEmpty;
import static com.feilong.core.bean.ConvertUtil.toInteger;
import static com.feilong.core.bean.ConvertUtil.toLong;
import static com.feilong.core.lang.ObjectUtil.defaultEmptyStringIfNull;
import static com.feilong.core.lang.ObjectUtil.defaultIfNullOrEmpty;
import static com.feilong.core.lang.StringUtil.EMPTY;
import static com.feilong.core.lang.StringUtil.tokenizeToStringArray;
import static com.feilong.core.util.MapUtil.newLinkedHashMap;
import static com.feilong.core.util.ResourceBundleUtil.getResourceBundle;
import static com.feilong.core.util.ResourceBundleUtil.getValue;
import static com.feilong.core.util.SortUtil.sortMapByKeyAsc;
import static com.feilong.servlet.http.HttpHeaders.ORIGIN;
import static com.feilong.servlet.http.HttpHeaders.REFERER;
import static com.feilong.servlet.http.HttpHeaders.USER_AGENT;
import static com.feilong.servlet.http.HttpHeaders.X_REQUESTED_WITH;
import static com.feilong.servlet.http.HttpHeaders.X_REQUESTED_WITH_VALUE_AJAX;
import static java.util.Collections.emptyMap;
import java.io.BufferedReader;
import java.io.IOException;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Map;
import java.util.TreeMap;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.feilong.core.CharsetType;
import com.feilong.core.Validate;
import com.feilong.core.bean.ConvertUtil;
import com.feilong.core.lang.StringUtil;
import com.feilong.core.net.ParamUtil;
import com.feilong.core.util.EnumerationUtil;
import com.feilong.core.util.MapUtil;
import com.feilong.io.ReaderUtil;
import com.feilong.json.JsonUtil;
import com.feilong.lib.lang3.StringUtils;
import com.feilong.servlet.http.entity.RequestLogSwitch;
/**
* {@link javax.servlet.http.HttpServletRequest HttpServletRequest}工具类.
*
* {@link HttpServletRequest#getRequestURI() getRequestURI()} 和 {@link HttpServletRequest#getRequestURL() getRequestURL()}:
*
*
*
*
* 字段
* 返回值
*
*
* request.getRequestURI()
* /feilong/requestdemo.jsp
*
*
* request.getRequestURL()
* http://localhost:8080/feilong/requestdemo.jsp
*
*
*
*
*
* 关于从request中获得相关路径和url:
*
*
*
*
* - getServletContext().getRealPath("/") 后包含当前系统的文件夹分隔符(windows系统是"\",linux系统是"/"),而getPathInfo()以"/"开头.
* - getPathInfo()与getPathTranslated()在servlet的url-pattern被设置为/*或/aa/*之类的pattern时才有值,其他时候都返回null.
* - 在servlet的url-pattern被设置为*.xx之类的pattern时,getServletPath()返回的是getRequestURI()去掉前面ContextPath的剩余部分.
*
*
*
*
* 字段
* 说明
*
*
*
* {@link HttpServletRequest#getContextPath()}
* {@link HttpServletRequest#getContextPath()}
*
*
*
* {@link HttpServletRequest#getPathInfo()}
* Returns any extra path information associated with the URL the client sent when it made this request.
* Servlet访问路径之后,QueryString之前的中间部分
*
*
*
* {@link HttpServletRequest#getServletPath()}
* web.xml中定义的Servlet访问路径
*
*
*
* {@link HttpServletRequest#getPathTranslated()}
* 等于getServletContext().getRealPath("/") + getPathInfo()
*
*
*
* {@link HttpServletRequest#getRequestURI()}
* 等于getContextPath() + getServletPath() + getPathInfo()
*
*
*
* {@link HttpServletRequest#getRequestURL()}
* 等于getScheme() + "://" + getServerName() + ":" + getServerPort() + getRequestURI()
*
*
*
* {@link HttpServletRequest#getQueryString()}
* {@code &}之后GET方法的参数部分
* Returns the query string that is contained in the request URL after the path.
* This method returns null if the URL does not have a query string.
* Same as the value of the CGI variable QUERY_STRING.
*
*
*
*
*
* Apache Tomcat Versions:
*
*
* Apache Tomcat™ is an open source software implementation of the Java Servlet and JavaServer Pages technologies.
* Different versions of Apache Tomcat are available for different versions of the Servlet and JSP specifications.
* The mapping between the specifications and the respective Apache Tomcat versions is:
*
*
*
*
*
* Servlet Spec
* JSP Spec
* EL Spec
* WebSocket Spec
* Apache Tomcat version
* Actual release revision
* Support Java Versions
*
*
*
* 4.0
* TBD (2.4?)
* TBD (3.1?)
* TBD (1.2?)
* 9.0.x
* 9.0.0.M8 (alpha)
* 8 and later
*
*
*
* 3.1
* 2.3
* 3.0
* 1.1
* 8.5.x
* 8.5.3
* 7 and later
*
*
*
* 3.1
* 2.3
* 3.0
* 1.1
* 8.0.x (superseded)
* 8.0.35 (superseded)
* 7 and later
*
*
*
* 3.0
* 2.2
* 2.2
* 1.1
* 7.0.x
* 7.0.70
* 6 and later
* (7 and later for WebSocket)
*
*
*
* 2.5
* 2.1
* 2.1
* N/A
* 6.0.x
* 6.0.45
* 5 and later
*
*
*
* 2.4
* 2.0
* N/A
* N/A
* 5.5.x (archived)
* 5.5.36 (archived)
* 1.4 and later
*
*
*
* 2.3
* 1.2
* N/A
* N/A
* 4.1.x (archived)
* 4.1.40 (archived)
* 1.3 and later
*
*
*
* 2.2
* 1.1
* N/A
* N/A
* 3.3.x (archived)
* 3.3.2 (archived)
* 1.1 and later
*
*
*
*
*
* @author feilong
* @see RequestAttributes
* @see RequestLogSwitch
* @since 1.0.0
*/
public final class RequestUtil{
/** The Constant LOGGER. */
private static final Logger LOGGER = LoggerFactory.getLogger(RequestUtil.class);
//---------------------------------------------------------------
/**
* 存放到 request 作用域中的 requestbody 名字.
*
* @see #getRequestBody(HttpServletRequest)
* @since 1.14.2
*/
private static final String REQUEST_BODY_SCOPE_ATTRIBUTE_NAME = RequestUtil.class.getName() + ".REQUEST_BODY";
//---------------------------------------------------------------
/**
* 获得用户真实IP 循环的IP头.
*
* @since 3.0.0
*/
private static final String[] IP_HEADER_NAMES = tokenizeToStringArray(
getValue(getResourceBundle("config/feilong-request-clientIP-headers"), "clientIP.headerNames"),
",");
//---------------------------------------------------------------
/**
* 静态资源的后缀.
* {@value}
.
*
* @see com.feilong.io.entity.MimeType
* @since 1.12.0
* @since 3.0.0 change to config
*/
private static final String[] STATIC_RESOURCE_SUFFIX = tokenizeToStringArray(
getValue(getResourceBundle("config/feilong-request-staticResourceSuffix"), "request.staticResourceSuffix"),
",");
//---------------------------------------------------------------
/** Don't let anyone instantiate this class. */
private RequestUtil(){
//AssertionError不是必须的. 但它可以避免不小心在类的内部调用构造器. 保证该类在任何情况下都不会被实例化.
//see 《Effective Java》 2nd
throw new AssertionError("No " + getClass().getName() + " instances for you!");
}
//---------------------------------------------------------------
/**
* 判断请求是否是静态资源.
*
* @param requestURI
* the request URI
* @return 如果 requestURI
是null或者empty,返回 false
* 如果 requestURI
不包含.,返回 false
* 其他循环 {@link #STATIC_RESOURCE_SUFFIX} ,忽视大小写判断后缀
* @see HttpServletRequest#getRequestURI()
* @since 1.12.0
*/
public static boolean isStaticResource(String requestURI){
if (isNullOrEmpty(requestURI)){
return false;
}
//---------------------------------------------------------------
if (!requestURI.contains(".")){
return false;
}
//---------------------------------------------------------------
for (String uriSuffix : STATIC_RESOURCE_SUFFIX){
if (StringUtils.endsWithIgnoreCase(requestURI, uriSuffix)){
return true;
}
}
return false;
}
//---------------------------------------------------------------
/**
* 判断传入的method
是否在 支持的supportHttpMethods
数组中.
*
* @param supportHttpMethods
* 支持的method 数组
* @param method
* the method
* @return 如果 method
是null,抛出 {@link NullPointerException}
* 如果 method
是blank,抛出 {@link IllegalArgumentException}
* 如果 supportHttpMethods
是null或者empty,返回 false
* 循环 supportHttpMethods, 忽视大小写判断和 method 是否equalsIgnoreCase, 如果是返回true,否则false
* @since 1.12.1
*/
public static boolean isSupportMethod(String[] supportHttpMethods,String method){
Validate.notBlank(method, "method can't be blank!");
//---------------------------------------------------------------
//null 或者 empty 表示没有一个 method 支持的,不过滤
if (isNullOrEmpty(supportHttpMethods)){
return false;
}
//---------------------------------------------------------------
for (String supportHttpMethod : supportHttpMethods){
//如果当前的请求 method ,在支持的列表里面, 那么表示要过滤
if (StringUtils.equalsIgnoreCase(supportHttpMethod, method)){
return true;
}
}
return false;
}
//-------------------------是否包含--------------------------------------
/**
* 请求路径中是否包含某个参数名称 (注意:这是判断是否包含参数,而不是判断参数值是否为空).
*
* @param request
* 请求
* @param paramName
* 参数名称
* @return 包含该参数返回true,不包含返回false
* 如果 paramName
是null,抛出 {@link NullPointerException}
* 如果 paramName
是blank,抛出 {@link IllegalArgumentException}
* @see com.feilong.core.util.EnumerationUtil#contains(Enumeration, Object)
* @since 1.4.0
*/
public static boolean containsParam(HttpServletRequest request,String paramName){
Validate.notBlank(paramName, "paramName can't be null/empty!");
return EnumerationUtil.contains(request.getParameterNames(), paramName);
}
//---------------------------------------------------------------
/**
* 获得参数map(结果转成了key自然排序的TreeMap).
*
*
* 此方式会将tomcat返回的map 转成TreeMap 返回,便于log; 也可以对这个返回的map进行操作
*
*
* tomcat getParameterMap() locked(只能读):
*
*
* 注意:tomcat 默认实现,返回的是 {@code org.apache.catalina.util#ParameterMap},tomcat返回之前,会将此map的状态设置为locked,
*
*
* 不像普通的map数据一样可以修改.
* 这是因为服务器为了实现一定的安全规范,所作的限制,WebLogic,Tomcat,Resin,JBoss等服务器均实现了此规范.
*
*
* 不能做以下的map操作:
*
*
* - {@link Map#clear()}
* - {@link Map#put(Object, Object)}
* - {@link Map#putAll(Map)}
* - {@link Map#remove(Object)}
*
*
*
*
* @param request
* the request
* @return the parameter map
* @see "org.apache.catalina.connector.Request#getParameterMap()"
*/
public static Map getParameterMap(HttpServletRequest request){
// http://localhost:8888/s.htm?keyword&a=
// 这种链接 map key 会是 keyword,a 值都是空
return sortMapByKeyAsc(request.getParameterMap()); // servlet 3.0 此处返回类型的是 泛型数组 Map
}
/**
* 获得请求参数和单值map.
*
*
* 由于调用的 {@link #getParameterMap(HttpServletRequest)}结果是有序的,
* 此方法返回的map也是有序的
*
*
*
* 由于j2ee{@link ServletRequest#getParameterMap()}返回的map值是数组形式,
* 对于一些确认是单值的请求(比如支付宝notify/return request),不便后续处理
*
*
* @param request
* the request
* @return the parameter single value map
* @see #getParameterMap(HttpServletRequest)
* @see MapUtil#toSingleValueMap(Map)
* @since 1.2.0
*/
public static Map getParameterSingleValueMap(HttpServletRequest request){
return MapUtil.toSingleValueMap(getParameterMap(request));
}
//---------------------------------------------------------------
/**
* 将 {@link HttpServletRequest} 相关属性,数据转成json格式 以便log显示(目前仅作log使用).
*
*
* 默认使用 {@link RequestLogSwitch#NORMAL}
*
*
* 示例:
*
*
*
* Map{@code } requestInfoMapForLog = RequestUtil.getRequestInfoMapForLog(request);}
* LOGGER.debug("class:[{}],request info:{}", getClass().getSimpleName(), JsonUtil.format(requestInfoMapForLog);
*
*
* 输出结果:
*
*
* 19:28:37 DEBUG (AbstractWriteContentTag.java:63) execute() - class:[HttpConcatTag],request info: {
* "requestFullURL": "/member/login.htm?a=b",
* "request.getMethod": "GET",
* "parameterMap": {"a": ["b"]}
* }
*
*
*
*
* @param request
* the request
* @return the request string for log
* @see RequestLogSwitch
* @see #getRequestInfoMapForLog(HttpServletRequest, RequestLogSwitch)
*/
public static Map getRequestInfoMapForLog(HttpServletRequest request){
return getRequestInfoMapForLog(request, RequestLogSwitch.NORMAL);
}
/**
* 将request full相关属性,数据转成json格式 以便log显示(目前仅作log使用).
*
* 示例:
*
*
*
* Map{@code } requestInfoMapForLog = RequestUtil.getRequestInfoFullMapForLog(request);
* LOGGER.debug(JsonUtil.format(requestInfoMapForLog));
*
*
* 输出结果:
*
*
{
"requestFullURL": "http://wws.feilong.com/feilongtest/book/index/undefined",
"requestMethod": "GET",
"parameters": {
"libId": [
"1"
]
},
"requestIdentity": {
"clientIP": "223.104.178.121",
"userAgent": "Mozilla/5.0 (Linux; Android 10; JAD-AL50 Build/HUAWEIJAD-AL50; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/86.0.4240.99 XWEB/4317 MMWEBSDK/20220903 Mobile Safari/537.36 MMWEBID/4791 MicroMessenger/8.0.28.2240(0x28001C3B) WeChat/arm64 Weixin NetType/4G Language/zh_CN ABI/arm64",
"sessionId": null
},
"headerInfos": {
"accept-encoding": "gzip, deflate",
"accept-language": "zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7",
"connection": "close",
"content-length": "0",
"cookie": "freeFlowType=0; minorProtectionStatus=0; pcdnFree=0",
"host": "wws.feilong.com",
"referer": "https://wws.feilong.com/wws-lib/book/index/315?libId=6585",
"sec-fetch-dest": "image",
"sec-fetch-mode": "no-cors",
"sec-fetch-site": "same-origin",
"user-agent": "Mozilla/5.0 (Linux; Android 10; JAD-AL50 Build/HUAWEIJAD-AL50; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/86.0.4240.99 XWEB/4317 MMWEBSDK/20220903 Mobile Safari/537.36 MMWEBID/4791 MicroMessenger/8.0.28.2240(0x28001C3B) WeChat/arm64 Weixin NetType/4G Language/zh_CN ABI/arm64",
"x-forwarded-for": "223.104.178.121",
"x-real-ip": "223.104.178.121",
"x-real-port": "15014",
"x-requested-with": "com.tencent.mm"
},
"cookieInfos": {
"domain": ".feilong.com",
"freeFlowType": "0",
"minorProtectionStatus": "0",
"path": "/",
"pcdnFree": "0",
"trackType": "H5"
},
"urlInfos": {
"request.getContextPath()": "/feilongtest",
"request.getServletPath()": "",
"request.getPathInfo()": "/book/index/undefined",
"request.getPathTranslated()": "/usr/local/tomcat/webapps/feilongtest/book/index/undefined",
"request.getRequestURI()": "/feilongtest//book/index/undefined",
"request.getRequestURL()": "http://wws.feilong.com/feilongtest//book/index/undefined",
"request.getQueryString()": null,
"getQueryStringLog": null
},
"elseInfos": {
"request.getScheme()": "http",
"request.getProtocol()": "HTTP/1.1",
"request.getAuthType()": null,
"request.getCharacterEncoding()": "UTF-8",
"request.getContentType()": null,
"request.getContentLength()": "0",
"request.getLocale()": "zh_CN",
"request.getLocalName()": "feilongtest-64fd6b8bc7-dhmck",
"request.getRemoteUser()": null,
"request.isRequestedSessionIdFromCookie()": false,
"request.isRequestedSessionIdFromURL()": false,
"request.isRequestedSessionIdValid()": false,
"request.isSecure()": false,
"request.getUserPrincipal()": null
},
"ipInfos": {
"getClientIp": "223.104.178.121",
"request.getLocalAddr()": "10.204.193.105",
"request.getRemoteAddr()": "192.168.58.182",
"request.getRemoteHost()": "192.168.58.182",
"request.getServerName()": "wws.feilong.com"
},
"portInfos": {
"request.getLocalPort()": "9582",
"request.getRemotePort()": "2980",
"request.getServerPort()": "80"
}
}
*
*
*
*
* @param request
* the request
* @return the request string for log
* @see RequestLogBuilder#RequestLogBuilder(HttpServletRequest, RequestLogSwitch)
* @since 3.3.2
*/
public static Map getRequestInfoFullMapForLog(HttpServletRequest request){
return getRequestInfoMapForLog(request, RequestLogSwitch.FULL);
}
/**
* 将request 相关属性,数据转成json格式 以便log显示(目前仅作log使用).
*
* 示例:
*
*
*
* RequestLogSwitch requestLogSwitch = RequestLogSwitch.NORMAL;
* Map{@code } requestInfoMapForLog = RequestUtil.getRequestInfoMapForLog(request, requestLogSwitch);
* LOGGER.debug("class:[{}],request info:{}", getClass().getSimpleName(), JsonUtil.format(requestInfoMapForLog);
*
*
* 输出结果:
*
*
* 19:28:37 DEBUG (AbstractWriteContentTag.java:63) execute() - class:[HttpConcatTag],request info: {
* "requestFullURL": "/member/login.htm?a=b",
* "request.getMethod": "GET",
* "parameterMap": {"a": ["b"]}
* }
*
*
*
*
* @param request
* the request
* @param requestLogSwitch
* the request log switch
* @return the request string for log
* @see RequestLogBuilder#RequestLogBuilder(HttpServletRequest, RequestLogSwitch)
*/
public static Map getRequestInfoMapForLog(HttpServletRequest request,RequestLogSwitch requestLogSwitch){
return new RequestLogBuilder(request, requestLogSwitch).build();
}
//-------------------------url参数相关 getAttribute--------------------------------------
// [start] url参数相关
/**
* 获得 attribute.
*
* @param
* the generic type
* @param request
* the request
* @param attributeName
* 属性名称
* @return 如果 attributeName
是null,抛出 {@link NullPointerException}
* 如果 attributeName
是blank,抛出 {@link IllegalArgumentException}
* @see javax.servlet.ServletRequest#getAttribute(String)
* @since 1.3.0
*/
@SuppressWarnings("unchecked")
public static T getAttribute(HttpServletRequest request,String attributeName){
Validate.notBlank(attributeName, "attributeName can't be null/empty!");
return (T) request.getAttribute(attributeName);
}
/**
* 取到request里面的属性值,转换类型成指定的参数 klass
.
*
* @param
* the generic type
* @param request
* 请求
* @param name
* 属性名称
* @param klass
* 需要被转换成的类型
* @return the attribute
* @see com.feilong.core.bean.ConvertUtil#convert(Object, Class)
* @see #getAttribute(HttpServletRequest, String)
* @since 1.3.0
*/
public static T getAttribute(HttpServletRequest request,String name,Class klass){
Object value = getAttribute(request, name);
return ConvertUtil.convert(value, klass);
}
//---------------------------------------------------------------
/**
* 获得请求的?部分前面的地址.
*
* 自动识别 request 是否 forword,如果是forword过来的,那么取 {@link RequestAttributes#FORWARD_REQUEST_URI}变量
*
*
*
* 如:http://localhost:8080/feilong/requestdemo.jsp?id=2
* 返回:http://localhost:8080/feilong/requestdemo.jsp
*
*
* 注:
*
*
*
*
* 字段
* 返回值
*
*
* request.getRequestURI()
* /feilong/requestdemo.jsp
*
*
* request.getRequestURL()
* http://localhost:8080/feilong/requestdemo.jsp
*
*
*
*
* @param request
* the request
* @return 获得请求的?部分前面的地址
*/
public static String getRequestURL(HttpServletRequest request){
String forwardRequestUri = getAttribute(request, RequestAttributes.FORWARD_REQUEST_URI);
return isNotNullOrEmpty(forwardRequestUri) ? forwardRequestUri : request.getRequestURL().toString();
}
/**
* Return the servlet path for the given request, detecting an include request URL if called within a RequestDispatcher include.
*
* @param request
* current HTTP request
* @return the servlet path
*/
public static String getOriginatingServletPath(HttpServletRequest request){
String servletPath = getAttribute(request, RequestAttributes.FORWARD_SERVLET_PATH);
return isNotNullOrEmpty(servletPath) ? servletPath : request.getServletPath();
}
//---------------------------------------------------------------
/**
* 获得请求的全地址.
*
* @param request
* the request
* @return 如:http://localhost:8080/feilong/requestdemo.jsp?id=2
* @since 3.1.1
* @since 4.0.6 修改内部实现逻辑,原先直接使用request.getQueryString() 拼接,现在改成兼容非get类型,从参数中提取, see
* {@link post 请求 显示的full url不全 #76}
*/
public static String getRequestFullURL(HttpServletRequest request){
StringBuilder sb = new StringBuilder(getRequestURL(request));
//---------------------------------------------------------------
//提取参数,转成类似于queryString字符串.
String queryString = parseParamsToQueryString(request);
if (isNotNullOrEmpty(queryString)){
sb.append(QUESTIONMARK).append(queryString);
}
return sb.toString();
}
/**
* 获得请求的全地址.
*
* @param request
* the request
* @param charsetType
* 字符编码,建议使用 {@link CharsetType} 定义好的常量
* @return 如:http://localhost:8080/feilong/requestdemo.jsp?id=2
* @since 4.0.6 修改内部实现逻辑,原先直接使用request.getQueryString() 拼接,现在改成兼容非get类型,从参数中提取, see
* {@link post 请求 显示的full url不全 #76}
* @deprecated since 4.0.6这个不需要charsetType,直接使用 {@link #getRequestFullURL(HttpServletRequest)} 就好
*/
@Deprecated
public static String getRequestFullURL(HttpServletRequest request,String charsetType){
return getRequestFullURL(request);
}
/**
* 提取参数,转成类似于queryString字符串.
*
*
* - 如果request是get 请求,那么直接返回 request.getQueryString()
* - 如果request不是get,那么提取参数组装成字符串返回
*
*
* @param request
* the request
* @return 如果 request
是null,返回 ""
* 如果request是get 请求,那么直接返回 request.getQueryString()
* 如果request不是get,那么提取参数组装成字符串返回
* @see javax.servlet.http.HttpServletRequest#getMethod()
* @since 4.0.6
*/
public static String parseParamsToQueryString(HttpServletRequest request){
if (null == request){
return EMPTY;
}
//---------------------------------------------------------------
String method = request.getMethod();
if ("get".equalsIgnoreCase(method)){
//request.getQueryString() : 返回 the query string that is contained in the request URL after the path.
//This method returns null if the URL does not have a query string.
// Same as the value of the CGI variable QUERY_STRING.
// 它只对get方法得到的数据有效.
//a String
containing the query string or null
if the URL contains no query string.
//The value is not decoded by the container.
return defaultEmptyStringIfNull(request.getQueryString());
}
//---------------------------------------------------------------
Map map = getParameterMap(request);
//内部判断是null
return ParamUtil.toQueryStringUseArrayValueMap(map);
}
//---------------------------------------------------------------
/**
* {@link CharsetType#ISO_8859_1} 的方式去除乱码.
*
*
* {@link CharsetType#ISO_8859_1} 是JAVA网络传输使用的标准 字符集
*
*
* 关于URI Encoding
*
*
*
* - tomcat server.xml Connector URIEncoding="UTF-8"
* - {@code
}
*
*
*
* @param str
* 字符串
* @param charsetType
* 字符编码,建议使用 {@link CharsetType} 定义好的常量
* @return 如果 str
是null或者empty,返回 {@link StringUtils#EMPTY}
* @see "org.apache.commons.codec.net.URLCodec#encode(String, String)"
* @see "org.apache.taglibs.standard.tag.common.fmt.RequestEncodingSupport"
* @see "org.apache.catalina.filters.SetCharacterEncodingFilter"
* @since 1.7.3 move from feilong-core
* @deprecated may delete
*/
@Deprecated
public static String decodeISO88591String(String str,String charsetType){
return StringUtil.newString(StringUtil.getBytes(str, ISO_8859_1), charsetType);
}
/**
* scheme+serverName+port+getContextPath.
*
*
* 区分 http 和https.
*
*
* @param request
* the request
* @return 如:http://localhost:8080/feilong/
* @see "org.apache.catalina.connector.Request#getRequestURL()"
* @see "org.apache.catalina.realm.RealmBase#hasUserDataPermission(Request, Response, SecurityConstraint[])"
* @see javax.servlet.http.HttpUtils#getRequestURL(HttpServletRequest)
*/
public static String getServerRootWithContextPath(HttpServletRequest request){
String scheme = request.getScheme();
int port = request.getServerPort() < 0 ? 80 : request.getServerPort();// Work around java.net.URL bug
//---------------------------------------------------------------
StringBuilder sb = new StringBuilder();
sb.append(scheme);
sb.append("://");
sb.append(request.getServerName());
if ((scheme.equals(SCHEME_HTTP) && (port != 80)) || (scheme.equals(SCHEME_HTTPS) && (port != 443))){
sb.append(':');
sb.append(port);
}
sb.append(request.getContextPath());
return sb.toString();
}
//---------------------------------------------------------------
// [end]
/**
* 用于将请求转发到 {@link RequestDispatcher} 对象封装的资源,Servlet程序在调用该方法转发之前可以对请求进行前期预处理.
*
*
* 该方法将 checked exception 转成了 unchecked exception,方便书写和调用
*
*
*
* Forwards a request from a servlet to another resource (servlet, JSP file, or HTML file) on the server.
* This method allows one servlet to do preliminary processing of a request and another resource to generate the response.
*
*
*
* For a RequestDispatcher
obtained via getRequestDispatcher()
, the ServletRequest
object has its
* path elements and parameters adjusted to match the path of the target resource.
*
*
*
* forward
should be called before the response has been committed to the client (before
* response body output has been flushed).
*
* If the response already has been committed, this method throws an IllegalStateException
. Uncommitted output in
* the response buffer is automatically cleared before the forward.
*
*
*
* 如果 path
是null,抛出 {@link NullPointerException}
*
*
* @param path
* a String specifying the pathname to the resource.
* If it is relative, it must be relative against the current servlet.The
* pathname specified may be relative, although it cannot extend outside the current servlet context.
* If the path begins with a "/" it is interpreted as relative to the current context root.
* This method returns null if the servlet container cannot return a RequestDispatcher.
* 如果 path
是null,抛出 {@link NullPointerException}
* @param request
* a {@link ServletRequest} object that represents the request the client makes of the servlet
* @param response
* a {@link ServletResponse} object,that represents the response the servlet returns to the client
* @since 1.2.2
*/
public static void forward(String path,HttpServletRequest request,HttpServletResponse response){
//since 2.0.1
Validate.notNull(path, "path can't be null!");
LOGGER.debug("will forward to path:[{}]", path);
try{
RequestDispatcher requestDispatcher = request.getRequestDispatcher(path);
requestDispatcher.forward(request, response);
}catch (ServletException | IOException e){
throw new RequestException("when forward to:" + path, e);
}
}
/**
* 用于将 {@link RequestDispatcher} 对象封装的资源内容作为当前响应内容的一部分包含进来,从而实现可编程服务器的服务器端包含功能.
*
*
* 该方法将 checked exception 转成了 unchecked exception,方便书写和调用
*
*
*
* Includes the content of a resource (servlet, JSP page,HTML file) in the response.
* In essence, this method enables programmatic server-side includes.
*
*
*
* 注:被包含的Servlet程序不能改变响应信息的状态码和响应头,如果里面包含这样的语句将被忽略.
* The {@link ServletResponse} object has its path elements and parameters remain unchanged from the caller's.
* The included servlet cannot change the response status code or set headers; any attempt to make a change is ignored.
*
*
* @param path
* a String specifying the pathname to the resource.
* If it is relative, it must be relative against the current servlet.The
* pathname specified may be relative, although it cannot extend outside the current servlet context.
* If the path begins with a "/" it is interpreted as relative to the current context root.
* This method returns null if the servlet container cannot return a RequestDispatcher.
* 如果 path
是null,抛出 {@link NullPointerException}
* @param request
* a {@link ServletRequest} object,that contains the client's request
* @param response
* a {@link ServletResponse} object,that contains the servlet's response
* @see javax.servlet.RequestDispatcher#include(ServletRequest, ServletResponse)
* @see "org.springframework.web.servlet.ResourceServlet"
* @since 1.2.2
*/
public static void include(String path,HttpServletRequest request,HttpServletResponse response){
//since 2.0.1
Validate.notNull(path, "path can't be null!");
LOGGER.debug("will include to path:[{}]", path);
try{
RequestDispatcher requestDispatcher = request.getRequestDispatcher(path);
requestDispatcher.include(request, response);
}catch (ServletException | IOException e){
throw new RequestException("when include:" + path, e);
}
}
//--------------------------Header-------------------------------------
/**
* 取指定headerName
的 header 值.
*
* @param request
* the request
* @param headerName
* headerName 名字,不区分大小写
* @return 如果请求不包含指定名称的标头,则此方法返回null;
* 如果有多个具有相同名称的头,此方法将返回请求中的第一个头
* @since 3.3.9
*/
public static String getHeader(HttpServletRequest request,String headerName){
return request.getHeader(headerName);
}
/**
* 取指定headerName
的 header 值转成Integer类型返回.
*
* @param request
* the request
* @param headerName
* headerName 名字,不区分大小写
* @return 如果请求不包含指定名称的标头,则此方法返回null;
* 如果有多个具有相同名称的头,此方法将返回请求中的第一个头
* 否则,返回 header value Integer 类型
* @see Cookie#getValue()
* @since 3.3.9
*/
public static Integer getHeaderIntegerValue(HttpServletRequest request,String headerName){
String value = getHeader(request, headerName);
return toInteger(value);
}
/**
* 取指定cookieName
的 {@link Cookie}值转成Long类型返回.
*
* @param request
* the request
* @param headerName
* headerName 名字,不区分大小写
* @return 如果请求不包含指定名称的标头,则此方法返回null;
* 如果有多个具有相同名称的头,此方法将返回请求中的第一个头
* 否则,返回 header value Long 类型
* @see Cookie#getValue()
* @since 3.3.9
*/
public static Long getHeaderLongValue(HttpServletRequest request,String headerName){
String value = getHeader(request, headerName);
return toLong(value);
}
/**
* 获得客户端真实ip地址.
*
*
* 这样做的好处是,对开发透明
*
*
* @param request
* the request
* @return 获得客户端ip地址
* @see "org.apache.catalina.valves.RemoteIpValve"
* @see Story behind
* X-Forwarded-For and X-Real-IP headers
* @see nginx做负载CDN加速获取端真实ip
*/
public static String getClientIp(HttpServletRequest request){
Map map = newLinkedHashMap();
for (String ipHeaderName : IP_HEADER_NAMES){
//The header name is case insensitive (不区分大小写)
map.put(ipHeaderName, getHeader(request, ipHeaderName));
}
map.put("request.getRemoteAddr()", request.getRemoteAddr());
return getClientIp(map);
}
/**
* Gets the client ip.
*
* @param map
* the map
* @return the client ip
* @since 3.0.0
*/
private static String getClientIp(Map map){
if (LOGGER.isTraceEnabled()){
LOGGER.trace("ips:{}", JsonUtil.toString(map));
}
//---------------------------------------------------------------
for (Map.Entry entry : map.entrySet()){
String value = entry.getValue();
//IPV4 127.0.0.1
//IPV6 2610:00f8:0c34:67f9:0200:83ff:fe94:4c36
if (isNullOrEmpty(value) || "unknown".equalsIgnoreCase(value)){
continue;
}
//取非空值里面第一个 ,比如 X-Forwarded-For: client1, proxy1, proxy2.
//已去空格 忽略empty元素
String[] ips = tokenizeToStringArray(value, ",");
return ips[0];
}
return EMPTY;
}
//---------------------------------------------------------------
/**
* User Agent中文名为用户代理,简称 UA.
*
*
* 它是一个特殊字符串头,使得服务器能够识别客户使用的操作系统及版本、CPU 类型、浏览器及版本、浏览器渲染引擎、浏览器语言、浏览器插件等.
*
*
* @param request
* the request
* @return 如果request没有指定名称 {@link HttpHeaders#USER_AGENT} 的header,那么返回null
* @see HttpHeaders#USER_AGENT
*/
public static String getHeaderUserAgent(HttpServletRequest request){
return getHeader(request, USER_AGENT);
}
/**
* 获得上个请求的URL.
*
*
* referer是浏览器在用户提交请求当前页面中的一个链接时,将当前页面的URL放在头域中提交给服务端的,如当前页面为a.html,
* 它里面有一个b.html的链接,当用户要访问b.html时浏览器就会把a.html作为referer发给服务端.
*
*
* 注意:
*
* 请用于常规请求,必须走http/https协议才有值,javascript跳转无效
*
* 也就是说要通过<a href="url">sss</a>标记才能获得那个值
* 而通过改变location或是<a href="javascript:location='url'">sss</a>都是得不到那个值得
*
*
* 不能正常取值的情况:
*
*
* - 从收藏夹链接
* - 用Window.open打开地址或者自定义的地址
* - 利用Jscript的location.href or location.replace()
* - 在浏览器直接输入地址
* - Response.Redirect
* - Response.AddHeader或{@code }转向
*
*
*
* 关于referer的获取问题
*
*
* 在上一页面做跳转操作,可以在下一页面获得上一页面的Referer从而判断页面的来路。
*
*
* 目前web开发有以下几种页面跳转方式:
*
*
*
*
*
*
* 方式
* 是否支持跨域
* 是否可以取到referer
* 代码示例
*
*
*
* {@link RequestDispatcher} 跳转
* 不支持跨域
* 目的页面无法取得referer
*
*
*
* RequestDispatcher rd = request.getRequestDispatcher(url);
* rd.forward(request, response);
*
*
*
*
*
*
* response.setHeader
* 支持跨域
* 目的页面无法取得referer
*
*
*
* response.setStatus(HttpServletResponse.SC_MOVED_PERMANENTLY);
* response.setHeader("Location", url);
*
*
*
*
*
*
*
* response.sendRedirect(url)
* 支持跨域
* 目的页面无法取得referer
*
*
*
*
* 用form表单, post方法提交
* 既可跨域
* 又能得到referer
* 并且支持form表单的action属性中url使用参数
*
*
*
* 用form表单, get方法提交
* 既可跨域
* 又能得到referer
*
*
* 但不支持form表单的action属性中url使用参数,
* 这种方式不会将action的值后面添加"?"提交到web服务器。
* 如果actio中的url就含有"?"则会将"?"后的数据忽略掉。而post方式不存在这个问题
*
*
*
*
*
* 使用html中href来跳转页面
*
* 目的页面可以获得referer
*
*
*
*
*
*
*
*
* @param request
* the request
* @return 如果request没有指定名称 {@link HttpHeaders#REFERER} 的header,那么返回null
* @see HttpHeaders#REFERER
* @see 关于referer的获取问题
*/
public static String getHeaderReferer(HttpServletRequest request){
return getHeader(request, REFERER);
}
/**
* 1、Origin字段里只包含是谁发起的请求,并没有其他信息 (通常情况下是方案,主机和活动文档URL的端口).
* 跟Referer不一样的是,Origin字段并没有包含涉及到用户隐私的URL路径和请求内容,这个尤其重要.
* 2、Origin字段只存在于POST请求,而Referer则存在于所有类型的请求.
*
* @param request
* the request
* @return 如果request没有指定名称 {@link HttpHeaders#ORIGIN} 的header,那么返回null
* @see HttpHeaders#ORIGIN
*/
public static String getHeaderOrigin(HttpServletRequest request){
return getHeader(request, ORIGIN);
}
/**
* 遍历显示request的header.
*
*
* 将 request header name 和value 封装到TreeMap.
*
*
*
* {@code
* "headerInfo": {
* "accept-encoding": "gzip,deflate",
* "connection": "Keep-Alive",
* "host": "127.0.0.1:8084",
* "user-agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.21 (KHTML, like Gecko) Chrome/19.0.1042.0 Safari/535.21"
* },
* }
*
*
* @param request
* the request
* @return the header map 按照名字顺序返回
* @since 3.1.0
*/
public static Map getHeaderMap(HttpServletRequest request){
Enumeration headerNames = request.getHeaderNames();
if (isNullOrEmpty(headerNames)){
return emptyMap();
}
Map map = new TreeMap<>();
while (headerNames.hasMoreElements()){
String name = headerNames.nextElement();
map.put(name, getHeader(request, name));
}
return map;
}
//---------------------------------------------------------------
/**
* 判断一个请求是不是get method.
*
* @param request
* the request
* @return 如果没有request,那么返回false;否则判断request.getMethod() 是不是get (忽略大小写)
* @since 3.5.1
*/
public static boolean isGetMethod(HttpServletRequest request){
if (null == request){
return false;
}
return StringUtils.equalsIgnoreCase("get", request.getMethod());
}
/**
* 判断一个请求是不是post method.
*
* @param request
* the request
* @return 如果没有request,那么返回false;否则判断request.getMethod() 是不是post (忽略大小写)
* @since 3.5.1
*/
public static boolean isPostMethod(HttpServletRequest request){
if (null == request){
return false;
}
return StringUtils.equalsIgnoreCase("post", request.getMethod());
}
//---------------------------------------------------------------
/**
* 判断一个请求是否是 微信浏览器 的请求.
*
* 说明:
*
*
*
* - 在iPhone下 (iphone11 IOS15.4.1 wechat8.0.22),返回
* Mozilla/5.0 (iPhone; CPU iPhone OS 15_4_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148
* MicroMessenger/8.0.22(0x18001628)
*
* - 在Android下,返回
* Mozilla/5.0 (Linux; U; Android 2.3.6; zh-cn; GT-S5660 Build/GINGERBREAD) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile
* Safari/533.1 MicroMessenger/4.5.255
*
*
* 不难发现微信浏览器为 MicroMessenger,并且有版本号,也可以判断手机类型为iPhone还是Android
*
*
* @param request
* the request
* @return 如果没有userAgent,那么返回false;否则判断ua 里面是否包含 micromessenger 值
* @see 微信浏览器的HTTP_USER_AGENT
* @see 如何在服务器端判断请求的客户端是微信调用的浏览器?
* @since 1.10.4
*/
public static boolean isWechatRequest(HttpServletRequest request){
return userAgentContainsString(request, "micromessenger");
}
//---------------------------------------------------------------
/**
* 判断一个请求不是 微信浏览器 的请求.
*
* @param request
* the request
* @return 如果不是微信浏览器请求的,那么返回true , 否则返回false
* @see #isWechatRequest(HttpServletRequest)
* @since 3.1.0
*/
public static boolean isNotWechatRequest(HttpServletRequest request){
return !isWechatRequest(request);
}
//---------------------------------------------------------------
/**
* 判断一个请求是否是 微信小程序 的请求.
*
*
* 注意:此方法目前android weixin userAgent 会带特殊字符,IOS环境没有,{@link ios微信web-view的user-agent缺失miniprogram}
* 请谨慎使用,为了将来扩展,暂时保留此方法
*
*
* 示例:
*
*
* - Mozilla/5.0 (Linux; Android 10; AGS3-AL00 Build/HUAWEIAGS3-AL00; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0
* Chrome/86.0.4240.99 XWEB/3225 MMWEBSDK/20220505 Safari/537.36 MMWEBID/8510 MicroMessenger/8.0.23.2160(0x280017A6) WeChat/arm64 Weixin
* Android Tablet NetType/WIFI Language/zh_CN ABI/arm64 MiniProgramEnv/android
*
* - Mozilla/5.0 (Linux; Android 10; TAS-AN00 Build/HUAWEITAS-AN00; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0
* Chrome/86.0.4240.99 XWEB/3225 MMWEBSDK/20220505 Mobile Safari/537.36 MMWEBID/5189 MicroMessenger/8.0.23.2160(0x2800173B) WeChat/arm64
* Weixin NetType/WIFI Language/zh_CN ABI/arm64 MiniProgramEnv/android
*
*
* 可以发现微信小程序 即带有为 MicroMessenger 还带有 MiniProgramEnv,并且有版本号,也可以判断手机类型为iPhone还是Android
*
*
* @param request
* the request
* @return 如果没有userAgent,那么返回false;否则判断ua 里面是否包含 miniprogram 值
* @see 从微信7.0.0开始,可以通过判断userAgent中包含miniProgram字样来判断小程序
* web-view 环境
* @see ios微信web-view的user-agent缺失miniprogram
* @since 3.1.0
* @since 微信7.0.0(2018-12-21)
*/
public static boolean isWechatMiniProgramRequest(HttpServletRequest request){
return userAgentContainsString(request, "miniprogram");
}
/**
* 判断一个请求 不是 微信小程序 的请求.
*
*
* 注意:此方法目前android weixin userAgent 会带特殊字符,IOS环境没有,{@link ios微信web-view的user-agent缺失miniprogram}
* 请谨慎使用,为了将来扩展,暂时保留此方法
*
*
* @param request
* the request
* @return 如果不是微信小程序请求,那么返回true , 否则返回false
* @see 从微信7.0.0开始,可以通过判断userAgent中包含miniProgram字样来判断小程序
* web-view 环境
* @see #isWechatMiniProgramRequest(HttpServletRequest)
* @since 3.1.0
* @since 微信7.0.0(2018-12-21)
*/
public static boolean isNotWechatMiniProgramRequest(HttpServletRequest request){
return !isWechatMiniProgramRequest(request);
}
//---------------------------------------------------------------
/**
* 判断header 中是否包含指定的字符串(忽视大小写).
*
* @param request
* the request
* @param s
* 指定的字符串
* @return 如果 userAgent
是null或者empty,返回false
* 如果 userAgent
包含指定字符串(忽视大小写),返回true;否则返回false
* @since 3.1.0
*/
private static boolean userAgentContainsString(HttpServletRequest request,String s){
String userAgent = getHeaderUserAgent(request);
if (isNullOrEmpty(userAgent)){
return false;
}
boolean contains = userAgent.toLowerCase().contains(s.toLowerCase());
//---------------------------------------------------------------
if (LOGGER.isDebugEnabled()){
LOGGER.debug("userAgent:{},isContains[\"{}\"]:[{}]", userAgent, s, contains);
}
//---------------------------------------------------------------
return contains;
}
/**
* 判断一个请求是否是ajax请求.
*
* @param request
* the request
* @return 如果是ajax 请求 返回true
* @see "http://en.wikipedia.org/wiki/X-Requested-With#Requested-With"
*/
public static boolean isAjaxRequest(HttpServletRequest request){
String header = getHeader(request, X_REQUESTED_WITH);
return isNotNullOrEmpty(header) && header.equalsIgnoreCase(X_REQUESTED_WITH_VALUE_AJAX);
}
/**
* 判断一个请求 ,不是ajax 请求.
*
* @param request
* the request
* @return 如果不是ajax 返回true
* @see #isAjaxRequest(HttpServletRequest)
*/
public static boolean isNotAjaxRequest(HttpServletRequest request){
return !isAjaxRequest(request);
}
//---------------------------------------------------------------
/**
* 遍历显示request的attribute,将 name /attributeValue 存入到map(TreeMap).
*
*
* 注意:
*
*
*
* 1.目前如果属性有级联关系,如果直接转json,可能会报错,此时建议使用 {@link JsonUtil#formatSimpleMap(Map, Class...) }
*
*
*
* 2.可以做返回的map进行remove操作,不会影响request的 Attribute
*
*
*
* @param request
* the request
* @return 如果{@link javax.servlet.ServletRequest#getAttributeNames()} 是null或者empty,返回{@link Collections#emptyMap()}
*/
public static Map getAttributeMap(HttpServletRequest request){
Enumeration attributeNames = request.getAttributeNames();
if (isNullOrEmpty(attributeNames)){
return emptyMap();
}
//---------------------------------------------------------------
Map map = new TreeMap<>();
while (attributeNames.hasMoreElements()){
String name = attributeNames.nextElement();
map.put(name, getAttribute(request, name));
}
return map;
}
//---------------------------------------------------------------
/**
* 获得request中的请求参数值.
*
* @param request
* 当前请求
* @param paramName
* 参数名称
* @return 获得request中的请求参数值
*/
public static String getParameter(HttpServletRequest request,String paramName){
return request.getParameter(paramName);
}
/**
* 获得request中的请求参数值,如果不存在或者是empty,那么使用默认值 defaultValue
.
*
* 重构:
*
*
*
* 对于以下代码:
*
*
*
*
* private static String buildQrChannel(HttpServletRequest request){
* // 添加二维码渠道
* String qrChannel = request.getParameter("_qr_channel");
* if (Strings.isNullOrEmpty(qrChannel)){
* return "0";
* }
* return qrChannel;
* }
*
*
*
* 可以重构成:
*
*
*
* private static String buildQrChannel(HttpServletRequest request){
* return RequestUtil.getParameterDefaultValue(request, "_qr_channel", "0");
* }
*
*
*
*
* @param request
* 当前请求
* @param paramName
* 参数名称
* @param defaultValue
* 如果参数值是空或者empty,使用默认值
* @return 获得request中的请求参数值
* @since 3.3.3
*/
public static String getParameterDefaultValue(HttpServletRequest request,String paramName,String defaultValue){
return defaultIfNullOrEmpty(getParameter(request, paramName), defaultValue);
}
/**
* 获取request body中的内容.
*
* 使用场景:
*
*
* wechat notify 的时候数据在 request body 里面,需要提取
*
*
* 说明:
*
*
* - request.getInputStream(); request.getReader();和request.getParameter("key");
* 这三个函数中任何一个函数执行一次后(可正常读取body数据),之后再执行就无效了。
*
*
*
* @param request
* the request
* @return the request body
* @see "org.springframework.web.bind.annotation.RequestBody"
* @see "org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor"
* @see Get the POST request
* body from HttpServletRequest
*
* @since 1.10.6
* @since 1.14.2 add 多次获取特性
* @since 3.5.1 如果出现异常返回空字符串 ""
*/
public static String getRequestBody(HttpServletRequest request){
//since 1.14.2
String requestBodyInRequestScope = getAttribute(request, REQUEST_BODY_SCOPE_ATTRIBUTE_NAME);
if (null != requestBodyInRequestScope){
//有就返回
return requestBodyInRequestScope;
}
//---------------------------------------------------------------
//解析
String requestBody = parseBody(request);
if (null != requestBody){
request.setAttribute(REQUEST_BODY_SCOPE_ATTRIBUTE_NAME, requestBody);
}
return requestBody;
}
/**
* Parses the body.
*
* @param request
* the request
* @return the string
* @since 1.14.2
* @since 3.5.1 如果出现异常返回空字符串 ""
*/
private static String parseBody(HttpServletRequest request){
try{
//Retrieves the body of the request as character data using a BufferedReader.
//The reader translates the character data according to the character encoding used on the body.
//Either this method or getInputStream may be called to read the body, not both.
BufferedReader reader = request.getReader();
return ReaderUtil.toString(reader);
}catch (Exception e){
//throw new UncheckedIOException(e);
LOGGER.warn("parseBodyException", e);
return EMPTY;
}
}
}