Feign Client使用@PatchMapping或PATCH方法报错:无效HTTP方法
java.net.ProtocolException: 无效的HTTP方法: PATCH
at java.base/java.net.HttpURLConnection.setRequestMethod(HttpURLConnection.java:489)
at java.base/sun.net.www.protocol.http.HttpURLConnection.setRequestMethod(HttpURLConnection.java:598)
at feign.Client$Default.convertAndSend(Client.java:171)
at feign.Client$Default.execute(Client.java:105)
at org.springframework.cloud.openfeign.loadbalancer.LoadBalancerUtils.executeWithLoadBalancerLifecycleProcessing(LoadBalancerUtils.java:56)
at org.springframework.cloud.openfeign.loadbalancer.RetryableFeignBlockingLoadBalancerClient.lambda$execute$2(RetryableFeignBlockingLoadBalancerClient.java:189)
at org.springframework.retry.support.RetryTemplate.doExecute(RetryTemplate.java:329)
at org.springframework.retry.support.RetryTemplate.execute(RetryTemplate.java:225)
at org.springframework.cloud.openfeign.loadbalancer.RetryableFeignBlockingLoadBalancerClient.execute(RetryableFeignBlockingLoadBalancerClient.java:136)
at feign.SynchronousMethodHandler.executeAndDecode(SynchronousMethodHandler.java:100)
... 183 more
问题原因
JDK原生的HttpURLConnection默认不支持PATCH HTTP方法,而Feign默认使用该客户端发送请求,因此调用PATCH接口时会触发此异常。
解决方案
方案1:切换Feign的HTTP客户端为OkHttp
步骤1:引入OkHttp依赖(Maven)
<dependency> <groupId>io.github.openfeign</groupId> <artifactId>feign-okhttp</artifactId> </dependency> <dependency> <groupId>com.squareup.okhttp3</groupId> <artifactId>okhttp</artifactId> </dependency>
步骤2:配置Feign启用OkHttp
在application.yml中添加配置:
feign: okhttp: enabled: true
方案2:切换为Apache HttpClient
步骤1:引入Apache HttpClient依赖(Maven)
<dependency> <groupId>io.github.openfeign</groupId> <artifactId>feign-httpclient</artifactId> </dependency>
步骤2:配置Feign启用Apache HttpClient
在application.yml中添加配置:
feign: httpclient: enabled: true
方案3:自定义Feign Client兼容PATCH方法(不换客户端)
如果不想更换HTTP客户端,可以通过自定义Feign Client,利用反射绕过HttpURLConnection的方法限制:
import feign.Client; import feign.Request; import feign.Response; import java.io.IOException; import java.lang.reflect.Field; import java.net.HttpURLConnection; import java.net.ProtocolException; public class PatchCompatibleFeignClient extends Client.Default { public PatchCompatibleFeignClient() { super(null, null); } @Override public Response execute(Request request, Request.Options options) throws IOException { if (request.method().equals("PATCH")) { return executePatchRequest(request, options); } return super.execute(request, options); } private Response executePatchRequest(Request request, Request.Options options) throws IOException { HttpURLConnection connection = (HttpURLConnection) request.url().openConnection(); configureConnection(connection, options); try { // 反射修改method字段,绕过HttpURLConnection的方法限制 Field methodField = HttpURLConnection.class.getDeclaredField("method"); methodField.setAccessible(true); methodField.set(connection, "PATCH"); } catch (NoSuchFieldException | IllegalAccessException e) { throw new ProtocolException("Failed to set PATCH method", e); } if (request.body() != null) { connection.setDoOutput(true); connection.addRequestHeader(request.headers().get("Content-Type").iterator().next(), request.body().toString()); request.body().writeTo(connection.getOutputStream()); } return super.convertResponse(connection, request); } }
然后在配置类中注册这个自定义Client:
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class FeignConfig { @Bean public Client feignClient() { return new PatchCompatibleFeignClient(); } }
内容的提问来源于stack exchange,提问作者Santhoshkannan

