Spring MVC:当未指定内容类型时@RequestBody

2024-03-19

我有一个 Spring MVC 应用程序,它以 JSON 字符串的形式从外部系统接收 HTTP 请求,其响应的返回方式与 JSON 字符串类似。我的控制器正确注释为@RequestBody and @ResponseBody我有集成测试,它实际上发送请求来验证一切是否按预期工作。

但是,当我针对将使用它的实际外部系统测试我的应用程序时,我发现传入请求未指定内容类型!这完全混淆了 Spring 并导致以下类型的错误:

DEBUG [] 2014-04-17 13:33:13,471 AbstractHandlerExceptionResolver.java:132 resolveException - Resolving exception from handler [com.example.controller.MyController@1d04f0a]: org.springframework.web.HttpMediaTypeNotSupportedException: Cannot extract parameter (ValidationRequest request): no Content-Type found

那么,有没有办法强制Spring通过以下方式路由这样的请求:MappingJacksonHttpMessageConverter,要么通过某种方式强制 Spring 使用自定义处理程序链,要么修改传入请求以显式设置内容类型?

我尝试过一些事情:

  • 延伸MappingJacksonHttpMessageConverter以便其canRead() and canWrite()方法总是返回 true。不幸的是,由于缺乏内容类型,Spring 在退出之前甚至没有达到查看消息转换器的程度。
  • 使用拦截器或 Servlet 过滤器手动设置内容类型。不幸的是,除了设置新属性之外,我看不到任何一种机制可以实际更改传入请求。

任何想法表示赞赏。


为了解决下面的评论,我的@RequestMapping好像:

@RequestMapping(value="/{service}" )
public @ResponseBody MyResponseObject( @PathVariable String service, @RequestBody MyRequestObject request) {

因此,这里没有任何内容指定 JSON,但是如果没有内容类型,Spring 似乎甚至不会尝试从传入请求构建我的请求对象(这是有道理的,因为它没有足够的信息来确定如何执行此操作) )。

至于 @geoand 的评论询问“为什么不能在 Servlet 过滤器或 Spring 拦截器中添加内容类型 http 标头”,答案是“因为我很笨,忘记了 servlet 过滤器是如何工作的”。这是我最终用来解决问题的方法,我将立即将其添加为答案。


当我问这个问题时,我有点傻,因为我正在 Spring 中寻找一种方法来直接操作传入的请求,或者明确地告诉处理程序链我希望请求始终被视为 JSON。我想了一下,我意识到这正是 Servlet Filter 的用途。

首先,我创建了一个新的HttpServletRequestWrapper看起来像这样:

public class ForcedContentTypeHttpServletRequestWrapper extends HttpServletRequestWrapper {

    private static final Logger log = Logger.getLogger( ForcedContentTypeHttpServletRequestWrapper.class );

    // this is the header to watch out for and what we should make sure it always resolves to.
    private static final String CONTENT_TYPE_HEADER = "content-type";
    private static final String CONTENT_TYPE = "application/json";


    public ForcedContentTypeHttpServletRequestWrapper( HttpServletRequest request ) {
        super( request );
    }

    /**
     * If content type is explicitly queried, return our hardcoded value
     */
    @Override
    public String getContentType() {
        log.debug( "Overriding request's content type of " + super.getContentType() );
        return CONTENT_TYPE;
    }

    /**
     * If we are being asked for the content-type header, always return JSON
     */
    @Override
    public String getHeader( String name ) {
        if ( StringUtils.equalsIgnoreCase( name, CONTENT_TYPE_HEADER ) ) {
            if ( super.getHeader( name ) == null ) {
                log.debug( "Content type was not originally included in request" );
            }
            else {
                log.debug( "Overriding original content type from request: " + super.getHeader( name ) );
            }
            log.debug( "Returning hard-coded content type of " + CONTENT_TYPE );
            return CONTENT_TYPE;
        }

        return super.getHeader( name );
    }

    /**
     * When asked for the names of headers in the request, make sure "content-type" is always
     * supplied.
     */
    @SuppressWarnings( { "unchecked", "rawtypes" } )
    @Override
    public Enumeration getHeaderNames() {

        ArrayList headerNames = Collections.list( super.getHeaderNames() );
        if ( headerNames.contains( CONTENT_TYPE_HEADER ) ) {
            log.debug( "content type already specified in request. Returning original request headers" );
            return super.getHeaderNames();
        }

        log.debug( "Request did not specify content type. Adding it to the list of headers" );
        headerNames.add( CONTENT_TYPE_HEADER );
        return Collections.enumeration( headerNames );
    }

