【Feign请求头丢失问题】no suitable HttpMessageConverter found for response type

这篇具有很好参考价值的文章主要介绍了【Feign请求头丢失问题】no suitable HttpMessageConverter found for response type。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

省流助手

HttpMessageConverter 失败的原因是:在项目中使用了一个拦截器拦截请求,部分接口需要登陆才能访问,否则返回一个text/html格式的响应,导致远程服务解析响应失败。
登陆失败的原因是:Feign发起远程调用的时候会重新生成一个新的请求,带来的问题就是不会携带原来请求的cookie,导致调用需要登陆的远程接口时会失败。解决方法是配置一个Feign的拦截器,在发送请求的时候带上原请求的cookie。
本文主要内容是围绕这个问题展开的一系列知识点,包括但不限于:

  • http的content type
  • 微服务联调debug
  • 查看Feign日志
  • 登陆拦截器
  • Fegin丢头问题

问题分析定位

今天在联调两个微服务的时候发现远程接口总是返回以下报错:

Could not extract response: no suitable HttpMessageConverter found for response type [class top.dumbzarro.greensource.common.utils.R] and content type [text/html;charset=UTF-8]

意思是没有一个HttpMessageConverter 可以将 [text/html;charset=UTF-8]转化为[class top.dumbzarro.greensource.common.utils.R] 。
其中,R是项目中定义的一个通用的返回对象,所有接口都返回这个对象。

远程接口在ware服务,详细如下:

@FeignClient("greensource-member")
public interface MemberFeignService {
    @GetMapping("/memberreceiveaddress/info/{id}")
    R info(@PathVariable("id") Long id);
}

被调用接口在member服务,详细如下:

@RestController
@RequestMapping("memberreceiveaddress")
public class MemberReceiveAddressController {
	@Autowired
    private MemberReceiveAddressService memberReceiveAddressService;

    @GetMapping("/info/{id}")
    //@RequiresPermissions("member:memberreceiveaddress:info")
    public R info(@PathVariable("id") Long id){
		MemberReceiveAddressEntity memberReceiveAddress = memberReceiveAddressService.getById(id);
		return R.ok().setData(memberReceiveAddress);
    }
}

比较疑惑的是,在联调这两个服务之前,已经调通了auth服务和member服务、auth服务和third-party服务,两个服务之间的Feign远程调用就没有问题。

网上对于no suitable HttpMessageConverter的解决方案就是添加一个自定义的转换器等等。但是隐约感觉这不是类型转换的问题,不然在没有额外配置的情况下,之前的服务不可能跑的通。

HTTP Content-type

Content-type是HTTP协议中的一个字段,Content-Type 标头告诉客户端实际返回的内容的内容类型。
常见的有:

  • text/html: HTML格式,浏览器在获取到这种文件时会自动调用html的解析器对文件进行渲染的处理。
  • text/plain:将文件设置为纯文本的形式,浏览器在获取到这种文件时并不会对其进行处理。
  • application/json: JSON数据格式,浏览器不会对其进行处理。

TODO Content-type springmvc fegin的默认content-type

印象里接口都是返回json数据,content-type是application/json,怎么会突然冒出个text/html呢。于是使用全局搜索查了一下。
feign httpmessageconverter,java,前端,开发语言
突然想起在部分需要登陆的业务中都增加了一个拦截器,用于判断用户是否登陆,在判断用户没有登陆的时候会返回一个text/html的响应。详细代码如下。

@Component
public class LoginUserInterceptor implements HandlerInterceptor {

    public static ThreadLocal<MemberResponseVo> loginUser = new ThreadLocal<>();

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        String uri = request.getRequestURI();
        if(uri.equals("/error")){
            response.setContentType("text/html;charset=UTF-8");
            PrintWriter out = response.getWriter();
            out.println("<script>alert('uri为 /error, 可能原因为:1.请求方法错误 2.参数格式解析错误');</script>");
            return false;
        }

        boolean match = new AntPathMatcher().match("/member/**", uri);
        if (match) { // member接口(登陆,注册)可以不用登陆就使用,否则需要登陆
            return true;
        }

        HttpSession session = request.getSession();

        //获取登录的用户信息
        MemberResponseVo attribute = (MemberResponseVo) session.getAttribute(LOGIN_USER);

