Я пытаюсь подключить мой набор конечных точек RESTful (которые зависят от реализации Jersey 2 JAX-RS) от стороннего Spring MVC веб-приложения, работающего поверх Tomcat 9.
Мне не разрешено изменять web.xml
, и мой плагин недоступен при запуске контейнера, поэтому конфигурация на основе аннотаций не разрешенавариант, либо. Это означает, что я не могу развернуть Джерси ServletContainer
напрямую.
То, что я am разрешено, подключается к SpringMVC , а именно
- создание пользовательского частичного Spring контекста приложения и
- реализация пользовательского
Controller
, поэтому янаписали эту наивную реализацию, которая на самом деле близка к стандартному ServletWrappingController
:
package com.example
import org.glassfish.jersey.server.ResourceConfig
import org.glassfish.jersey.servlet.ServletContainer
import org.springframework.web.servlet.ModelAndView
import org.springframework.web.servlet.mvc.AbstractController
import java.io.IOException
import java.util.Collections
import java.util.Enumeration
import javax.servlet.ServletConfig
import javax.servlet.ServletContext
import javax.servlet.http.HttpServletRequest
import javax.servlet.http.HttpServletResponse
class JerseyServletInitializer(servletContext: ServletContext,
componentClasses: List<Class<*>>,
components: List<Any>): AbstractController() {
private val servletContainer: ServletContainer
init {
val resourceConfig = ResourceConfig()
.apply {
// Register org.glassfish.jersey.media.multipart.MultiPartFeature
componentClasses.forEach { clazz ->
register(clazz)
}
// Register REST endpoints
components.forEach { component ->
register(component)
}
}
// Provide a dummy ServletConfig
val servletConfig = object : ServletConfig {
override fun getInitParameter(name: String): String? = null
override fun getInitParameterNames(): Enumeration<String> = emptyList<String>().let {
Collections.enumeration(it)
}
override fun getServletName(): String = ServletContainer::class.java.name
override fun getServletContext(): ServletContext = servletContext
}
// Create and initialize Jersey's ServletContainer
servletContainer = ServletContainer(resourceConfig).apply {
init(servletConfig)
}
setSupportedMethods("GET", "POST", "PUT", "OPTIONS")
}
@Throws(IOException::class)
override fun handleRequestInternal(request: HttpServletRequest,
response: HttpServletResponse): ModelAndView? {
// Delegate all HTTP requests to Jersey
servletContainer.service(request, response)
return null
}
}
Вышеприведенное работает отлично, за исключением сценария загрузки файла (т. е. * 1040)* with Content-Type: multipart/form-data
).
Spring обнаруживает такие запросы и преобразует обычный HttpServletRequest
в MultipartHttpServletRequest
, считывая и исчерпывая его входной поток (mark()
/ reset()
не поддерживаются). В веб-приложении есть пользовательский MultipartResolver
, на который я никак не могу повлиять:
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"/>
Это означает, что Джерси получает составную часть HTTP POST
спустое тело запроса, выбрасывает MIMEParsingException
и отвечает HTTP 400 Bad Request
.
Насколько я понимаю, многочастное разрешение может быть отключено только глобально, а не для каждого контроллерабазис ( 1 , 2 , 3 ).
Вопросы:
- Каков наилучший способ работы с
MultipartResolver
, чтобы Джерси получал неизмененный запрос? - В качестве альтернативы, вы можете порекомендовать API для пересоздания тела запроса (то есть делает ли обратное тому, что делает
MultipartResolver
и кормит нового HttpServletRequest
на Джерси )? Это, конечно, можно сделать вручную, но я бы предпочел использовать существующие библиотеки.