    /**
     * If we are being asked for the content-type header, always return JSON
     */
    @SuppressWarnings( { "rawtypes", "unchecked" } )
    @Override
    public Enumeration getHeaders( String name ) {
        if ( StringUtils.equalsIgnoreCase( CONTENT_TYPE_HEADER, name ) ) {
            if ( super.getHeaders( name ) == null ) {
                log.debug( "Content type was not originally included in request" );
            }
            else {
                log.debug( "Overriding original content type from request: " + Collections.list( super.getHeaders( name ) ) );
            }
            log.debug( "Returning hard-coded content type of " + CONTENT_TYPE );
            return Collections.enumeration( Arrays.asList( CONTENT_TYPE ) );
        }

        return super.getHeaders( name );
    }

}

然后我将此包装器用于过滤器,如下所示:

public class ContentTypeFilter implements Filter {

    /**
     * @see Filter#destroy()
     */
    @Override
    public void destroy() {
        // do nothing
    }

    /**
     * @see Filter#doFilter(ServletRequest, ServletResponse, FilterChain)
     */
    @Override
    public void doFilter( ServletRequest request, ServletResponse response, FilterChain chain ) throws IOException, ServletException {
        ForcedContentTypeHttpServletRequestWrapper requestWrapper = new ForcedContentTypeHttpServletRequestWrapper( (HttpServletRequest) request );
        chain.doFilter( requestWrapper, response );
    }

    /**
     * @see Filter#init(FilterConfig)
     */
    @Override
    public void init( FilterConfig fConfig ) throws ServletException {
        // do nothing
    }

}

