我以前使用过以下版本的Spring+Spring Boot:

<springVersion>5.0.16.RELEASE</springVersion>
<springBootVersion>2.0.5.RELEASE</springBootVersion>

以及以下处理多部分/混合上传的类.使用上面的Spring版本,这些都可以正常工作:

突然燃烧.Java

import io.swagger.annotations.*;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import javax.validation.Valid;
import javax.validation.constraints.*;
@javax.annotation.Generated(value = "io.swagger.codegen.v3.generators.java.SpringCodegen", date = "2022-05-10T11:14:13.587+01:00[Europe/London]")
@Api(value = "Staging", description = "API")
public interface StagingApi {

    @ApiOperation(value = "", nickname = "createOrReplaceBatch", notes = "", tags={  })
    @ApiResponses(value = { 
        @ApiResponse(code = 200, message = ""),
        @ApiResponse(code = 400, message = ""),
        @ApiResponse(code = 500, message = "") })
    @RequestMapping(value = "/batches/{batchId}",
        consumes = { "multipart/mixed" },
        method = RequestMethod.PUT)
    ResponseEntity<Void> createOrReplaceBatch(
        @ApiParam(value = "" ,required=true) @RequestHeader(value="X-TENANT-ID", required=true) String X_TENANT_ID,
        @Size(min=1) @ApiParam(value = "",required=true) @PathVariable("batchId") String batchId,
        @ApiParam(value = ""  )  @Valid @RequestBody Object body);
}

stage控制器.Java

public ResponseEntity<Void> createOrReplaceBatch(
    @ApiParam(value = "Identifies the tenant making the request.", required = true)
    @RequestHeader(value = "X-TENANT-ID", required = true) String X_TENANT_ID,
    @Size(min = 1) @ApiParam(value = "Identifies the batch.", required = true)
    @PathVariable("batchId") String batchId,
    Object body)
{

    final ServletFileUpload fileUpload = new ServletFileUpload();
    final FileItemIterator fileItemIterator;
    try {
        fileItemIterator = fileUpload.getItemIterator(request);
    } catch (final FileUploadException | IOException ex) {
        LOGGER.error("Error getting FileItemIterator", ex);
        throw new WebMvcHandledRuntimeException(HttpStatus.BAD_REQUEST, ex.getMessage());
    }
    try {
        batchDao.saveFiles(new TenantId(X_TENANT_ID), new BatchId(batchId), fileItemIterator);
        return new ResponseEntity<>(HttpStatus.OK);
    } catch (final InvalidTenantIdException | InvalidBatchIdException | IncompleteBatchException | InvalidBatchException ex) {
        throw new WebMvcHandledRuntimeException(HttpStatus.BAD_REQUEST, ex.getMessage());
    } catch (final StagingException ex) {
        throw new WebMvcHandledRuntimeException(HttpStatus.INTERNAL_SERVER_ERROR, ex.getMessage());
    }
}

已成功处理请求的日志(log):

