mirror of
https://gitee.com/chinabugotech/hutool.git
synced 2025-07-21 15:09:48 +08:00
prepare 5.3.0
This commit is contained in:
@@ -28,10 +28,18 @@ public enum ContentType {
|
||||
* Rest请求XML编码
|
||||
*/
|
||||
XML("application/xml"),
|
||||
/**
|
||||
* text/plain编码
|
||||
*/
|
||||
TEXT_PLAIN("text/plain"),
|
||||
/**
|
||||
* Rest请求text/xml编码
|
||||
*/
|
||||
TEXT_XML("text/xml");
|
||||
TEXT_XML("text/xml"),
|
||||
/**
|
||||
* text/html编码
|
||||
*/
|
||||
TEXT_HTML("text/html");
|
||||
|
||||
private String value;
|
||||
|
||||
@@ -39,9 +47,19 @@ public enum ContentType {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取value值
|
||||
*
|
||||
* @return value值
|
||||
* @since 5.2.6
|
||||
*/
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return value;
|
||||
return getValue();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -62,7 +80,7 @@ public enum ContentType {
|
||||
* @since 4.1.5
|
||||
*/
|
||||
public static boolean isDefault(String contentType) {
|
||||
return null == contentType || isFormUrlEncoed(contentType);
|
||||
return null == contentType || isFormUrlEncode(contentType);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -71,7 +89,7 @@ public enum ContentType {
|
||||
* @param contentType 内容类型
|
||||
* @return 是否为application/x-www-form-urlencoded
|
||||
*/
|
||||
public static boolean isFormUrlEncoed(String contentType) {
|
||||
public static boolean isFormUrlEncode(String contentType) {
|
||||
return StrUtil.startWithIgnoreCase(contentType, FORM_URLENCODED.toString());
|
||||
}
|
||||
|
||||
|
@@ -2,6 +2,7 @@ package cn.hutool.http;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import cn.hutool.core.collection.IterUtil;
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import cn.hutool.core.io.FastByteArrayOutputStream;
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
@@ -416,7 +417,7 @@ public class HttpUtil {
|
||||
if (value instanceof Iterable) {
|
||||
value = CollUtil.join((Iterable<?>) value, ",");
|
||||
} else if (value instanceof Iterator) {
|
||||
value = CollUtil.join((Iterator<?>) value, ",");
|
||||
value = IterUtil.join((Iterator<?>) value, ",");
|
||||
}
|
||||
valueStr = Convert.toStr(value);
|
||||
if (StrUtil.isNotEmpty(key)) {
|
||||
@@ -435,30 +436,33 @@ public class HttpUtil {
|
||||
*
|
||||
* <p>注意,此方法只能标准化整个URL,并不适合于单独编码参数值</p>
|
||||
*
|
||||
* @param paramsStr url参数,可以包含url本身
|
||||
* @param urlWithParams url和参数,可以包含url本身,也可以单独参数
|
||||
* @param charset 编码
|
||||
* @return 编码后的url和参数
|
||||
* @since 4.0.1
|
||||
*/
|
||||
public static String encodeParams(String paramsStr, Charset charset) {
|
||||
if (StrUtil.isBlank(paramsStr)) {
|
||||
public static String encodeParams(String urlWithParams, Charset charset) {
|
||||
if (StrUtil.isBlank(urlWithParams)) {
|
||||
return StrUtil.EMPTY;
|
||||
}
|
||||
|
||||
String urlPart = null; // url部分,不包括问号
|
||||
String paramPart; // 参数部分
|
||||
int pathEndPos = paramsStr.indexOf('?');
|
||||
final int pathEndPos = urlWithParams.indexOf('?');
|
||||
if (pathEndPos > -1) {
|
||||
// url + 参数
|
||||
urlPart = StrUtil.subPre(paramsStr, pathEndPos);
|
||||
paramPart = StrUtil.subSuf(paramsStr, pathEndPos + 1);
|
||||
urlPart = StrUtil.subPre(urlWithParams, pathEndPos);
|
||||
paramPart = StrUtil.subSuf(urlWithParams, pathEndPos + 1);
|
||||
if (StrUtil.isBlank(paramPart)) {
|
||||
// 无参数,返回url
|
||||
return urlPart;
|
||||
}
|
||||
} else {
|
||||
// 无URL
|
||||
paramPart = paramsStr;
|
||||
} else if(false == StrUtil.contains(urlWithParams, '=')){
|
||||
// 无参数的URL
|
||||
return urlWithParams;
|
||||
}else {
|
||||
// 无URL的参数
|
||||
paramPart = urlWithParams;
|
||||
}
|
||||
|
||||
paramPart = normalizeParams(paramPart, charset);
|
||||
@@ -534,6 +538,18 @@ public class HttpUtil {
|
||||
* @since 4.0.2
|
||||
*/
|
||||
public static HashMap<String, String> decodeParamMap(String paramsStr, String charset) {
|
||||
return decodeParamMap(paramsStr, CharsetUtil.charset(charset));
|
||||
}
|
||||
|
||||
/**
|
||||
* 将URL参数解析为Map(也可以解析Post中的键值对参数)
|
||||
*
|
||||
* @param paramsStr 参数字符串(或者带参数的Path)
|
||||
* @param charset 字符集
|
||||
* @return 参数Map
|
||||
* @since 5.2.6
|
||||
*/
|
||||
public static HashMap<String, String> decodeParamMap(String paramsStr, Charset charset) {
|
||||
final Map<String, List<String>> paramsMap = decodeParams(paramsStr, charset);
|
||||
final HashMap<String, String> result = MapUtil.newHashMap(paramsMap.size());
|
||||
List<String> valueList;
|
||||
@@ -552,6 +568,18 @@ public class HttpUtil {
|
||||
* @return 参数Map
|
||||
*/
|
||||
public static Map<String, List<String>> decodeParams(String paramsStr, String charset) {
|
||||
return decodeParams(paramsStr, CharsetUtil.charset(charset));
|
||||
}
|
||||
|
||||
/**
|
||||
* 将URL参数解析为Map(也可以解析Post中的键值对参数)
|
||||
*
|
||||
* @param paramsStr 参数字符串(或者带参数的Path)
|
||||
* @param charset 字符集
|
||||
* @return 参数Map
|
||||
* @since 5.2.6
|
||||
*/
|
||||
public static Map<String, List<String>> decodeParams(String paramsStr, Charset charset) {
|
||||
if (StrUtil.isBlank(paramsStr)) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
@@ -811,7 +839,7 @@ public class HttpUtil {
|
||||
* @param value value
|
||||
* @param charset 编码
|
||||
*/
|
||||
private static void addParam(Map<String, List<String>> params, String name, String value, String charset) {
|
||||
private static void addParam(Map<String, List<String>> params, String name, String value, Charset charset) {
|
||||
name = URLUtil.decode(name, charset);
|
||||
value = URLUtil.decode(value, charset);
|
||||
final List<String> values = params.computeIfAbsent(name, k -> new ArrayList<>(1));
|
||||
|
@@ -1,7 +1,10 @@
|
||||
package cn.hutool.http.server;
|
||||
|
||||
import cn.hutool.core.util.CharsetUtil;
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
/**
|
||||
* HttpServer公用对象,提供HttpExchange包装和公用方法
|
||||
*
|
||||
@@ -10,6 +13,8 @@ import com.sun.net.httpserver.HttpExchange;
|
||||
*/
|
||||
public class HttpServerBase {
|
||||
|
||||
final static Charset DEFAULT_CHARSET = CharsetUtil.CHARSET_UTF_8;
|
||||
|
||||
final HttpExchange httpExchange;
|
||||
|
||||
/**
|
||||
|
@@ -1,9 +1,13 @@
|
||||
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.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;
|
||||
@@ -15,6 +19,7 @@ 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;
|
||||
@@ -32,6 +37,9 @@ import java.util.Map;
|
||||
public class HttpServerRequest extends HttpServerBase {
|
||||
|
||||
private Map<String, HttpCookie> cookieCache;
|
||||
private ListValueMap<String, String> paramsCache;
|
||||
private Charset charsetCache;
|
||||
private byte[] bodyCache;
|
||||
|
||||
/**
|
||||
* 构造
|
||||
@@ -159,9 +167,13 @@ public class HttpServerRequest extends HttpServerBase {
|
||||
* @return 编码,默认UTF-8
|
||||
*/
|
||||
public Charset getCharset() {
|
||||
final String contentType = getContentType();
|
||||
final String charsetStr = HttpUtil.getCharset(contentType);
|
||||
return CharsetUtil.parse(charsetStr, CharsetUtil.CHARSET_UTF_8);
|
||||
if(null == this.charsetCache){
|
||||
final String contentType = getContentType();
|
||||
final String charsetStr = HttpUtil.getCharset(contentType);
|
||||
this.charsetCache = CharsetUtil.parse(charsetStr, DEFAULT_CHARSET);
|
||||
}
|
||||
|
||||
return this.charsetCache;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -226,12 +238,20 @@ public class HttpServerRequest extends HttpServerBase {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取请求体的流,流中可以读取请求内容,包括请求表单数据或文件上传数据
|
||||
* 是否为Multipart类型表单,此类型表单用于文件上传
|
||||
*
|
||||
* @return 流
|
||||
* @return 是否为Multipart类型表单,此类型表单用于文件上传
|
||||
*/
|
||||
public InputStream getBodyStream() {
|
||||
return this.httpExchange.getRequestBody();
|
||||
public boolean isMultipart() {
|
||||
if (false == isPostMethod()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final String contentType = getContentType();
|
||||
if (StrUtil.isBlank(contentType)) {
|
||||
return false;
|
||||
}
|
||||
return contentType.toLowerCase().startsWith("multipart/");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -251,31 +271,49 @@ public class HttpServerRequest extends HttpServerBase {
|
||||
* @return 请求
|
||||
*/
|
||||
public String getBody(Charset charset) {
|
||||
InputStream in = null;
|
||||
try {
|
||||
in = getBodyStream();
|
||||
return IoUtil.read(in, charset);
|
||||
} finally {
|
||||
IoUtil.close(in);
|
||||
}
|
||||
return StrUtil.str(getBodyBytes(), charset);
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否为Multipart类型表单,此类型表单用于文件上传
|
||||
* 获取body的bytes数组
|
||||
*
|
||||
* @return 是否为Multipart类型表单,此类型表单用于文件上传
|
||||
* @return body的bytes数组
|
||||
*/
|
||||
public boolean isMultipart() {
|
||||
if (false == isPostMethod()) {
|
||||
return false;
|
||||
public byte[] getBodyBytes(){
|
||||
if(null == this.bodyCache){
|
||||
this.bodyCache = IoUtil.readBytes(getBodyStream(), true);
|
||||
}
|
||||
return this.bodyCache;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取请求体的流,流中可以读取请求内容,包括请求表单数据或文件上传数据
|
||||
*
|
||||
* @return 流
|
||||
*/
|
||||
public InputStream getBodyStream() {
|
||||
return this.httpExchange.getRequestBody();
|
||||
}
|
||||
|
||||
public ListValueMap<String, String> 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));
|
||||
}
|
||||
|
||||
// 解析body中的参数
|
||||
final String body = getBody();
|
||||
if(StrUtil.isNotBlank(body)){
|
||||
this.paramsCache.putAll(HttpUtil.decodeParams(body, charset));
|
||||
}
|
||||
}
|
||||
|
||||
final String contentType = getContentType();
|
||||
if (StrUtil.isBlank(contentType)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return contentType.toLowerCase().startsWith("multipart/");
|
||||
return this.paramsCache;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -332,4 +370,36 @@ public class HttpServerRequest extends HttpServerBase {
|
||||
ip = this.httpExchange.getRemoteAddress().getHostName();
|
||||
return NetUtil.getMultistageReverseProxyIp(ip);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得MultiPart表单内容,多用于获得上传的文件 在同一次请求中,此方法只能被执行一次!
|
||||
*
|
||||
* @return MultipartFormData
|
||||
* @throws IORuntimeException IO异常
|
||||
* @since 5.3.0
|
||||
*/
|
||||
public MultipartFormData getMultipart() throws IORuntimeException {
|
||||
return getMultipart(new UploadSetting());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得multipart/form-data 表单内容<br>
|
||||
* 包括文件和普通表单数据<br>
|
||||
* 在同一次请求中,此方法只能被执行一次!
|
||||
*
|
||||
* @param uploadSetting 上传文件的设定,包括最大文件大小、保存在内存的边界大小、临时目录、扩展名限定等
|
||||
* @return MultiPart表单
|
||||
* @throws IORuntimeException IO异常
|
||||
* @since 5.3.0
|
||||
*/
|
||||
public MultipartFormData getMultipart(UploadSetting uploadSetting) throws IORuntimeException {
|
||||
final MultipartFormData formData = new MultipartFormData(uploadSetting);
|
||||
try {
|
||||
formData.parseRequestStream(getBodyStream(), getCharset());
|
||||
} catch (IOException e) {
|
||||
throw new IORuntimeException(e);
|
||||
}
|
||||
|
||||
return formData;
|
||||
}
|
||||
}
|
||||
|
@@ -3,12 +3,14 @@ package cn.hutool.http.server;
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.core.io.IORuntimeException;
|
||||
import cn.hutool.core.io.IoUtil;
|
||||
import cn.hutool.core.util.CharsetUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.core.util.URLUtil;
|
||||
import cn.hutool.http.ContentType;
|
||||
import cn.hutool.http.Header;
|
||||
import cn.hutool.http.HttpStatus;
|
||||
import cn.hutool.http.HttpUtil;
|
||||
|
||||
import com.sun.net.httpserver.Headers;
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
|
||||
@@ -18,6 +20,8 @@ import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.PrintWriter;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -28,6 +32,10 @@ import java.util.Map;
|
||||
public class HttpServerResponse extends HttpServerBase {
|
||||
|
||||
private Charset charset;
|
||||
/**
|
||||
* 是否已经发送了Http状态码,如果没有,提前写出状态码
|
||||
*/
|
||||
private boolean isSendCode;
|
||||
|
||||
/**
|
||||
* 构造
|
||||
@@ -48,6 +56,38 @@ public class HttpServerResponse extends HttpServerBase {
|
||||
return send(httpStatusCode, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送成功状态码
|
||||
*
|
||||
* @return this
|
||||
*/
|
||||
public HttpServerResponse sendOk() {
|
||||
return send(HttpStatus.HTTP_OK);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送404错误页
|
||||
*
|
||||
* @param content 错误页页面内容,默认text/html类型
|
||||
* @return this
|
||||
*/
|
||||
public HttpServerResponse send404(String content) {
|
||||
return sendError(HttpStatus.HTTP_NOT_FOUND, content);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送错误页
|
||||
*
|
||||
* @param errorCode HTTP错误状态码,见HttpStatus
|
||||
* @param content 错误页页面内容,默认text/html类型
|
||||
* @return this
|
||||
*/
|
||||
public HttpServerResponse sendError(int errorCode, String content) {
|
||||
send(errorCode);
|
||||
setContentType(ContentType.TEXT_HTML.toString());
|
||||
return write(content);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送HTTP状态码
|
||||
*
|
||||
@@ -56,11 +96,17 @@ public class HttpServerResponse extends HttpServerBase {
|
||||
* @return this
|
||||
*/
|
||||
public HttpServerResponse send(int httpStatusCode, long bodyLength) {
|
||||
if (this.isSendCode) {
|
||||
throw new IORuntimeException("Http status code has been send!");
|
||||
}
|
||||
|
||||
try {
|
||||
this.httpExchange.sendResponseHeaders(httpStatusCode, bodyLength);
|
||||
} catch (IOException e) {
|
||||
throw new IORuntimeException(e);
|
||||
}
|
||||
|
||||
this.isSendCode = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -70,6 +116,9 @@ public class HttpServerResponse extends HttpServerBase {
|
||||
* @return 响应头
|
||||
*/
|
||||
public Headers getHeaders() {
|
||||
if (false == this.isSendCode) {
|
||||
sendOk();
|
||||
}
|
||||
return this.httpExchange.getResponseHeaders();
|
||||
}
|
||||
|
||||
@@ -141,7 +190,7 @@ public class HttpServerResponse extends HttpServerBase {
|
||||
public HttpServerResponse setContentType(String contentType) {
|
||||
if (null != contentType && null != this.charset) {
|
||||
if (false == contentType.contains(";charset=")) {
|
||||
contentType += ";charset=" + this.charset;
|
||||
contentType = ContentType.build(contentType, this.charset);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -169,12 +218,27 @@ public class HttpServerResponse extends HttpServerBase {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置属性
|
||||
*
|
||||
* @param name 属性名
|
||||
* @param value 属性值
|
||||
* @return this
|
||||
*/
|
||||
public HttpServerResponse setAttr(String name, Object value) {
|
||||
this.httpExchange.setAttribute(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取响应数据流
|
||||
*
|
||||
* @return 响应数据流
|
||||
*/
|
||||
public OutputStream getOut() {
|
||||
if (false == this.isSendCode) {
|
||||
sendOk();
|
||||
}
|
||||
return this.httpExchange.getResponseBody();
|
||||
}
|
||||
|
||||
@@ -183,8 +247,43 @@ public class HttpServerResponse extends HttpServerBase {
|
||||
*
|
||||
* @return 响应数据流
|
||||
*/
|
||||
public OutputStream getWriter() {
|
||||
return this.httpExchange.getResponseBody();
|
||||
public PrintWriter getWriter() {
|
||||
final Charset charset = ObjectUtil.defaultIfNull(this.charset, DEFAULT_CHARSET);
|
||||
return new PrintWriter(new OutputStreamWriter(getOut(), charset));
|
||||
}
|
||||
|
||||
/**
|
||||
* 写出数据到客户端
|
||||
*
|
||||
* @param data 数据
|
||||
* @return this
|
||||
*/
|
||||
public HttpServerResponse write(String data, String contentType) {
|
||||
setContentType(contentType);
|
||||
return write(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 写出数据到客户端
|
||||
*
|
||||
* @param data 数据
|
||||
* @return this
|
||||
*/
|
||||
public HttpServerResponse write(String data) {
|
||||
final Charset charset = ObjectUtil.defaultIfNull(this.charset, DEFAULT_CHARSET);
|
||||
return write(StrUtil.bytes(data, charset));
|
||||
}
|
||||
|
||||
/**
|
||||
* 写出数据到客户端
|
||||
*
|
||||
* @param data 数据
|
||||
* @param contentType 返回的类型
|
||||
* @return this
|
||||
*/
|
||||
public HttpServerResponse write(byte[] data, String contentType) {
|
||||
setContentType(contentType);
|
||||
return write(data);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -194,8 +293,19 @@ public class HttpServerResponse extends HttpServerBase {
|
||||
* @return this
|
||||
*/
|
||||
public HttpServerResponse write(byte[] data) {
|
||||
write(new ByteArrayInputStream(data));
|
||||
return this;
|
||||
return write(new ByteArrayInputStream(data));
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回数据给客户端
|
||||
*
|
||||
* @param in 需要返回客户端的内容
|
||||
* @param contentType 返回的类型
|
||||
* @since 5.2.6
|
||||
*/
|
||||
public HttpServerResponse write(InputStream in, String contentType) {
|
||||
setContentType(contentType);
|
||||
return write(in);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -236,7 +346,7 @@ public class HttpServerResponse extends HttpServerBase {
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回数据给客户端
|
||||
* 返回文件数据给客户端(文件下载)
|
||||
*
|
||||
* @param in 需要返回客户端的内容
|
||||
* @param contentType 返回的类型
|
||||
@@ -244,7 +354,7 @@ public class HttpServerResponse extends HttpServerBase {
|
||||
* @since 5.2.6
|
||||
*/
|
||||
public void write(InputStream in, String contentType, String fileName) {
|
||||
final Charset charset = ObjectUtil.defaultIfNull(this.charset, CharsetUtil.CHARSET_UTF_8);
|
||||
final Charset charset = ObjectUtil.defaultIfNull(this.charset, DEFAULT_CHARSET);
|
||||
setHeader("Content-Disposition", StrUtil.format("attachment;filename={}", URLUtil.encode(fileName, charset)));
|
||||
setContentType(contentType);
|
||||
write(in);
|
||||
|
@@ -1,9 +1,10 @@
|
||||
package cn.hutool.http.server;
|
||||
|
||||
import cn.hutool.core.io.IORuntimeException;
|
||||
import cn.hutool.core.lang.Console;
|
||||
import cn.hutool.http.server.action.Action;
|
||||
import cn.hutool.http.server.action.RootAction;
|
||||
import cn.hutool.http.server.handler.ActionHandler;
|
||||
import cn.hutool.http.server.handler.RootHandler;
|
||||
import com.sun.net.httpserver.HttpHandler;
|
||||
import com.sun.net.httpserver.HttpServer;
|
||||
|
||||
@@ -72,7 +73,7 @@ public class SimpleServer {
|
||||
* @return this
|
||||
*/
|
||||
public SimpleServer setRoot(String root) {
|
||||
return addHandler("/", new RootHandler(root));
|
||||
return addAction("/", new RootAction(root));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -119,6 +120,8 @@ public class SimpleServer {
|
||||
* 启动Http服务器,启动后会阻塞当前线程
|
||||
*/
|
||||
public void start() {
|
||||
final InetSocketAddress address = getAddress();
|
||||
Console.log("Hutool Simple Http Server listen on 【{}:{}】", address.getHostName(), address.getPort());
|
||||
this.server.start();
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,62 @@
|
||||
package cn.hutool.http.server.action;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.http.server.HttpServerRequest;
|
||||
import cn.hutool.http.server.HttpServerResponse;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 默认的处理器,通过解析用户传入的path,找到网页根目录下对应文件后返回
|
||||
*
|
||||
* @author looly
|
||||
* @since 5.2.6
|
||||
*/
|
||||
public class RootAction implements Action {
|
||||
|
||||
public static final String DEFAULT_INDEX_FILE_NAME = "index.html";
|
||||
|
||||
private final String rootDir;
|
||||
private List<String> indexFileNames;
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param rootDir 网页根目录
|
||||
*/
|
||||
public RootAction(String rootDir) {
|
||||
this(rootDir, DEFAULT_INDEX_FILE_NAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param rootDir 网页根目录
|
||||
* @param indexFileNames 主页文件名列表
|
||||
*/
|
||||
public RootAction(String rootDir, String... indexFileNames) {
|
||||
this.rootDir = rootDir;
|
||||
this.indexFileNames = CollUtil.toList(indexFileNames);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doAction(HttpServerRequest request, HttpServerResponse response) {
|
||||
final String path = request.getPath();
|
||||
File file = FileUtil.file(rootDir, path);
|
||||
if (file.exists()) {
|
||||
if (file.isDirectory()) {
|
||||
for (String indexFileName : indexFileNames) {
|
||||
//默认读取主页
|
||||
file = FileUtil.file(file, indexFileName);
|
||||
if (file.exists() && file.isFile()) {
|
||||
response.write(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
response.send404("404 Not Found !");
|
||||
}
|
||||
}
|
@@ -27,6 +27,9 @@ public class ActionHandler implements HttpHandler {
|
||||
|
||||
@Override
|
||||
public void handle(HttpExchange httpExchange) {
|
||||
action.doAction(new HttpServerRequest(httpExchange), new HttpServerResponse(httpExchange));
|
||||
action.doAction(
|
||||
new HttpServerRequest(httpExchange),
|
||||
new HttpServerResponse(httpExchange)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -1,91 +0,0 @@
|
||||
package cn.hutool.http.server.handler;
|
||||
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.core.io.IoUtil;
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.hutool.http.Header;
|
||||
import cn.hutool.http.HttpStatus;
|
||||
import cn.hutool.http.HttpUtil;
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
/**
|
||||
* 请求处理器相关工具类
|
||||
*
|
||||
* @since 5.2.6
|
||||
*/
|
||||
public class HandlerUtil {
|
||||
|
||||
/**
|
||||
* 返回404页面
|
||||
*
|
||||
* @param httpExchange HttpExchange
|
||||
* @param content 要发送的404页面内容
|
||||
* @throws IOException IO异常
|
||||
*/
|
||||
public static void send404(HttpExchange httpExchange, String content) throws IOException {
|
||||
if (null == httpExchange) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (null == content) {
|
||||
content = "404 Not Found !";
|
||||
}
|
||||
|
||||
httpExchange.sendResponseHeaders(HttpStatus.HTTP_NOT_FOUND, 0);
|
||||
try (OutputStream out = httpExchange.getResponseBody()) {
|
||||
IoUtil.writeUtf8(out, false, content);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回文件
|
||||
*
|
||||
* @param httpExchange HttpExchange
|
||||
* @param file 要发送的文件
|
||||
* @throws IOException IO异常
|
||||
*/
|
||||
public static void sendFile(HttpExchange httpExchange, File file) throws IOException {
|
||||
if (ArrayUtil.hasNull(httpExchange, file)) {
|
||||
return;
|
||||
}
|
||||
addHeader(httpExchange,
|
||||
Header.CONTENT_TYPE.toString(),
|
||||
HttpUtil.getMimeType(file.getName(), "text/html"));
|
||||
httpExchange.sendResponseHeaders(HttpStatus.HTTP_OK, 0);
|
||||
try (OutputStream out = httpExchange.getResponseBody()) {
|
||||
FileUtil.writeToStream(file, out);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 增加响应头信息
|
||||
*
|
||||
* @param httpExchange HttpExchange
|
||||
* @param header 头名
|
||||
* @param value 头值
|
||||
*/
|
||||
public static void addHeader(HttpExchange httpExchange, String header, String value) {
|
||||
if (ArrayUtil.hasEmpty(httpExchange, header)) {
|
||||
return;
|
||||
}
|
||||
httpExchange.getResponseHeaders().add(header, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取响应头信息
|
||||
*
|
||||
* @param httpExchange HttpExchange
|
||||
* @param header 头名
|
||||
* @return 值,不存在返回null
|
||||
*/
|
||||
public static String getHeader(HttpExchange httpExchange, String header) {
|
||||
if (ArrayUtil.hasEmpty(httpExchange, header)) {
|
||||
return null;
|
||||
}
|
||||
return httpExchange.getRequestHeaders().getFirst(header);
|
||||
}
|
||||
}
|
@@ -1,45 +0,0 @@
|
||||
package cn.hutool.http.server.handler;
|
||||
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
import com.sun.net.httpserver.HttpHandler;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
|
||||
/**
|
||||
* 默认的处理器,通过解析用户传入的path,找到网页根目录下对应文件后返回
|
||||
*
|
||||
* @author looly
|
||||
* @since 5.2.6
|
||||
*/
|
||||
public class RootHandler implements HttpHandler {
|
||||
|
||||
private final String rootDir;
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param rootDir 网页根目录
|
||||
*/
|
||||
public RootHandler(String rootDir) {
|
||||
this.rootDir = rootDir;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(HttpExchange httpExchange) throws IOException {
|
||||
final URI uri = httpExchange.getRequestURI();
|
||||
File file = FileUtil.file(rootDir, uri.getPath());
|
||||
if (file.exists()) {
|
||||
if (file.isDirectory()) {
|
||||
//默认读取主页
|
||||
file = FileUtil.file(file, "index.html");
|
||||
}
|
||||
HandlerUtil.sendFile(httpExchange, file);
|
||||
}
|
||||
|
||||
// 文件未找到
|
||||
HandlerUtil.send404(httpExchange, null);
|
||||
}
|
||||
}
|
@@ -1,13 +1,23 @@
|
||||
package cn.hutool.http.server;
|
||||
|
||||
import cn.hutool.http.ContentType;
|
||||
import cn.hutool.http.HttpUtil;
|
||||
import cn.hutool.http.server.handler.RootHandler;
|
||||
|
||||
public class SimpleServerTest {
|
||||
|
||||
public static void main(String[] args) {
|
||||
HttpUtil.createServer(8888)
|
||||
.addHandler("/", new RootHandler("D:\\test"))
|
||||
// 设置默认根目录,
|
||||
.setRoot("d:/test")
|
||||
// 返回JSON数据测试
|
||||
.addAction("/restTest", (request, response) ->
|
||||
response.write("{\"id\": 1, \"msg\": \"OK\"}", ContentType.JSON.toString())
|
||||
)
|
||||
// 获取表单数据测试
|
||||
// http://localhost:8888/formTest?a=1&a=2&b=3
|
||||
.addAction("/formTest", (request, response) ->
|
||||
response.write(request.getParams().toString(), ContentType.TEXT_PLAIN.toString())
|
||||
)
|
||||
.start();
|
||||
}
|
||||
}
|
||||
|
@@ -1,13 +1,5 @@
|
||||
package cn.hutool.http.test;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.core.lang.Console;
|
||||
import cn.hutool.core.util.CharsetUtil;
|
||||
@@ -15,6 +7,13 @@ import cn.hutool.core.util.ReUtil;
|
||||
import cn.hutool.http.Header;
|
||||
import cn.hutool.http.HttpRequest;
|
||||
import cn.hutool.http.HttpUtil;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class HttpUtilTest {
|
||||
|
||||
@@ -164,6 +163,16 @@ public class HttpUtilTest {
|
||||
paramsStr = "a=bbb&c=你好&哈喽&";
|
||||
encode = HttpUtil.encodeParams(paramsStr, CharsetUtil.CHARSET_UTF_8);
|
||||
Assert.assertEquals("a=bbb&c=%E4%BD%A0%E5%A5%BD&%E5%93%88%E5%96%BD=", encode);
|
||||
|
||||
// URL原样输出
|
||||
paramsStr = "https://www.hutool.cn/";
|
||||
encode = HttpUtil.encodeParams(paramsStr, CharsetUtil.CHARSET_UTF_8);
|
||||
Assert.assertEquals(paramsStr, encode);
|
||||
|
||||
// URL原样输出
|
||||
paramsStr = "https://www.hutool.cn/?";
|
||||
encode = HttpUtil.encodeParams(paramsStr, CharsetUtil.CHARSET_UTF_8);
|
||||
Assert.assertEquals("https://www.hutool.cn/", encode);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
Reference in New Issue
Block a user