Feign报错Method Not Allowed 405 5种解决方案

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

一问题产生背景

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:

feign:
  httpclient:
    enabled: true

maven:

  <!--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;
    }
}

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6