        if (attribute != null) {
            //把登录后用户的信息放在ThreadLocal里面进行保存
            loginUser.set(attribute);

            return true;
        } else {
            //未登录,返回登录页面
            response.setContentType("text/html;charset=UTF-8");
            PrintWriter out = response.getWriter();
            out.println("<script>alert('请先进行登录,再进行后续操作!');location.href='http://auth.dumbzarro.top/login.html'</script>");
            return false;
        }
    }

在feign的请求的时候,被判定为没有登陆,所以返回了这个“text/html”格式的数据,而在远程接口处我们使用的是R进行接受,自然就无法成功解析然后就会出现报错。
正常来说这里应该返回的是一个application对象,由于这个项目是基于谷粒商城修改的,谷粒商城是前后端不分离了,而后续这个项目使用的是前后端分离的结构,所以这里将这个返回值做一个修改,即可解决这个报错了。
可参考如下代码修改

@Component
public class LoginUserInterceptor implements HandlerInterceptor {

    public static ThreadLocal<MemberResponseVo> loginUser = new ThreadLocal<>();

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        String uri = request.getRequestURI();
        if(uri.equals("/error")){
            response.setContentType("application/json; charset=utf-8");
            PrintWriter out = response.getWriter();

            out.println(JSONObject.toJSONString(R.error()
                            .put("error","uri为 /error, 可能原因为:1.请求方法错误 2.参数格式解析错误"),
                    SerializerFeature.WriteMapNullValue,
                    SerializerFeature.WriteDateUseDateFormat));
            return false;
        }

        boolean match = new AntPathMatcher().match("/member/**", uri);
        if (match) { // member接口(登陆,注册)可以不用登陆就使用,否则需要登陆
            return true;
        }

        HttpSession session = request.getSession();

        //获取登录的用户信息
        MemberResponseVo attribute = (MemberResponseVo) session.getAttribute(LOGIN_USER);

        if (attribute != null) {
            //把登录后用户的信息放在ThreadLocal里面进行保存
            loginUser.set(attribute);

            return true;
        } else {
            //未登录
            response.setContentType("application/json; charset=utf-8");
            PrintWriter out = response.getWriter();
            out.println(JSONObject.toJSONString(
                    R.error().put("error","用户未登录"),
                    SerializerFeature.WriteMapNullValue,
                    SerializerFeature.WriteDateUseDateFormat
                    )
            );
            return false;
        }
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {

    }
}

用户未登录

虽然不会报转换异常,但是还会返回“用户未登录”。
feign httpmessageconverter,java,前端,开发语言
可以确保的是我在swagger已经登陆了,请求的时候带上了cookie了的,但是经过fegin之后就显示没有登陆,而仅仅是ware服务的这个接口报错,而auth和third-party都不会报错。

微服务联调

因为单独去测试member服务的时候都没有问题,于是就想看直接请求member服务和从ware服务器请求member的请求有什么不同,于是打算在两个服务都打断点看看。注意,如果你同一个服务有多个实例注册在nacos上,那么要在@FeignClient加入url的参数,去指定到本地的服务,否则请求可能会打到其他的机器上,导致没办法debug到当前的机器上。当然,如果只有一个实例,其实不用加也可以。示例如下:

//@FeignClient(value="greensource-member")
@FeignClient(value="greensource-member",url="localhost:7000")// 指定某台机器
public interface MemberFeignService {
    @GetMapping("/memberreceiveaddress/info/{id}")
    R info(@PathVariable("id") Long id);
}

这时候启动服务,开始debug,发现程序不会经过接口调用处经过,而是在member的登陆拦截器处被判定为没有登陆,直接返回到ware服务。
查看请求,发现此时没有session,没有登陆成功。
feign httpmessageconverter,java,前端,开发语言

打开fegin 日志

我们配置一个FeginConfig,查看fegin的请求响应情况

@Configuration
public class FeignConfig {

    @Bean
    public feign.Logger logger() {
        return new Slf4jLogger();
    }
    @Bean
    public Logger.Level level() {
        return Logger.Level.FULL;
    }
}

在application.yml配置打印日志

logging:
  level:
    feign.Logger: debug

