返回顶部
首页 > 资讯 > 精选 >如何解决FeignClient发送post请求异常的问题
  • 641
分享到

如何解决FeignClient发送post请求异常的问题

2023-06-20 15:06:24 641人浏览 泡泡鱼
摘要

本篇内容介绍了“如何解决FeignClient发送post请求异常的问题”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!FeignClient

本篇内容介绍了“如何解决FeignClient发送post请求异常的问题”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!

FeignClient发送post请求异常

这个问题其实很基础。但是却难倒了我。记录一下

在发送post请求的时候要指定消息格式

正确的写法是这样

@PostMapping(value = "/test/post", consumes = "application/JSON") String test(@RequestBody String name);

不生效的写法

@PostMapping(value = "/test/post", produces= "application/json")

关于这个区别

produces:它的作用是指定返回值类型,不但可以设置返回值类型还可以设定返回值的字符编码;

consumes:指定处理请求的提交内容类型(Content-Type),例如application/json, text/html;

基础真的很重要啊~

FeignClient调用POST请求时查询参数被丢失的情况分析与处理

本文没有详细介绍 FeignClient 的知识点,网上有很多优秀的文章介绍了 FeignCient 的知识点,在这里本人就不重复了,只是专注在这个问题点上。

查询参数丢失场景

业务描述: 业务系统需要更新用户系统中的A资源,由于只想更新A资源的一个字段信息为B,所以没有选择通过 entity 封装B,而是直接通过查询参数来传递B信息

文字描述:使用FeignClient来进行远程调用时,如果POST请求中有查询参数并且没有请求实体(body为空),那么查询参数被丢失,服务提供者获取不到查询参数的值。

代码描述:B的值被丢失,服务提供者获取不到B的值

@FeignClient(name = "a-service", configuration = FeignConfiguration.class)public interface ACall {     @RequestMapping(method = RequestMethod.POST, value = "/api/xxx/{A}", headers = {"Content-Type=application/json"})    void updateAToB(@PathVariable("A") final String A, @RequestParam("B") final String B) throws Exception;}

问题分析

背景
  1. 使用 FeignClient 客户端

  2. 使用 feign-Httpclient 中的 ApacheHttpClient 来进行实际请求的调用

<dependency>    <groupId>com.netflix.feign</groupId>    <artifactId>feign-httpclient</artifactId>    <version>8.18.0</version></dependency>
直入源码

通过对 FeignClient 的源码阅读,发现问题不是出在参数解析上,而是在使用 ApacheHttpClient 进行请求时,其将查询参数放进请求body中了,下面看源码具体是如何处理的

feign.httpclient.ApacheHttpClient 这是 feign-httpclient 进行实际请求的方法