它并不完全安全,但它可以正确处理来自该应用程序实际关心的单一来源的请求。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Spring MVC:当未指定内容类型时@RequestBody 的相关文章

  • Java:在 eclipse 中导出到 .jar 文件

    我正在尝试将 Eclipse 中的程序导出到 jar 文件 在我的项目中 我添加了一些图片和 PDF s 当我导出到 jar 文件时 似乎只有main已编译并导出 我的意愿是如果可能的话将所有内容导出到 jar 文件 因为这样我想将其转换为
  • GWT - 如何组织项目以拥有多个网页以及它们之间的导航

    我是 GET 的新手 顺便说一句 它给我留下了深刻的印象 并且发现它对于像我这样熟悉 C NET 桌面技术并愿意编写 Web 应用程序的人来说非常有吸引力 我根据 GWT Eclipse 向导生成的示例启动了自己的项目 该项目生成带有面板的
  • Spring RestTemplate 使用 cookie 遵循重定向

    最近我遇到了一个问题 我需要做一个GET请求远程服务 我假设使用一个简单的 servlet 并且 RestTemplate 返回Too many redirects 经过一番调查 似乎对指定远程服务发出的第一个请求实际上只是一个 302 重
  • 在 swift 中将简单字符串转换为 JSON 字符串

    我知道有一个同标题的问题here https stackoverflow com questions 30825755 convert string to json string in swift 但在那个问题中 他试图将字典转换为 JSO
  • 大数据使用什么数据结构

    我有一个包含一百万行的 Excel 工作表 每行有 100 列 每行代表一个具有 100 个属性的类的实例 列值是这些属性的值 哪种数据结构最适合在这里使用来存储数百万个数据实例 Thanks 这实际上取决于您需要如何访问这些数据以及您想要
  • 当客户端关闭连接时,Spring StreamingResponseBody 请求线程未清理

    我在控制器中有一个端点 它返回一个StreamingResponseBody 用于向客户端发送文件 其代码大致如下 RestController RequestMapping value api public class Controlle
  • 如何从 JSON 响应重定向?

    所以我尝试使用 Flask 和 Javascript 上传器 Dropzone 上传文件并在上传完成后重定向 文件上传正常 但在烧瓶中使用传统的重定向 return redirect http somesite com 不执行任何操作 页面
  • Java Swing For mac 中的 DJ Native Swing 浏览器

    我有一个用 Swing 制作的 Java 应用程序 并且使用了一个 DJ Native Swing 浏览器 当我尝试在 OS X 上使用它时 它抛出了一个NoClassDefFoundError尽管我添加了 swt jar 但始终如此 有人
  • 内部存储的安全性如何?

    我需要的 对于 Android 我需要永久保存数据 但也能够编辑 并且显然是读取 它 用户不应访问此数据 它可以包含诸如高分之类的内容 用户不得对其进行编辑 我的问题 我会 并且已经 使用过Internal Storage 但我不确定它实际
  • 读取电子邮件的文本文件转换为 Javamail MimeMessage

    我有一个电子邮件原始来源的文本文件 直接从 gmail 复制 如果您单击 查看原始文件 您就会看到它 我想读入该文件并将其转换为 MimeMessage 如果您好奇为什么 我设置了 JavaMaildir 并且需要用电子邮件填充它的收件箱以
  • Java实现累加器类,提供Collector

    A Collector具有三种通用类型 public interface Collector
  • Freemarker 和 Struts 2,有时它计算为序列+扩展哈希

    首先我要说的是 使用 Struts2 Freemarker 真是太棒了 然而有些事情让我发疯 因为我不明白为什么会发生这种情况 我在这里问是因为也许其他人有一个想法可以分享 我有一个动作 有一个属性 说 private String myT
  • 使用架构注册表对 avro 消息进行 Spring 云合约测试

    我正在查看 spring 文档和 spring github 我可以看到一些非常基本的内容examples https github com spring cloud samples spring cloud contract sample
  • 返回 Java 8 中的通用函数接口

    我想写一种函数工厂 它应该是一个函数 以不同的策略作为参数调用一次 它应该返回一个函数 该函数根据参数选择其中一种策略 该参数将由谓词实现 嗯 最好看看condition3为了更好的理解 问题是 它没有编译 我认为因为编译器无法弄清楚函数式
  • 使用布尔值进行冒泡排序以确定数组是否已排序

    我有以下用于冒泡排序的代码 但它根本不排序 如果我删除布尔值那么它工作正常 我知道 由于我的 a 0 小于所有其他元素 因此没有执行交换 任何人都可以帮助我解决这个问题 package com sample public class Bub
  • 是否可以使用 Java Guava 将函数应用于集合?

    我想使用 Guava 将函数应用于集合 地图等 基本上 我需要调整 a 的行和列的大小Table分别使所有行和列的大小相同 执行如下操作 Table
  • Hamcrest Matchers - 断言列表类型

    问题 我目前正在尝试使用 Hamcrest Matchers 来断言返回的列表类型是特定类型 例如 假设我的服务调用返回以下列表 List
  • OpenCSV:将嵌套 Bean 映射到 CSV 文件

    我正在尝试将 bean 映射到 CSV 文件 但问题是我的 bean 具有其他嵌套 bean 作为属性 所发生的情况是 OpenCSV 遍历属性找到一个 bean 然后进入其中并映射该 bean 内的所有数据 如果找到另一个 bean 它就
  • 如何使用play框架上传多个文件?

    我在用play framework 2 1 2 使用java我正在创建视图来上传多个文件 我的代码在这里 form action routes upload up enctype gt multipart form data
  • 使用 Python 将对象列表转为 JSON

    我在转换时遇到问题Object实例到 JSON ob Object list name scaping myObj base url u number page for ob in list name json string json du

