我以前使用过以下版本的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;
}
}
}
targetType
和targetClass
个变量是java.朗,反对.
这是循环正在迭代的消息转换器列表:
这是contentType
的值:
所以,当被问及以下问题时,每个转换器都返回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版本之间发生了什么变化,这段代码现在正在执行(并引发了一个异常),而以前没有?