log4j定义了8个级别的log,优先级从高到低依次为:OFF、FATAL、ERROR、WARN、INFO、DEBUG、TRACE、 ALL。log4j默认的优先级为ERROR。Log4j建议只使用ERROR、WARN、INFO、DEBUG这四个级别(优先级从高到低)。如果将log level设置在某一个级别上,那么比此级别优先级高的log都能打印出来。

  1. ALL:最低等级的,用于打开所有日志记录。
  2. TRACE:很低的日志级别,一般不会使用。
  3. DEBUG:指出细粒度信息事件对调试应用程序是非常有帮助的,主要用于开发过程中打印一些运行信息。
  4. INFO:消息在粗粒度级别上突出强调应用程序的运行过程。打印一些你感兴趣的或者重要的信息,这个可以用于生产环境中输出程序运行的一些重要信息,但是不能滥用,避免打印过多的日志。
  5. WARN:表明会出现潜在错误的情形,有些信息不是错误信息,但是也要给程序员的一些提示。
  6. ERROR:打印错误和异常信息,指出虽然发生错误事件,但仍然不影响系统的继续运行。
  7. FATAL:指出每个严重的错误事件将会导致应用程序的退出。重大错误,这种级别可以直接停止程序了。
  8. OFF:最高等级的,用于关闭所有日志记录。

可以看到我们的请求是没有设置cookie的
feign httpmessageconverter,java,前端,开发语言
这就是fegin请求失败的根本原因,所以我们在ware配置fegin发送请求是带上cookie。

Feign丢失cookie问题

由于fegin每次请求都会自己发一个新的请求,而不会带上我们之前的请求的cookie,这时候我们就要手动配置一下。在之前设置debug的地方继续添加配置,注入一个拦截器到spring容器中,在Feign请求之前我们设置一下cookie

@Configuration
public class FeignConfig {

    @Bean
    public feign.Logger logger() {
        return new Slf4jLogger();
    }
    @Bean
    public Logger.Level level() {
        return Logger.Level.FULL;
    }

    @Bean("requestInterceptor")
    public RequestInterceptor requestInterceptor() {

        RequestInterceptor requestInterceptor = new RequestInterceptor() {
            @Override
            public void apply(RequestTemplate template) {
                //1、使用RequestContextHolder拿到刚进来的请求数据
                ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();

                if (requestAttributes != null) {
                    //老请求
                    HttpServletRequest request = requestAttributes.getRequest();

                    //2、同步请求头的数据(主要是cookie)
                    //把老请求的cookie值放到新请求上
                    String cookie = request.getHeader("Cookie");
                    template.header("Cookie", cookie);
                }
            }
        };
        return requestInterceptor;
    }
}

查看日志,发现请求已经成功带上了cookie
feign httpmessageconverter,java,前端,开发语言
按道理来说两个请求应该是一个cookie和session的,但是这里却发现两个session不一致。
大概是登陆超时了,过期了,从新登陆一下就好了。
feign httpmessageconverter,java,前端,开发语言
成功返回了消息。

为什么之前的微服务不会出现问题?

之前调通了auth-server和third-party 以及 auth-server 和 member,都没有出现类似的问题。
前者的原因是third-party没有登陆拦截器,因此auth-server 调用third-party的时候不会返回text/html的内容,因此能正常解析。既然没有登陆拦截器,那么有无cookie也不影响远程调用。
后者的原因是虽然member有登陆拦截器,但是因为auth-server请求的接口是放行的(详细见上面的代码),所以也不会返回text/html的返回值,因此也能正常解析。同时有因为接口不需要登陆认证的cookie,fegin请求头的cookie丢失了也不影响。文章来源地址https://www.toymoban.com/news/detail-570780.html

