Browse Source

add patch support

Looly 5 years ago
parent
commit
405a92cfe4

+ 1 - 0
CHANGELOG.md

@@ -10,6 +10,7 @@
 * 【core  】     增加ReflectUtil.getFieldValue支持Alias注解
 * 【core  】     Bean字段支持Alias注解(包括转map,转bean等)
 * 【core  】     增加ValueListHandler,优化结果集获取方式
+* 【http  】     支持patch方法(issue#666@Github)
 
 ### Bug修复
 

+ 2 - 0
hutool-core/src/main/java/cn/hutool/core/date/DateUtil.java

@@ -1849,7 +1849,9 @@ public class DateUtil {
 	 * @param checkDate 检查时间,可以是当前时间,既
 	 * @return 是否过期
 	 * @since 5.1.1
+	 * @deprecated 使用isIn方法
 	 */
+	@Deprecated
 	public static boolean isExpired(Date startDate, Date endDate, Date checkDate) {
 		return betweenMs(startDate, checkDate) > betweenMs(startDate, endDate);
 	}

+ 6 - 7
hutool-core/src/main/java/cn/hutool/core/util/ReflectUtil.java

@@ -294,7 +294,7 @@ public class ReflectUtil {
 	/**
 	 * 设置字段值
 	 *
-	 * @param obj       对象
+	 * @param obj       对象,static字段则此处传Class
 	 * @param fieldName 字段名
 	 * @param value     值,值类型必须与字段类型匹配,不会自动转换对象类型
 	 * @throws UtilException 包装IllegalAccessException异常
@@ -303,7 +303,7 @@ public class ReflectUtil {
 		Assert.notNull(obj);
 		Assert.notBlank(fieldName);
 
-		final Field field = getField(obj.getClass(), fieldName);
+		final Field field = getField((obj instanceof Class) ? (Class<?>)obj : obj.getClass(), fieldName);
 		Assert.notNull(field, "Field [{}] is not exist in [{}]", fieldName, obj.getClass().getName());
 		setFieldValue(obj, field, value);
 	}
@@ -317,9 +317,7 @@ public class ReflectUtil {
 	 * @throws UtilException UtilException 包装IllegalAccessException异常
 	 */
 	public static void setFieldValue(Object obj, Field field, Object value) throws UtilException {
-		Assert.notNull(field, "Field in [{}] not exist !", obj.getClass().getName());
-
-		setAccessible(field);
+		Assert.notNull(field, "Field in [{}] not exist !", obj);
 
 		if (null != value) {
 			Class<?> fieldType = field.getType();
@@ -332,10 +330,11 @@ public class ReflectUtil {
 			}
 		}
 
+		setAccessible(field);
 		try {
-			field.set(obj, value);
+			field.set(obj instanceof Class ? null : obj, value);
 		} catch (IllegalAccessException e) {
-			throw new UtilException(e, "IllegalAccess for {}.{}", obj.getClass(), field.getName());
+			throw new UtilException(e, "IllegalAccess for {}.{}", obj, field.getName());
 		}
 	}
 

+ 8 - 0
hutool-core/src/test/java/cn/hutool/core/date/DateUtilTest.java

@@ -83,6 +83,14 @@ public class DateUtilTest {
 	}
 
 	@Test
+	public void truncateTest(){
+		String dateStr2 = "2020-02-29 12:59:34";
+		Date date2 = DateUtil.parse(dateStr2);
+		final DateTime dateTime = DateUtil.truncate(date2, DateField.MINUTE);
+		Assert.assertEquals("2020-02-29 12:59:00", dateTime.toString());
+	}
+
+	@Test
 	public void beginAndWeedTest() {
 		String dateStr = "2017-03-01 22:33:23";
 		DateTime date = DateUtil.parse(dateStr);

+ 86 - 60
hutool-http/src/main/java/cn/hutool/http/HttpConnection.java

@@ -2,11 +2,10 @@ package cn.hutool.http;
 
 import cn.hutool.core.map.MapUtil;
 import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.core.util.ReflectUtil;
 import cn.hutool.core.util.StrUtil;
 import cn.hutool.core.util.URLUtil;
-import cn.hutool.http.ssl.AndroidSupportSSLFactory;
 import cn.hutool.http.ssl.DefaultSSLInfo;
-import cn.hutool.http.ssl.SSLSocketFactoryBuilder;
 
 import javax.net.ssl.HostnameVerifier;
 import javax.net.ssl.HttpsURLConnection;
@@ -14,6 +13,8 @@ import javax.net.ssl.SSLSocketFactory;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
 import java.net.HttpURLConnection;
 import java.net.ProtocolException;
 import java.net.Proxy;
@@ -21,17 +22,14 @@ import java.net.URL;
 import java.net.URLConnection;
 import java.nio.charset.Charset;
 import java.nio.charset.UnsupportedCharsetException;
-import java.security.KeyManagementException;
-import java.security.NoSuchAlgorithmException;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
 
 /**
  * http连接对象,对HttpURLConnection的包装
- * 
- * @author Looly
  *
+ * @author Looly
  */
 public class HttpConnection {
 
@@ -41,9 +39,9 @@ public class HttpConnection {
 
 	/**
 	 * 创建HttpConnection
-	 * 
+	 *
 	 * @param urlStr URL
-	 * @param proxy 代理,无代理传{@code null}
+	 * @param proxy  代理,无代理传{@code null}
 	 * @return HttpConnection
 	 */
 	public static HttpConnection create(String urlStr, Proxy proxy) {
@@ -52,8 +50,8 @@ public class HttpConnection {
 
 	/**
 	 * 创建HttpConnection
-	 * 
-	 * @param url URL
+	 *
+	 * @param url   URL
 	 * @param proxy 代理,无代理传{@code null}
 	 * @return HttpConnection
 	 */
@@ -62,10 +60,11 @@ public class HttpConnection {
 	}
 
 	// --------------------------------------------------------------- Constructor start
+
 	/**
 	 * 构造HttpConnection
-	 * 
-	 * @param url URL
+	 *
+	 * @param url   URL
 	 * @param proxy 代理
 	 */
 	public HttpConnection(URL url, Proxy proxy) {
@@ -80,7 +79,7 @@ public class HttpConnection {
 
 	/**
 	 * 初始化连接相关信息
-	 * 
+	 *
 	 * @return HttpConnection
 	 * @since 4.4.1
 	 */
@@ -98,10 +97,11 @@ public class HttpConnection {
 	}
 
 	// --------------------------------------------------------------- Getters And Setters start
+
 	/**
 	 * 获取请求方法,GET/POST
-	 * 
-	 * @return 请求方法,GET/POST
+	 *
+	 * @return 请求方法, GET/POST
 	 */
 	public Method getMethod() {
 		return Method.valueOf(this.conn.getRequestMethod());
@@ -109,11 +109,23 @@ public class HttpConnection {
 
 	/**
 	 * 设置请求方法
-	 * 
+	 *
 	 * @param method 请求方法
 	 * @return 自己
 	 */
 	public HttpConnection setMethod(Method method) {
+		if (Method.POST.equals(method) //
+				|| Method.PUT.equals(method)//
+				|| Method.PATCH.equals(method)//
+				|| Method.DELETE.equals(method)) {
+			this.conn.setUseCaches(false);
+
+			// 增加PATCH方法支持
+			if (Method.PATCH.equals(method)) {
+				allowPatch();
+			}
+		}
+
 		// method
 		try {
 			this.conn.setRequestMethod(method.toString());
@@ -121,18 +133,12 @@ public class HttpConnection {
 			throw new HttpException(e);
 		}
 
-		if (Method.POST.equals(method) //
-				|| Method.PUT.equals(method)//
-				|| Method.PATCH.equals(method)//
-				|| Method.DELETE.equals(method)) {
-			this.conn.setUseCaches(false);
-		}
 		return this;
 	}
 
 	/**
 	 * 获取请求URL
-	 * 
+	 *
 	 * @return 请求URL
 	 */
 	public URL getUrl() {
@@ -141,7 +147,7 @@ public class HttpConnection {
 
 	/**
 	 * 获得代理
-	 * 
+	 *
 	 * @return {@link Proxy}
 	 */
 	public Proxy getProxy() {
@@ -150,7 +156,7 @@ public class HttpConnection {
 
 	/**
 	 * 获取HttpURLConnection对象
-	 * 
+	 *
 	 * @return HttpURLConnection
 	 */
 	public HttpURLConnection getHttpURLConnection() {
@@ -160,12 +166,13 @@ public class HttpConnection {
 	// --------------------------------------------------------------- Getters And Setters end
 
 	// ---------------------------------------------------------------- Headers start
+
 	/**
 	 * 设置请求头<br>
 	 * 当请求头存在时,覆盖之
-	 * 
-	 * @param header 头名
-	 * @param value 头值
+	 *
+	 * @param header     头名
+	 * @param value      头值
 	 * @param isOverride 是否覆盖旧值
 	 * @return HttpConnection
 	 */
@@ -184,9 +191,9 @@ public class HttpConnection {
 	/**
 	 * 设置请求头<br>
 	 * 当请求头存在时,覆盖之
-	 * 
-	 * @param header 头名
-	 * @param value 头值
+	 *
+	 * @param header     头名
+	 * @param value      头值
 	 * @param isOverride 是否覆盖旧值
 	 * @return HttpConnection
 	 */
@@ -197,8 +204,8 @@ public class HttpConnection {
 	/**
 	 * 设置请求头<br>
 	 * 不覆盖原有请求头
-	 * 
-	 * @param headerMap 请求头
+	 *
+	 * @param headerMap  请求头
 	 * @param isOverride 是否覆盖
 	 * @return this
 	 */
@@ -217,7 +224,7 @@ public class HttpConnection {
 
 	/**
 	 * 获取Http请求头
-	 * 
+	 *
 	 * @param name Header名
 	 * @return Http请求头值
 	 */
@@ -227,7 +234,7 @@ public class HttpConnection {
 
 	/**
 	 * 获取Http请求头
-	 * 
+	 *
 	 * @param name Header名
 	 * @return Http请求头值
 	 */
@@ -237,7 +244,7 @@ public class HttpConnection {
 
 	/**
 	 * 获取所有Http请求头
-	 * 
+	 *
 	 * @return Http请求头Map
 	 */
 	public Map<String, List<String>> headers() {
@@ -249,9 +256,9 @@ public class HttpConnection {
 	/**
 	 * 设置https请求参数<br>
 	 * 有些时候htts请求会出现com.sun.net.ssl.internal.www.protocol.https.HttpsURLConnectionOldImpl的实现,此为sun内部api,按照普通http请求处理
-	 * 
+	 *
 	 * @param hostnameVerifier 域名验证器,非https传入null
-	 * @param ssf SSLSocketFactory,非https传入null
+	 * @param ssf              SSLSocketFactory,非https传入null
 	 * @return this
 	 * @throws HttpException KeyManagementException和NoSuchAlgorithmException异常包装
 	 */
@@ -271,9 +278,9 @@ public class HttpConnection {
 
 	/**
 	 * 关闭缓存
-	 * 
+	 *
 	 * @return this
-	 * @see HttpURLConnection#setUseCaches(boolean) 
+	 * @see HttpURLConnection#setUseCaches(boolean)
 	 */
 	public HttpConnection disableCache() {
 		this.conn.setUseCaches(false);
@@ -282,7 +289,7 @@ public class HttpConnection {
 
 	/**
 	 * 设置连接超时
-	 * 
+	 *
 	 * @param timeout 超时
 	 * @return this
 	 */
@@ -296,7 +303,7 @@ public class HttpConnection {
 
 	/**
 	 * 设置读取超时
-	 * 
+	 *
 	 * @param timeout 超时
 	 * @return this
 	 */
@@ -310,7 +317,7 @@ public class HttpConnection {
 
 	/**
 	 * 设置连接和读取的超时时间
-	 * 
+	 *
 	 * @param timeout 超时时间
 	 * @return this
 	 */
@@ -323,7 +330,7 @@ public class HttpConnection {
 
 	/**
 	 * 设置Cookie
-	 * 
+	 *
 	 * @param cookie Cookie
 	 * @return this
 	 */
@@ -337,12 +344,12 @@ public class HttpConnection {
 	/**
 	 * 采用流方式上传数据,无需本地缓存数据。<br>
 	 * HttpUrlConnection默认是将所有数据读到本地缓存,然后再发送给服务器,这样上传大文件时就会导致内存溢出。
-	 * 
+	 *
 	 * @param blockSize 块大小(bytes数),0或小于0表示不设置Chuncked模式
 	 * @return this
 	 */
 	public HttpConnection setChunkedStreamingMode(int blockSize) {
-		if(blockSize > 0) {
+		if (blockSize > 0) {
 			conn.setChunkedStreamingMode(blockSize);
 		}
 		return this;
@@ -350,7 +357,7 @@ public class HttpConnection {
 
 	/**
 	 * 设置自动HTTP 30X跳转
-	 * 
+	 *
 	 * @param isInstanceFollowRedirects 是否自定跳转
 	 * @return this
 	 */
@@ -361,7 +368,7 @@ public class HttpConnection {
 
 	/**
 	 * 连接
-	 * 
+	 *
 	 * @return this
 	 * @throws IOException IO异常
 	 */
@@ -371,10 +378,10 @@ public class HttpConnection {
 		}
 		return this;
 	}
-	
+
 	/**
 	 * 静默断开连接。不抛出异常
-	 * 
+	 *
 	 * @return this
 	 * @since 4.6.0
 	 */
@@ -384,13 +391,13 @@ public class HttpConnection {
 		} catch (Throwable e) {
 			// ignore
 		}
-		
+
 		return this;
 	}
 
 	/**
 	 * 断开连接
-	 * 
+	 *
 	 * @return this
 	 */
 	public HttpConnection disconnect() {
@@ -403,7 +410,7 @@ public class HttpConnection {
 	/**
 	 * 获得输入流对象<br>
 	 * 输入流对象用于读取数据
-	 * 
+	 *
 	 * @return 输入流对象
 	 * @throws IOException IO异常
 	 */
@@ -416,7 +423,7 @@ public class HttpConnection {
 
 	/**
 	 * 当返回错误代码时,获得错误内容流
-	 * 
+	 *
 	 * @return 错误内容
 	 */
 	public InputStream getErrorStream() {
@@ -428,7 +435,7 @@ public class HttpConnection {
 
 	/**
 	 * 获取输出流对象 输出流对象用于发送数据
-	 * 
+	 *
 	 * @return OutputStream
 	 * @throws IOException IO异常
 	 */
@@ -444,7 +451,7 @@ public class HttpConnection {
 
 	/**
 	 * 获取响应码
-	 * 
+	 *
 	 * @return 响应码
 	 * @throws IOException IO异常
 	 */
@@ -459,7 +466,7 @@ public class HttpConnection {
 	 * 获得字符集编码<br>
 	 * 从Http连接的头信息中获得字符集<br>
 	 * 从ContentType中获取
-	 * 
+	 *
 	 * @return 字符集编码
 	 */
 	public String getCharsetName() {
@@ -470,7 +477,7 @@ public class HttpConnection {
 	 * 获取字符集编码<br>
 	 * 从Http连接的头信息中获得字符集<br>
 	 * 从ContentType中获取
-	 * 
+	 *
 	 * @return {@link Charset}编码
 	 * @since 3.0.9
 	 */
@@ -501,10 +508,11 @@ public class HttpConnection {
 	}
 
 	// --------------------------------------------------------------- Private Method start
+
 	/**
 	 * 初始化http或https请求参数<br>
 	 * 有些时候https请求会出现com.sun.net.ssl.internal.www.protocol.https.HttpsURLConnectionOldImpl的实现,此为sun内部api,按照普通http请求处理
-	 * 
+	 *
 	 * @return {@link HttpURLConnection},https返回{@link HttpsURLConnection}
 	 */
 	private HttpURLConnection openHttp() throws IOException {
@@ -519,12 +527,30 @@ public class HttpConnection {
 
 	/**
 	 * 建立连接
-	 * 
+	 *
 	 * @return {@link URLConnection}
 	 * @throws IOException IO异常
 	 */
 	private URLConnection openConnection() throws IOException {
 		return (null == this.proxy) ? url.openConnection() : url.openConnection(this.proxy);
 	}
+
+	/**
+	 * 增加支持的METHOD方法
+	 * see: https://stackoverflow.com/questions/25163131/httpurlconnection-invalid-http-method-patch
+	 *
+	 * @since 5.1.6
+	 */
+	private static void allowPatch() {
+		final Field methodsField = ReflectUtil.getField(HttpURLConnection.class, "methods");
+		if (null != methodsField) {
+			// 去除final修饰
+			ReflectUtil.setFieldValue(methodsField, "modifiers", methodsField.getModifiers() & ~Modifier.FINAL);
+			final String[] methods = {
+					"GET", "POST", "HEAD", "OPTIONS", "PUT", "DELETE", "TRACE", "PATCH"
+			};
+			ReflectUtil.setFieldValue(null, methodsField, methods);
+		}
+	}
 	// --------------------------------------------------------------- Private Method end
 }

+ 8 - 7
hutool-http/src/main/java/cn/hutool/http/HttpRequest.java

@@ -338,13 +338,14 @@ public class HttpRequest extends HttpBase<HttpRequest> {
 	 * @return HttpRequest
 	 */
 	public HttpRequest method(Method method) {
-		if (Method.PATCH == method) {
-			this.method = Method.POST;
-			this.header("X-HTTP-Method-Override", "PATCH");
-		} else {
-			this.method = method;
-		}
-
+//		if (Method.PATCH == method) {
+//			this.method = Method.POST;
+//			this.header("X-HTTP-Method-Override", "PATCH");
+//		} else {
+//			this.method = method;
+//		}
+
+		this.method = method;
 		return this;
 	}
 

+ 12 - 2
hutool-http/src/main/java/cn/hutool/http/HttpUtil.java

@@ -9,14 +9,24 @@ import cn.hutool.core.io.IoUtil;
 import cn.hutool.core.io.StreamProgress;
 import cn.hutool.core.map.MapUtil;
 import cn.hutool.core.text.StrBuilder;
-import cn.hutool.core.util.*;
+import cn.hutool.core.util.CharsetUtil;
+import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.core.util.ReUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.core.util.URLUtil;
 
 import java.io.File;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.net.HttpURLConnection;
 import java.nio.charset.Charset;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
 import java.util.Map.Entry;
 import java.util.regex.Pattern;
 

+ 1 - 1
hutool-http/src/main/java/cn/hutool/http/Method.java

@@ -2,8 +2,8 @@ package cn.hutool.http;
 
 /**
  * Http方法枚举
- * @author Looly
  *
+ * @author Looly
  */
 public enum Method {
 	GET, POST, HEAD, OPTIONS, PUT, DELETE, TRACE, CONNECT, PATCH

+ 1 - 1
hutool-http/src/test/java/cn/hutool/http/test/HttpUtilTest.java

@@ -220,7 +220,7 @@ public class HttpUtilTest {
 	@Test
 	@Ignore
 	public void patchTest() {
-		String body = HttpRequest.post("https://www.baidu.com").execute().body();
+		String body = HttpRequest.patch("https://www.baidu.com").execute().body();
 		Console.log(body);
 	}