随机推荐

  • SVG 内的链接图像

    想象一下以下 SVG
  • Android NDK - NativeActivity 与 JNI 库

    两年前 我开发了一个增强现实框架android 7 闪电泡芙 http developer android com about versions android 2 1 html 由于 AR 应用程序是计算密集型任务 因此我开发了一个 JN
  • 我应该使用类还是字典?

    我有一个只包含字段而不包含方法的类 如下所示 class Request object def init self environ self environ environ self request method environ get R
  • 如何创建通用存储库?

    我想知道是否有人有关于制作通用存储库的任何好的教程 或者甚至可能是已经制作好的并且有详细记录的库 我当前正在使用 linq to sql 但它可能会发生变化 所以我不知道您是否可以创建一个通用存储库 如果我说切换到实体框架 则几乎不需要任何
  • 以编程方式展开/折叠 CoordinatorLayout 中的底部导航视图

    我有一个CoordinatorLayout其中包含一个BottomNavigationView and an AppBarLayout with a ToolBar在它里面 这BottomNavigationView不在里面AppBarLa
  • 部署不渲染 Kendo UI

    VS2012 asp net MVC4 c 带有 KendoUI 实现的互联网应用程序 最简单的说法是我的网站看起来像这样 开发机器上的本地 像这样部署 我检查了服务器 Kendo UI 论坛 所有论坛都指向图像丢失 我检查了甚至复制了我的
  • 授予 EC2 实例对 S3 存储桶的访问权限

    我想授予我的 ec2 实例对 s3 存储桶的访问权限 在此 ec2 实例上 启动了一个包含我的应用程序的容器 现在我没有获得 s3 存储桶的许可 这是我的存储桶政策 Version 2012 10 17 Id Policy146280822
  • 如何使用iPhone SDK下载文本文件?

    我是开发基于视图的 iPhone 应用程序的新手 我需要下载 这个 txt 文件来自互联网 并将其保存到应用程序的文档文件夹中 谁能简单地告诉我如何做到这一点 txt 文件很小 所以我不会 需要任何用户界面对象 Thanks Kevin N
  • 如何使用CSS仅在移动设备上显示文本?

    我有一个文本 在 div 中 显示在桌面和移动屏幕上 Expected 我希望文本仅显示在 media only screen and max width 768px How to 隐藏 div 与display none or 还有其他解
  • Django-rest-framework 多个 url 参数

    如何将 示例对象 映射到 url website com api
  • 编辑 PrimeNG 组件的 CSS

    我目前正在使用 Angular 4 Angular Materials 和 PrimeNG 组件开发用户界面 我正在处理的最新组件是来自 PrimeNG 的 MultiSelect 组件 https www primefaces org p
  • iOS 禁用横向 LaunchScreen.storyboard

    我有一个LaunchScreen storybaord显示徽标 文本 因此与方向无关 该应用程序始终以纵向启动 但它有某些允许横向模式的视图控制器 因此不能选择仅使应用程序纵向 我想要的是启动屏幕始终以纵向显示 因此 在应用程序启动期间将手
  • 通过模拟器发送电话号码

    我正在制作一个应用程序 我想检索设备电话号码并将其发送到服务器上 但我正在 Android 模拟器上测试这个应用程序 谁能告诉我如何在模拟器和实际设备中设置或获取电话号码 Thanks 如果我们使用电话管理器 我们可以在模拟器中获取电话号码
  • 如何在 Entity Framework Core 中删除多行? [复制]

    这个问题在这里已经有答案了 我需要使用 Entity Framework Core 从数据库中删除多行 此代码不起作用 foreach var item in items myCollection Remove item 因为我在第一个对象
  • 如何测试 Ruby on Rails 功能测试的 JSON 结果?

    我该如何维护我的Ajax http en wikipedia org wiki Ajax 28programming 29请求并测试 Ruby on Rails 功能测试的 JSON 输出 在 Rails gt 5 中 Use Action
  • Jena PrefixMapping:当模型是从数据集中获取的命名模型时,基本命名空间缺失

    这是我用来加载的代码OntModel to a Dataset作为命名模型 然后我尝试检索PrefixMapping以两种不同的方式实现相同的目的 public static void loadDatasetwithNamedModels
  • 获取“exec”调用中最后一个表达式的值

    假设我在字符串中有一些 python 代码 code a 42 a and I exec那串代码 result exec code Then result一直会None 有没有办法获得最后一个表达式的值 在这种情况下 那就是5 since
  • VueJS 读取 Dom 属性

    我想获取按钮单击事件的 href 属性 a href user all 2 i class fa fa edit i span Get Data span a 主要 JS 文件 new Vue el body methods func fu
  • 实体框架 4 存储过程调用超时

    我有一个导入到 EF4 中的存储过程 当我在 30 秒后使用某些参数调用它时 它会抛出超时错误 在 SQL Server Profiler 中 我可以看到使用正确参数的存储过程调用仅花费了 30 秒多一点 这是我的应用程序的超时时间 然而
  • Spring MVC:当未指定内容类型时@RequestBody

    我有一个 Spring MVC 应用程序 它以 JSON 字符串的形式从外部系统接收 HTTP 请求 其响应的返回方式与 JSON 字符串类似 我的控制器正确注释为 RequestBody and ResponseBody我有集成测试 它实