到了这里,关于【Feign请求头丢失问题】no suitable HttpMessageConverter found for response type的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 解决 Could not write request: no suitable HttpMessageConverter found for request type [java.lang.Long]

    业务服务通过RestTemplate调用文件上传服务。( java.version1.8/java.version spring.cloud.versionHoxton.SR12/spring.cloud.version spring.cloud.alibaba.version2.2.9.RELEASE/spring.cloud.alibaba.version spring.boot.version2.3.12.RELEASE/spring.boot.version ) 由于restTemplate中引入了FormHttpMessageConverter消息转换器,在调用过程中

    2024年02月04日
    浏览(60)
  • Qt创建控制台程序选择构建套件问题“No suitable kits found”

    QT 选择构建套件时出现问题: “No suitable kits found” = 没有找到合适的kits套件,在安装Qt Creator时没有安装MinGW,所以只需要进行安装即可。 3.1 选择安装目录下的“MaintenanceTool.exe”,双击计入组件安装界面。 3.2 点击“下一步” 3.3 选择“添加或移除组件”: 3.4 根据自己安装

    2024年02月11日
    浏览(40)
  • 已解决No suitable driver found for jdbc:mysql://localhost:3306/ 问题

    已解决No suitable driver found for jdbc:mysql://localhost:3306/ 问题 在学习java数据库连接池使用的时候遇到问题,无法连接到数据库,查看日志是\\\"No Suitable Driver Found For Jdbc\\\",但查看数据库连接配置没问题。 这个问题可把我愁坏了,要不问一下GPT?上。 问了一下GPT,得到的答案是这样的

    2024年02月07日
    浏览(48)
  • 谷粒商城笔记+踩坑(20)——订单确认页。feign、异步请求头丢失问题+令牌保证幂等性

    导航: 谷粒商城笔记+踩坑汇总篇 目录 1、订单确认页 1.1、vo类抽取 1.2、获取订单详情页数据,完整代码 1.2.1、Controller编写跳转订单确认页方法 1.2.2、Service获取订单详情页数据 1.3、【会员模块】获取会员所有收货地址 1.3.1、controller 1.3.2、service  1.4、订单服务远程调用用户

    2023年04月09日
    浏览(77)
  • Navicat Premium 连接 MongoDB 失败: Cannot connect to MongoDB.No suitable servers found: ......

    问题: 在 CentOS7 中使用 docker 部署 MongoDB,通过 Navicat Premium 无法连接。 解决办法:  1、执行命令,进入 mongodb 容器(mongodb-server 为容器名) 2、在 mongodb 容器中执行以下命令 [ps]------------------------------------------------------------------------------------------- 如果执行 apt-get update 出现 Err:1

    2023年04月08日
    浏览(37)
  • java.sql.SQLException: No suitable driver found for jdbc:mysql://localhost:3306/DB3

    第一: 查看具体配置的路径是否错误,以及当前的DB3数据库是否存在。 如果地址填写错误(包括localhost或者端口3306写出都会)会报: Exception in thread \\\"main\\\" com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: Communications link failure The last packet sent successfully to the server was 0 milliseconds ago

    2024年02月04日
    浏览(38)
  • 处理JDBC:java.sql.SQLException: No suitable driver found for jdbc:mysql://localhost:3306/DB

    今天花费一个多小时的事件处理这个问题,最终的处理方法真的是太…棒了,我重开了个项目,建了下模块,结果问题解决了。 本着斩草要除根……咳咳,刨根问底的原则,我决定细查一下这个问题,下面是我得到的结果! 不说废话,简单来说:   官方定义了的一套操作

    2023年04月11日
    浏览(38)
  • 超详细干货解决Exception in thread “main“ java.lang.ClassNotFoundException及No suitable driver found for jdbc

    最近一直被eclipse的WindowBuilder和java继jdbc连接Mysql的问题困扰,终于摸索出对我有用的解决方法,希望这对你也有所帮助! 首先,当你在eclipse等IDE编译器里Run数据库连接后,出现Exception in thread \\\"main\\\" java.lang.ClassNotFoundException的结果报错,我本人后面出现的还有illegal一串字符,

    2024年02月06日
    浏览(62)
  • 请求500失败-No primary or single unique constructor found for interface xxx

    错误:No primary or single unique constructor found for interface java.util.List(没有为List接口找到主要的或唯一的构造函数) 原因:请求的参数没有匹配上处理函数的参数 解决:为List参数添加@RequestParam(\\\"strList\\\")指定参数名称即可 附加:本接口为测试异常接口,一般多个参数会封装为一个入

    2024年02月12日
    浏览(50)
  • Nginx 重定向后请求参数丢失问题及解决方案

    在日常开发和运维中,我们经常会遇到需要使用 Nginx 进行反向代理的场景。但在配置 proxy_pass 时,有时候可能会遇到请求参数丢失的问题。在这篇文章中,我们将会详细探讨这个问题并给出几种解决方案。 假设我们有一个基础的 Nginx 反向代理配置,如下: 当我们通过 Ngin

    2024年02月05日
    浏览(68)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包