一,问题产生背景
Feign发送Get请求时,采用POJO传递参数 Method Not Allowed 405
@FeignClient("microservice-provider-user")
public interface UserFeignClient {
@RequestMapping(value = "/user", method = RequestMethod.GET)
public PageBean<User> get(User user);
}
二,问题产生原因
sun.net.www.protocol.http;
public class HttpURLConnection extends java.net.HttpURLConnection {
// 这里很简单:只要你doOutput = true了,你是get请求的话也会强制给你转为POST请求
private synchronized OutputStream getOutputStream0() throws IOException {
try {
if (!this.doOutput) {
throw new ProtocolException("cannot write to a URLConnection if doOutput=false - call setDoOutput(true)");
} else {
if (this.method.equals("GET")) {
this.method = "POST";
}
}
...
}
}
这段代码是在 HttpURLConnection 中发现的,jdk原生的http连接请求工具类,原来是因为Feign默认使用的连接工具实现类,所以里面发现只要你有body体对象,就会强制的把get请求转换成POST请求。
三,解决方案
1,方案一使用Map 接收参数
量大的话改的东西多
使用@Validated验证的时候不支持了,需要手动调用校验方法
@FeignClient("microservice-provider-user")
public interface UserFeignClient {
//@RequestParam不要指定参数
@RequestMapping(value = "/user", method = RequestMethod.GET)
public PageBean<User> get(@RequestParam Map<String,Object> param);
}
2,方案二不用实体接收,改为单独的参数
量大的话改的东西多
@FeignClient("microservice-provider-user")
public interface UserFeignClient {
@RequestMapping(value = "/user", method = RequestMethod.GET)
public PageBean<User> get(@RequestParam("name") String name,@RequestParam("age") int age);
}
3,方案三修改feign的请求工具
默认的是jdk的,可以修改为okhttp 或者 httpclent
yml:文章来源:https://www.toymoban.com/news/detail-773209.html
feign:
httpclient:
enabled: true
maven:文章来源地址https://www.toymoban.com/news/detail-773209.html
<!--httpClient-->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.3</version>
</dependency>
<dependency>
<groupId>com.netflix.feign</groupId>
<artifactId>feign-httpclient</artifactId>
<version>8.17.0</version>
</dependency>
四,方案四添加RequestInterceptor 拦截器
package com.mugua.demo.config;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import feign.Request;
import feign.RequestInterceptor;
import feign.RequestTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import java.io.IOException;
import java.util.*;
/**
* GET 405
*
* @author liwenchao
*/
@Component
public class FeignRequestParamInterceptor implements RequestInterceptor {
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
@Override
public void apply(RequestTemplate template) {
// feign 不支持 GET 方法传 POJO, json body转query
if ("GET".equals(template.method()) && template.requestBody() != null) {
try {
JsonNode jsonNode = OBJECT_MAPPER.readTree(template.requestBody().asBytes());
template.body(Request.Body.empty());
Map<String, Collection<String>> queries = new HashMap<>();
buildQuery(jsonNode, "", queries);
template.queries(queries);
} catch (IOException e) {
e.printStackTrace();
}
}
}
private void buildQuery(JsonNode jsonNode, String path, Map<String, Collection<String>> queries) {
// 叶子节点
if (!jsonNode.isContainerNode()) {
if (jsonNode.isNull()) {
return;
}
Collection<String> values = queries.computeIfAbsent(path, k -> new ArrayList<>());
values.add(jsonNode.asText());
return;
}
// 数组节点
if (jsonNode.isArray()) {
Iterator<JsonNode> it = jsonNode.elements();
while (it.hasNext()) {
buildQuery(it.next(), path, queries);
}
} else {
Iterator<Map.Entry<String, JsonNode>> it = jsonNode.fields();
while (it.hasNext()) {
Map.Entry<String, JsonNode> entry = it.next();
if (StringUtils.hasText(path)) {
buildQuery(entry.getValue(), path + "." + entry.getKey(), queries);
} else { // 根节点
buildQuery(entry.getValue(), entry.getKey(), queries);
}
}
}
}
}
5,方案五重写SpringMvcContract
package com.mugua.demo.config;
import feign.MethodMetadata;
import org.springframework.cloud.openfeign.AnnotatedParameterProcessor;
import org.springframework.cloud.openfeign.support.SpringMvcContract;
import org.springframework.core.convert.ConversionService;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestBody;
import java.lang.annotation.Annotation;
import java.util.List;
/**
* GET 405
*
* @author lwc
*/
@Component
public class SpringMvcPojoObjectQueryContract extends SpringMvcContract {
public SpringMvcPojoObjectQueryContract(List<AnnotatedParameterProcessor> annotatedParameterProcessors, ConversionService conversionService) {
super(annotatedParameterProcessors, conversionService);
}
@Override
protected boolean processAnnotationsOnParameter(MethodMetadata data, Annotation[] annotations, int paramIndex) {
boolean httpAnnotation = super.processAnnotationsOnParameter(data, annotations, paramIndex);
//在springMvc中如果是Get请求且参数中是对象 没有声明为@RequestBody 则默认为Param
if (!httpAnnotation && "GET".equalsIgnoreCase(data.template().method())) {
for (Annotation parameterAnnotation : annotations) {
if (!(parameterAnnotation instanceof RequestBody)) {
return false;
}
}
data.queryMapIndex(paramIndex);
return true;
}
return httpAnnotation;
}
}
到了这里,关于Feign报错Method Not Allowed 405 5种解决方案的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!