带有 multipart/form-data 的请求返回 415 错误

2024-02-04

我需要使用以下方式接收此请求Spring:

POST /test HTTP/1.1
user-agent: Dart/2.8 (dart:io)
content-type: multipart/form-data; boundary=--dio-boundary-3791459749
accept-encoding: gzip
content-length: 151
host: 192.168.0.107:8443

----dio-boundary-3791459749
content-disposition: form-data; name="MyModel"

{"testString":"hello world"}
----dio-boundary-3791459749--

但不幸的是这Spring端点:

@PostMapping(value = "/test", consumes = MediaType.APPLICATION_OCTET_STREAM_VALUE)
public void test(@Valid @RequestPart(value = "MyModel") MyModel myModel) {
    String testString = myModel.getTestString();
}

returns 415 error:

Content type 'multipart/form-data;boundary=--dio-boundary-2534440849' not supported

给客户。

这(相同的端点但带有consumes = MULTIPART_FORM_DATA_VALUE):

@PostMapping(value = "/test", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public void test(@Valid @RequestPart(value = "MyModel") MyModel myModel) {
    String testString = myModel.getTestString();
}

再次返回415但是,有这样的消息:

Content type 'application/octet-stream' not supported

我已经成功使用了这个端点(即使没有consumes)与这个旧请求:

POST /test HTTP/1.1
Content-Type: multipart/form-data; boundary=62b81b81-05b1-4287-971b-c32ffa990559
Content-Length: 275
Host: 192.168.0.107:8443
Connection: Keep-Alive
Accept-Encoding: gzip
User-Agent: okhttp/3.8.0

--62b81b81-05b1-4287-971b-c32ffa990559
Content-Disposition: form-data; name="MyModel"
Content-Transfer-Encoding: binary
Content-Type: application/json; charset=UTF-8
Content-Length: 35

{"testString":"hello world"}
--62b81b81-05b1-4287-971b-c32ffa990559--

但不幸的是,现在我需要使用第一个描述的请求,并且无法向其中添加其他字段。

所以,我需要改变Spring终点,但是如何呢?


你需要让你的控制器方法消耗MediaType.MULTIPART_FORM_DATA_VALUE,

@PostMapping(value = "/test", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
......

您还需要添加一个MappingJackson2HttpMessageConverter支持application/octet-stream。在这个答案中,

  • 我通过使用配置它WebMvcConfigurer#extendMessageConverters这样我就可以保留其他转换器的默认配置。(Spring MVC 使用 Spring Boot 的转换器进行配置)。
  • 我从创建转换器ObjectMapperSpring 使用的实例。

[了解更多信息]
Spring Boot 参考文档 - Spring MVC 自动配置 https://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/#boot-features-spring-mvc-auto-configuration
如何获取 Spring 4.1 使用的 Jackson ObjectMapper? https://stackoverflow.com/questions/30060006/how-do-i-obtain-the-jackson-objectmapper-in-use-by-spring-4-1
为什么即使配置了从不处理 JSON 的自定义转换器,Spring Boot 也会更改 JSON 响应的格式? https://stackoverflow.com/questions/60462767/why-does-spring-boot-change-the-format-of-a-json-response-even-when-a-custom-con

@Configuration
public class MyConfigurer implements WebMvcConfigurer {

    @Autowired
    private ObjectMapper objectMapper;

    @Override
    public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {

        ReadOnlyMultipartFormDataEndpointConverter converter = new ReadOnlyMultipartFormDataEndpointConverter(
                objectMapper);
        List<MediaType> supportedMediaTypes = new ArrayList<>();
        supportedMediaTypes.addAll(converter.getSupportedMediaTypes());
        supportedMediaTypes.add(MediaType.APPLICATION_OCTET_STREAM);
        converter.setSupportedMediaTypes(supportedMediaTypes);

        converters.add(converter);
    }

}

[NOTE]
您还可以通过扩展转换器来修改转换器的行为。
在这个答案中,我扩展了MappingJackson2HttpMessageConverter以便

  • 仅当映射的控制器方法仅消耗时才读取数据MediaType.MULTIPART_FORM_DATA_VALUE
  • 它不写任何响应(另一个转换器这样做)。
public class ReadOnlyMultipartFormDataEndpointConverter extends MappingJackson2HttpMessageConverter {

    public ReadOnlyMultipartFormDataEndpointConverter(ObjectMapper objectMapper) {
        super(objectMapper);
    }

    @Override
    public boolean canRead(Type type, Class<?> contextClass, MediaType mediaType) {
        // When a rest client(e.g. RestTemplate#getForObject) reads a request, 'RequestAttributes' can be null.
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        if (requestAttributes == null) {
            return false;
        }
        HandlerMethod handlerMethod = (HandlerMethod) requestAttributes
                .getAttribute(HandlerMapping.BEST_MATCHING_HANDLER_ATTRIBUTE, RequestAttributes.SCOPE_REQUEST);
        if (handlerMethod == null) {
            return false;
        }
        RequestMapping requestMapping = handlerMethod.getMethodAnnotation(RequestMapping.class);
        if (requestMapping == null) {
            return false;
        }
        // This converter reads data only when the mapped controller method consumes just 'MediaType.MULTIPART_FORM_DATA_VALUE'.
        if (requestMapping.consumes().length != 1
                || !MediaType.MULTIPART_FORM_DATA_VALUE.equals(requestMapping.consumes()[0])) {
            return false;
        }
        return super.canRead(type, contextClass, mediaType);
    }

//      If you want to decide whether this converter can reads data depending on end point classes (i.e. classes with '@RestController'/'@Controller'),
//      you have to compare 'contextClass' to the type(s) of your end point class(es).
//      Use this 'canRead' method instead.
//      @Override
//      public boolean canRead(Type type, Class<?> contextClass, MediaType mediaType) {
//          return YourEndpointController.class == contextClass && super.canRead(type, contextClass, mediaType);
//      }

    @Override
    protected boolean canWrite(MediaType mediaType) {
        // This converter is only be used for requests.
        return false;
    }
}



产生的原因415 errors

当你的控制器方法消耗时MediaType.APPLICATION_OCTET_STREAM_VALUE,它不处理请求Content-Type: multipart/form-data;。因此你得到415.

另一方面,当你的控制器方法消耗MediaType.MULTIPART_FORM_DATA_VALUE,它可以处理请求Content-Type: multipart/form-data;。然而 JSON 没有Content-Type根据您的配置不进行处理。
当您用注释方法参数时@RequestPart注解,

  • RequestPartMethodArgumentResolver解析一个请求。
  • RequestPartMethodArgumentResolver将内容类型识别为application/octet-stream当没有指定时。
  • RequestPartMethodArgumentResolver uses a MappingJackson2HttpMessageConverter解析请求正文并获取 JSON。
  • 默认配置MappingJackson2HttpMessageConverter仅支持 application/json 和 application/*+json。
  • (据我读到你的问题)你的MappingJackson2HttpMessageConverter似乎不支持application/octet-stream.(因此你得到415.)



结论

因此我认为你可以通过让MappingJackson2HttpMessageConverter(一个实现HttpMessageConverter) 支持application/octet-stream像上面一样。


[更新1]

如果您不需要验证MyModel with @Valid注释并且只想将 JSON 主体转换为MyModel, @RequestParam可能有用。
如果您选择此解决方案,您就可以NOT必须配置MappingJackson2HttpMessageConverter支持application/octet-stream.
使用此解决方案,您不仅可以处理 JSON 数据,还可以处理文件数据。

@PostMapping(value = "/test", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public void test(@RequestParam(value = "MyModel") Part part) throws IOException {

    // 'part' is an instance of 'javax.servlet.http.Part'.
    // According to javadoc of 'javax.servlet.http.Part',
    // 'The part may represent either an uploaded file or form data'

    try (InputStream is = part.getInputStream()) {
        ObjectMapper objectMapper = new ObjectMapper();
        MyModel myModel = objectMapper.readValue(part.getInputStream(), MyModel.class);

        .....
    }
    .....
}

See Also

RequestPartMethodArgumentResolver 的 Javadoc https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/servlet/mvc/method/annotation/RequestPartMethodArgumentResolver.html
MappingJackson2HttpMessageConverter 的 Javadoc https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/http/converter/json/MappingJackson2HttpMessageConverter.html
不支持空白内容类型 https://stackoverflow.com/questions/60207486/content-type-blank-is-not-supported/60213085#60213085(相关问题)
Spring Web MVC - 多部分 https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#mvc-multipart-forms

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

带有 multipart/form-data 的请求返回 415 错误 的相关文章

随机推荐

  • 如何使用 ASP.NET Core v2 在 Visual Studio 2017 中安装 font-awesome

    我正在使用 ASP NET Core v2 构建一个网页 并且想使用 font awesome 首先我要说的是我已经尝试了几件事 比如从 NPM 安装 Bower 从 NPM 安装 font awesome 从 VS 中的 Bower 包安
  • 如何将数据集写入Kafka主题?

    我正在使用 Spark 2 1 0 和 Kafka 0 9 0 我正在尝试将批处理 Spark 作业的输出推送到 kafka 该作业应该每小时运行一次 但不是流式运行 在网上寻找答案时 我只能找到 kafka 与 Spark 流的集成 而没
  • 如何抑制“SpriteKit 纹理图集生成器警告”?

    Xcode 报告以下警告 SpriteKit 纹理图集生成器警告由于输入纹理尺寸而将 images atlas 拆分为 2 个纹理图集 虽然我将来会修改我的图像以避免 SpriteKit 生成此警告 但我想使用一些 pragma 指令暂时抑
  • 点击事件中的全日历错误时间

    我正在使用角度和全日历 我有 令人惊讶的 时区问题 而且我似乎无法解决问题 如果在周视图中我点击 08 00 我打开一个模式并显示时间 我会看到 09 00 timezone Europe Brussels ignoreTimezone f
  • 枕头调整像素图像大小 - Django/Pillow

    我正在 Django 中开发一个图像上传器 图像上传并保存到磁盘后 我正在尝试调整保存的图像的大小 同时保持其纵横比 我正在使用 Pillow 进行图像处理 调整大小 当我尝试调整图像大小时出现问题 即使调整大小的图像的长宽比与原始图像的长
  • 使用 PHP 和 jQuery 生成并下载 .csv 文件

    我想生成一个 csv 文件 然后使用 AJAX 下载它 在现场 csv php 我有这个代码
  • 错误错误:未捕获(承诺):QuotaExceededError Ionic 3

    我正在使用 ionic 3 创建一个应用程序 我正在 ionic 存储中保存一个字符串 我工作得很好 但突然出现这个错误 Home ts 这是我设置字符串的地方 this storage set 123 JSON stringify dat
  • 如何跳过 x 轴上的标签?

    我已经尝试了很长一段时间来弄清楚如何跳过该图表 x 轴上的标签 例如 显示每第三个或第四个标签 我尝试将 autoSkip 和 stepSize 添加到刻度配置中 但没有任何变化 有没有一种简单的方法可以做到这一点 Edit visitor
  • 如何使用 JavaScript 检测我的浏览器版本和操作系统?

    我尝试使用下面的代码 但它只在 Chrome 和 Mozilla 中显示结果 在 IE6 中不起作用 div div
  • matplotlib 轴标签出现奇怪错误

    我对 Python 和一般编程都很陌生 所以如果我遗漏了一些明显的东西 请提前道歉 我正在尝试绘制图表并标记轴 但每次尝试标记 y 轴时都会引发异常 我在新脚本中编写了下面的代码 以确保问题不是来自模块中的其他地方 我正在使用Python
  • 未知目录的重写规则

    所以我试图获取 mod rewrite 规则来将请求重定向到带有 htaccess 文件的 php 脚本 问题是 我希望它能够工作 无论我将项目放在网络服务器上的哪个位置 htaccess 文件和 php 脚本始终位于同一文件夹中 重写本身
  • 读取 XMLStreamReader 时出错。获取故障响应

    我开发了一个应用程序 用户应该填写申请表并在最后付款 要付款 用户应在下面给出的链接中填写详细信息 Payment https k2 karnataka gov in wps portal Khajane II Scope Remittan
  • 在 Ubuntu 上安装 Python3 的 mod_wsgi

    谁能给我一套在 Ubuntu for Python 3 上安装 mod wsgi 的清晰说明 我确实使用 Python3 成功获得了 Flask 和 mod wsgi 并且短暂地感到高兴 直到我查看 Apache 的日志并意识到我遇到了这个
  • UnicodeDecodeError:无效的起始字节

    我有一个关于 UnicodeDecodeError invalid start byte 的快速问题 我认为我的文本中的某个地方有非 UTF 8 字符 但错误消息的位置是读取文件的起点 所以我不知道如何修复它 如果您有任何建议 请告诉我 以
  • Mysql 如何获取给定年份和月份的第一个星期一

    如何获取给定年份月份的第一个星期一 SET YearMonth 201304 Result 2013 04 01 For April 2013 11 04 For November 提前致谢 Try this SET firstday 20
  • 使用 CSVHelper 动态映射嵌套对象

    我正在使用 CSVHelper 感谢 Josh Close 来读取 CSV 文件 效果非常好 我现在尝试使用它将该文件映射到一些内部类 但是 我映射的 CSV 因客户而异 但都需要映射到我的内部类 我需要允许客户定义 CSV 如何映射到我的
  • 安装失败,并显示消息“INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME”

    因此 我在项目中对包名称进行了一些重构 现在我无法再安装我的应用程序 大约在同一时间 我更新到了 android studio 的最新版本 我相信这可能是问题所在 因为我认为我在升级之前进行了重构 我只是不记得 100 这是我的清单
  • 金字塔图案中圆的绘制方法

    我想在 HTML 画布上以金字塔图案绘制圆形球 像这样 小提琴 你可以向我展示算法 https jsfiddle net ofxmr17c 3 https jsfiddle net ofxmr17c 3 var canvas documen
  • Android OpenGL ES 应用程序的屏幕截图

    我有一个在已添加的 GLSurfaceView 上运行的基本 openGL ES 20 应用程序 GLSurfaceView view new GLSurfaceView this view setRenderer new OpenGLRe
  • 带有 multipart/form-data 的请求返回 415 错误

    我需要使用以下方式接收此请求Spring POST test HTTP 1 1 user agent Dart 2 8 dart io content type multipart form data boundary dio bounda