@Override  public Response execute(Request request, Request.Options options) throws IOException {    HttpUriRequest httpUriRequest;    try {      httpUriRequest = toHttpUriRequest(request, options);    } catch (URISyntaxException e) {      throw new IOException("URL '" + request.url() + "' couldn't be parsed into a URI", e);    }    HttpResponse httpResponse = client.execute(httpUriRequest);    return toFeignResponse(httpResponse);  }   HttpUriRequest toHttpUriRequest(Request request, Request.Options options) throws          UnsupportedEncodingException, MalfORMedURLException, URISyntaxException {    RequestBuilder requestBuilder = RequestBuilder.create(request.method());     //per request timeouts    RequestConfig requestConfig = RequestConfig            .custom()            .setConnectTimeout(options.connectTimeoutMillis())            .setSocketTimeout(options.readTimeoutMillis())            .build();    requestBuilder.setConfig(requestConfig);     URI uri = new URIBuilder(request.url()).build();     requestBuilder.setUri(uri.getScheme() + "://" + uri.getAuthority() + uri.getRawPath());     //request query params    List<NameValuePair> queryParams = URLEncodedUtils.parse(uri, requestBuilder.getCharset().name());    for (NameValuePair queryParam: queryParams) {      requestBuilder.addParameter(queryParam);    }     //request headers    boolean hasAcceptHeader = false;    for (Map.Entry<String, Collection<String>> headerEntry : request.headers().entrySet()) {      String headerName = headerEntry.geTKEy();      if (headerName.equalsIgnoreCase(ACCEPT_HEADER_NAME)) {        hasAcceptHeader = true;      }       if (headerName.equalsIgnoreCase(Util.CONTENT_LENGTH)) {        // The 'Content-Length' header is always set by the Apache client and it        // doesn't like us to set it as well.        continue;      }       for (String headerValue : headerEntry.getValue()) {        requestBuilder.addHeader(headerName, headerValue);      }    }    //some servers choke on the default accept string, so we'll set it to anything    if (!hasAcceptHeader) {      requestBuilder.addHeader(ACCEPT_HEADER_NAME, "*/*");    }     //request body    if (request.body() != null) {     //body为空,则HttpEntity为空       HttpEntity entity = null;      if (request.charset() != null) {        ContentType contentType = getContentType(request);        String content = new String(request.body(), request.charset());        entity = new StringEntity(content, contentType);      } else {        entity = new ByteArrayEntity(request.body());      }       requestBuilder.setEntity(entity);    }     //调用org.apache.http.client.methods.RequestBuilder#build方法    return requestBuilder.build();  }

org.apache.http.client.methods.RequestBuilder 此类是 HttpUriRequest 的Builder类,下面看build方法

public HttpUriRequest build() {        final HttpRequestBase result;        URI uriNotNull = this.uri != null ? this.uri : URI.create("/");        HttpEntity entityCopy = this.entity;        if (parameters != null && !parameters.isEmpty()) {    // 这里:如果HttpEntity为空,并且为POST请求或者为PUT请求时,这个方法会将查询参数取出来封装成了HttpEntity    // 就是在这里查询参数被丢弃了,准确的说是被转换位置了            if (entityCopy == null && (HttpPost.METHOD_NAME.equalsIgnoreCase(method)                    || HttpPut.METHOD_NAME.equalsIgnoreCase(method))) {                entityCopy = new UrlEncodedFormEntity(parameters, charset != null ? charset : HTTP.DEF_CONTENT_CHARSET);            } else {                try {                    uriNotNull = new URIBuilder(uriNotNull)                      .setCharset(this.charset)                      .addParameters(parameters)                      .build();                } catch (final URISyntaxException ex) {                    // should never happen                }            }        }        if (entityCopy == null) {            result = new InternalRequest(method);        } else {            final InternalEntityEclosingRequest request = new InternalEntityEclosingRequest(method);            request.setEntity(entityCopy);            result = request;        }        result.setProtocolVersion(this.version);        result.setURI(uriNotNull);        if (this.headergroup != null) {            result.setHeaders(this.headergroup.getAllHeaders());        }        result.setConfig(this.config);        return result;    }

解决方案

既然已经知道原因了,那么解决方法就有很多种了,下面就介绍常规的解决方案:

  1. 使用 feign-okhttp 来进行请求调用,这里就不列源码了,感兴趣大家可以去看, feign-okhttp 底层没有判断如果body为空则把查询参数放入body中。

  2. 使用 io.GitHub.openfeign:feign-httpclient:9.5.1 依赖,截取部分源码说明原因如下:

HttpUriRequest toHttpUriRequest(Request request, Request.Options options) throws          UnsupportedEncodingException, MalformedURLException, URISyntaxException {    RequestBuilder requestBuilder = RequestBuilder.create(request.method());    //省略部分代码    //request body    if (request.body() != null) {      //省略部分代码    } else {    // 此处,如果为null,则会塞入一个byte数组为0的对象      requestBuilder.setEntity(new ByteArrayEntity(new byte[0]));    }     return requestBuilder.build();  }

推荐的依赖

<dependency>    <groupId>io.github.openfeign</groupId>    <artifactId>feign-httpclient</artifactId>    <version>9.5.1</version></dependency>

或者

<dependency>    <groupId>io.github.openfeign</groupId>    <artifactId>feign-okhttp</artifactId>    <version>9.5.1</version></dependency>

总结

目前绝大部分的介绍 feign 的文章都是推荐的 com.netflix.feign:feign-httpclient:8.18.0 和 com.netflix.feign:feign-okhttp:8.18.0 ,如果不巧你使用了 com.netflix.feign:feign-httpclient:8.18.0,那么在POST请求时并且body为空时就会发生丢失查询参数的问题。

这里推荐大家使用 feign-httpclient 或者是 feign-okhttp的时候不要依赖 com.netflix.feign,而应该选择 io.github.openfeign,因为看起来 Netflix 很久没有对这两个组件进行维护了,而是由 OpenFeign 来进行维护了。

“如何解决FeignClient发送post请求异常的问题”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注编程网网站,小编将为大家输出更多高质量的实用文章!

--结束END--

本文标题: 如何解决FeignClient发送post请求异常的问题

本文链接: https://lsjlt.com/news/297580.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • 解决FeignClient发送post请求异常的问题
    FeignClient发送post请求异常 这个问题其实很基础。但是却难倒了我。记录一下 在发送post请求的时候要指定消息格式 正确的写法是这样 @PostMapping(va...
    99+
    2024-04-02
  • 如何解决FeignClient发送post请求异常的问题
    本篇内容介绍了“如何解决FeignClient发送post请求异常的问题”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!FeignClient...
    99+
    2023-06-20
  • Java如何解决发送Post请求报Stream closed问题
    目录问题场景问题分析解决办法springboot项目还是ssm等java常用框架都会有这样的问题,解决办法通用 问题场景 前端发送Post请求,前端返回400 Bad Request...
    99+
    2024-04-02
  • PHP发送POST请求失败如何解决
    这篇文章主要讲解了“PHP发送POST请求失败如何解决”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“PHP发送POST请求失败如何解决”吧!PHP发送POST请求失败的解决办法:首先通过“e...
    99+
    2023-06-20
  • jquery如何发送post请求
    本篇内容主要讲解“jquery如何发送post请求”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“jquery如何发送post请求”吧! ...
    99+
    2024-04-02
  • 如何解决axios会发送两次请求有个OPTIONS请求的问题
    这篇文章主要为大家展示了“如何解决axios会发送两次请求有个OPTIONS请求的问题”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“如何解决axios会发送两次...
    99+
    2024-04-02
  • axios发送post请求上传文件到后端的问题怎么解决
    这篇“axios发送post请求上传文件到后端的问题怎么解决”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“axios发送po...
    99+
    2023-06-30
  • vue如何实现发送websocket请求和http post请求
    这篇文章主要介绍vue如何实现发送websocket请求和http post请求,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!先给大家介绍下vue发送websocket请求和http...
    99+
    2024-04-02
  • SpringBoot 如何使用RestTemplate发送Post请求
    目录背景:1、待访问的API2、返回对象3、将发送Post请求的部分封装如下4、UserInfo对象5、在Service层封装将要发送的参数6、在控制器中调用service中的方法,...
    99+
    2024-04-02
  • 解决axios发送post请求上传文件到后端的问题(multipart/form-data)
    目录项目场景:问题描述原因分析:解决方案:项目场景: 后端:实现了一个文件上传服务接口,可以接收前端传递过来的MultipartFile文件,并存储到服务器本地中。前端:获取type...
    99+
    2024-04-02
  • php curl如何发送get或者post请求
    这篇文章主要为大家展示了“php curl如何发送get或者post请求”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“php curl如何发送get或者post...
    99+
    2024-04-02
  • Android发送GET与POST请求的DEMO详解
    4.0后网络访问必须单独起一个子线程访问,否则无法运行,这里有一个发送请求的工具类GetPostUtil 代码如下:public class GetPostUtil{ ...
    99+
    2022-06-06
    get demo post请求 post Android
  • 使用axios发送post请求返回400状态码如何解决
    使用axios发送post请求返回400状态码如何解决,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。前台代码如下:axios(...
    99+
    2024-04-02
  • 如何解决vue中$http的get和post请求跨域问题
    这篇文章给大家分享的是有关如何解决vue中$http的get和post请求跨域问题的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。vue $http的get和post请求跨域问题首先在config/index.js...
    99+
    2023-06-15
  • 关于Python中request发送post请求传递json参数的问题
    昨天遇到了一个奇怪的问题,在Python中需要传递dict参数,利用json.dumps将dict转为json格式用post方法发起请求: params = {"score":{"g...
    99+
    2024-04-02
  • 解决vue $http的get和post请求跨域问题
    vue $http的get和post请求跨域问题 首先在config/index.js中配置proxyTable proxyTable: { '/api':{ ...
    99+
    2024-04-02
  • vue如何解决一个方法同时发送多个请求的问题
    这篇文章主要介绍了vue如何解决一个方法同时发送多个请求的问题,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。在...
    99+
    2024-04-02
  • 如何解决ie发送ajax请求返回上一次结果的问题
    这篇文章主要讲解了“如何解决ie发送ajax请求返回上一次结果的问题”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“如何解决ie发送ajax请求返回上一次结果...
    99+
    2024-04-02
  • 如何实现ajax发送异步请求
    这篇文章将为大家详细讲解有关如何实现ajax发送异步请求,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。具体内容如下第一步(得到XMLHttpRequest)ajax其实只...
    99+
    2024-04-02
  • 在Go语言中如何解决并发网络请求的请求服务降级和异常处理问题?
    在Go语言中如何解决并发网络请求的请求服务降级和异常处理问题?随着互联网的快速发展,越来越多的应用需要进行并发网络请求。然而,在高并发的情况下,网络请求可能会导致超时、阻塞等问题,从而影响到整个系统的稳定性和可靠性。面对这个问题,我们可以使...
    99+
    2023-10-22
    异常处理 (Exception Handling) 并发网络请求 (concurrent network request
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作