[2022-05-10 11:12:14.861Z #bc7.042 DEBUG -            -   ] o.s.w.s.m.m.a.RequestMappingHandlerMapping: Looking up handler method for path /batches/test-batch
[2022-05-10 11:12:14.861Z #bc7.042 DEBUG -            -   ] o.s.w.a.FixedContentNegotiationStrategy: Requested media types: [application/json, */*]
[2022-05-10 11:12:14.861Z #bc7.042 DEBUG -            -   ] o.s.w.a.FixedContentNegotiationStrategy: Requested media types: [application/json, */*]
[2022-05-10 11:12:14.862Z #bc7.042 DEBUG -            -   ] o.s.w.a.FixedContentNegotiationStrategy: Requested media types: [application/json, */*]
[2022-05-10 11:12:14.863Z #bc7.042 DEBUG -            -   ] o.s.w.s.m.m.a.RequestMappingHandlerMapping: Returning handler method [public org.springframework.http.ResponseEntity<java.lang.Void> com.acme.corp.staging.StagingController.createOrReplaceBatch(java.lang.String,java.lang.String,java.lang.Object)]
[2022-05-10 11:12:14.863Z #bc7.042 DEBUG -            -   ] o.s.b.f.s.DefaultListableBeanFactory: Returning cached instance of singleton bean 'stagingController'
[2022-05-10 11:12:14.866Z #bc7.042 DEBUG -            -   ] o.s.b.w.s.f.OrderedRequestContextFilter: Bound request context to thread: org.apache.catalina.connector.RequestFacade@37f2254a
[2022-05-10 11:12:14.870Z #bc7.042 DEBUG -            -   ] o.s.w.s.DispatcherServlet: DispatcherServlet with name 'dispatcherServlet' processing PUT request for [/batches/test-batch]
[2022-05-10 11:12:14.871Z #bc7.042 DEBUG -            -   ] o.s.w.s.m.m.a.RequestMappingHandlerMapping: Looking up handler method for path /batches/test-batch
[2022-05-10 11:12:14.871Z #bc7.042 DEBUG -            -   ] o.s.w.a.FixedContentNegotiationStrategy: Requested media types: [application/json, */*]
[2022-05-10 11:12:14.871Z #bc7.042 DEBUG -            -   ] o.s.w.a.FixedContentNegotiationStrategy: Requested media types: [application/json, */*]
[2022-05-10 11:12:14.871Z #bc7.042 DEBUG -            -   ] o.s.w.a.FixedContentNegotiationStrategy: Requested media types: [application/json, */*]
[2022-05-10 11:12:14.871Z #bc7.042 DEBUG -            -   ] o.s.w.s.m.m.a.RequestMappingHandlerMapping: Returning handler method [public org.springframework.http.ResponseEntity<java.lang.Void> com.acme.corp.staging.StagingController.createOrReplaceBatch(java.lang.String,java.lang.String,java.lang.Object)]
[2022-05-10 11:12:14.871Z #bc7.042 DEBUG -            -   ] o.s.b.f.s.DefaultListableBeanFactory: Returning cached instance of singleton bean 'stagingController'
[2022-05-10 11:12:14.932Z #bc7.042 DEBUG -            10c9] o.s.w.a.FixedContentNegotiationStrategy: Requested media types: [application/json, */*]
[2022-05-10 11:12:14.933Z #bc7.042 DEBUG -        -       ] o.s.w.s.DispatcherServlet: Null ModelAndView returned to DispatcherServlet with name 'dispatcherServlet': assuming HandlerAdapter completed request handling
[2022-05-10 11:12:14.933Z #bc7.042 DEBUG -        -       ] o.s.w.s.DispatcherServlet: Successfully completed request

但是,当我try 将Spring和Spring Boot的版本更新为:

<springVersion>5.2.4.RELEASE</springVersion>
<springBootVersion>2.2.6.RELEASE</springBootVersion>

同一请求失败,响应类型为415不受支持的_MEDIA_.

失败请求的日志(log):

[2022-05-10 11:31:52.158Z #dc2.024 INFO  -            -   ] o.a.c.c.C..localhost.: Initializing Spring DispatcherServlet 'dispatcherServlet'
[2022-05-10 11:31:52.158Z #dc2.024 INFO  -            -   ] o.s.w.s.DispatcherServlet: Initializing Servlet 'dispatcherServlet'
[2022-05-10 11:31:52.158Z #dc2.024 DEBUG -            -   ] o.s.w.s.DispatcherServlet: Detected StandardServletMultipartResolver
[2022-05-10 11:31:52.162Z #dc2.024 DEBUG -            -   ] o.s.w.s.DispatcherServlet: enableLoggingRequestDetails='false': request parameters and headers will be masked to prevent unsafe logging of potentially sensitive data
[2022-05-10 11:31:52.162Z #dc2.024 INFO  -            -   ] o.s.w.s.DispatcherServlet: Completed initialization in 4 ms
[2022-05-10 11:31:52.165Z #dc2.024 DEBUG -            -   ] o.s.w.s.DispatcherServlet: PUT "/batches/test-batch", parameters={}
[2022-05-10 11:31:52.192Z #dc2.024 DEBUG -            -   ] o.s.w.s.m.m.a.RequestMappingHandlerMapping: Mapped to com.acme.corp.staging.StagingController#createOrReplaceBatch(String, String, Object)
[2022-05-10 11:31:52.202Z #dc2.024 DEBUG -            a17e] o.s.w.s.m.m.a.ServletInvocableHandlerMethod: Could not resolve parameter [2] in public org.springframework.http.ResponseEntity<java.lang.Void> com.acme.corp.staging.StagingController.createOrReplaceBatch(java.lang.String,java.lang.String,java.lang.Object): Content type 'multipart/mixed;boundary=efb8369b-607b-4dcf-9f92-e6cd8244db1e;charset=UTF-8' not supported
[2022-05-10 11:31:52.204Z #dc2.024 DEBUG -            a17e] o.s.w.s.m.m.a.ExceptionHandlerExceptionResolver: Using @ExceptionHandler acme.corp.staging.exceptions.WebMvcExceptionHandler#handleException(Exception, WebRequest)
[2022-05-10 11:31:52.206Z #dc2.024 DEBUG -            a17e] o.s.w.s.m.m.a.HttpEntityMethodProcessor: No match for [application/json, */*], supported: []
[2022-05-10 11:31:52.206Z #dc2.024 DEBUG -            a17e] o.s.w.s.m.m.a.ExceptionHandlerExceptionResolver: Resolved [org.springframework.web.HttpMediaTypeNotSupportedException: Content type 'multipart/mixed;boundary=efb8369b-607b-4dcf-9f92-e6cd8244db1e;charset=UTF-8' not supported]
[2022-05-10 11:31:52.206Z #dc2.024 DEBUG -            a17e] o.s.w.s.DispatcherServlet: Completed 415 UNSUPPORTED_MEDIA_TYPE

调试这个,我发现:

当我使用最新的Spring版本向上述端点发送PUT请求时,AbstractMessageConverterMethodArgumentResolver被称为:

它循环浏览消息转换器列表,并判断是否有任何转换器响应请求:

for (HttpMessageConverter<?> converter : this.messageConverters) {
    Class<HttpMessageConverter<?>> converterType = (Class<HttpMessageConverter<?>>) converter.getClass();
    GenericHttpMessageConverter<?> genericConverter =
            (converter instanceof GenericHttpMessageConverter ? (GenericHttpMessageConverter<?>) converter : null);
    if (genericConverter != null ? genericConverter.canRead(targetType, contextClass, contentType) :
            (targetClass != null && converter.canRead(targetClass, contentType))) {
        if (message.hasBody()) {
            HttpInputMessage msgToUse =
                    getAdvice().beforeBodyRead(message, parameter, targetType, converterType);
            body = (genericConverter != null ? genericConverter.read(targetType, contextClass, msgToUse) :
                    ((HttpMessageConverter<T>) converter).read(targetClass, msgToUse));
            body = getAdvice().afterBodyRead(body, msgToUse, parameter, targetType, converterType);
        }
        else {
            body = getAdvice().handleEmptyBody(null, message, parameter, targetType, converterType);
        }
        break;
    }
}
}

targetTypetargetClass个变量是java.朗,反对.

这是循环正在迭代的消息转换器列表:

enter image description here

这是contentType的值:

enter image description here

所以,当被问及以下问题时,每个转换器都返回false:

canRead(targetClass=java.lang.Object, contentType=multipart/mixed; boundary=efb8369b-607b-4dcf-9f92-e6cd8244db1e;charset=UTF-8

这会导致body没有被赋予一个值,并抛出一个HttpMediaTypeNotSupportedException :

if (body == NO_VALUE) {
    if (httpMethod == null || !SUPPORTED_METHODS.contains(httpMethod) ||
            (noContentType && !message.hasBody())) {
        return null;
    }
    throw new HttpMediaTypeNotSupportedException(contentType,
            getSupportedMediaTypes(targetClass != null ? targetClass : Object.class));
}

我试图使用我的原始帖子中列出的较旧的Spring版本逐步完成相同的代码(例如,为了查看早期的Spring版本是否有更多的消息转换器):

<springVersion>5.0.16.RELEASE</springVersion>
<springBootVersion>2.0.5.RELEASE</springBootVersion>

但我发现,循环通过消息转换器的代码并不是使用这些较旧的Spring版本执行的

我不确定这些Spring版本之间发生了什么变化,这段代码现在正在执行(并引发了一个异常),而以前没有?

推荐答案

见@wilkinsona answer spring 开机版本:

Spring Framework 5.1还支持多部分PUT请求.这意味着Spring MVC现在正在读取正文,使其无法用于commons上传.

您应该切换到使用MVC的内置多部分支持.有几种不同的方法可以做到这一点,例如:

@RestController
public class StagingController implements StagingApi
{    
    public ResponseEntity<Void> createOrReplaceBatch(@PathVariable("batchId") String batchId, MultipartHttpServletRequest request)
    {
        try {
            Collection<Part> parts = request.getParts();

Java相关问答推荐

基本时态运算的ISO-8601周数据表示法

Chunk(Int)已弃用并标记为要删除

当返回Mono<;Something>;时,不会调用Mono<;void>;.flatMap

如何在Microronaut中将 map 读取为 map

声明MessageChannel Bean的首选方式

WebSockets和Spring Boot安全性出现错误401

如何创建模块信息类文件并将其添加到JAR中?

从LineChart<;字符串、字符串和gt;中删除数据时出现特殊的ClassCastException;

Domino中不同的java.Protocol.handler.pkgs设置在XPages Java中导致错误

如果第一位数字和最后一位数字相差超过一位,您将如何获得随机数?

AWS Java SDK v2.x中没有setObjectAcl方法

有没有办法知道在合并中执行了什么操作?

在Java泛型中使用通配符时,如何推断类型

判断重复的两个二维表算法?

如何在Maven Central上部署?

无法使用Open WebStart Java 8运行jnlp

Java System.getProperty在哪里检索user.home?

读取ConcurrentHashMap中的可变对象

SonarQube在合并升级到java17后对旧代码提出错误

在java中使用SevenZip.openArchive方法后无法删除文件