form - spring mvc tags
Spring Boot con dos configuraciones de MVC (4)
Ampliando mi comentario de ayer y la idea de @Ashoka Header propongo registrar 2 MessageConverters (heredados y actuales) para tipos de medios personalizados. Puedes hacer esto así:
@Bean
MappingJackson2HttpMessageConverter currentMappingJackson2HttpMessageConverter() {
MappingJackson2HttpMessageConverter jsonConverter = new MappingJackson2HttpMessageConverter();
ObjectMapper objectMapper = new ObjectMapper();
// set features
jsonConverter.setObjectMapper(objectMapper);
jsonConverter.setSupportedMediaTypes(Arrays.asList(new MediaType("json", "v2")));
return jsonConverter;
}
@Bean
MappingJackson2HttpMessageConverter legacyMappingJackson2HttpMessageConverter() {
MappingJackson2HttpMessageConverter jsonConverter = new MappingJackson2HttpMessageConverter();
ObjectMapper objectMapper = new ObjectMapper();
// set features
jsonConverter.setObjectMapper(objectMapper);
return jsonConverter;
}
Preste atención al tipo de medio personalizado para uno de los convertidores.
Si lo desea, puede usar un interceptor para reescribir los encabezados de versión propuestos por @Ashoka a un tipo de medio personalizado como ese:
public class ApiVersionMediaTypeMappingInterceptor extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
try {
if(request.getHeader("X-API-Version") == "2") {
request.setAttribute("Accept:","json/v2");
}
.....
}
}
Puede que esta no sea la respuesta exacta que estabas buscando, pero tal vez te sirva de inspiración. Un interceptor está registrado como tal .
Tengo una aplicación Spring Boot con una API REST, usando Jackson para la configuración de vista JSON. Funciona muy bien y puedo obtener toda la bondad de Spring Boot.
Sin embargo, necesito agregar una API REST adicional que sea similar pero con configuraciones diferentes. Por ejemplo, entre otras cosas, necesita una configuración de asignador de objetos Jackson diferente porque el JSON se verá bastante diferente (por ejemplo, no hay matrices JSON). Ese es solo un ejemplo, pero hay bastantes diferencias. Cada API tiene un contexto diferente (por ejemplo, / api / current y / api / legacy).
Idealmente, me gustaría que dos configuraciones de MVC se asignaran a estos contextos diferentes, y no tuviera que renunciar al cableado automático de las cosas en el arranque.
Hasta ahora todo lo que he podido acercarme es usar dos servlets de despachador cada uno con su propia configuración de MVC, pero eso da como resultado que Boot deje caer un montón de cosas que obtengo de manera automática y, básicamente, anula el motivo por el que usas boot.
No puedo dividir la aplicación en varias aplicaciones.
La respuesta "no puedes hacer esto con Boot y aún así obtener toda su magia" es una respuesta aceptable. Parece que debería ser capaz de manejar esto sin embargo.
En Spring-boot ypu puede usar diferentes perfiles (como dev
y test
).
Inicie la aplicación con -Dspring.profiles.active=dev
o -Dspring.profiles.active=test
y use diferentes archivos de propiedades llamados application-dev.properties
o application-test.properties
dentro de su directorio de properties
. Eso podría hacer el problema.
Hay varias formas de lograr esto. En función de su requerimiento, diría que este es un caso de administración de versiones de REST API. Hay varias formas de versionar la API REST, algunas de las cuales son urls de versión y otras técnicas mencionadas en los enlaces de los comentarios. El enfoque basado en URL es más impulsado a tener múltiples versiones de la dirección:
Por ejemplo para V1 :
/path/v1/resource
y V2 :
/path/v2/resource
Esto resolverá dos métodos diferentes en el bean Spring MVC Controller, al que se delegan las llamadas.
La otra opción para resolver las versiones de la API es usar los encabezados, de esta manera solo hay URL, múltiples métodos basados en la versión. Por ejemplo:
/path/resource
ENCABEZAMIENTO:
X-API-Version: 1.0
ENCABEZAMIENTO:
X-API-Version: 2.0
Esto también se resolverá en dos operaciones separadas en el controlador.
Ahora estas son las estrategias basadas en las múltiples versiones de descanso que pueden manejarse.
Los enfoques anteriores se explican bien en lo siguiente: git example
Nota: Lo anterior es una aplicación de arranque de primavera.
Lo común en estos dos enfoques es que tendrá que haber diferentes POJOS basados en la biblioteca Jackson JSON para ordenar automáticamente las instancias del tipo especificado en JSON.
Es decir, suponiendo que el código usa @RestController [org.springframework.web.bind.annotation.RestController]
Ahora bien, si su requisito es tener diferentes JSON Mapper, es decir, diferentes configuraciones de mapeadores JSON, entonces, independientemente de los contextos de Spring, necesitará una estrategia diferente para la serialización / des-serialización.
En este caso, deberá implementar un De-Serializador personalizado {CustomDeSerializer} que extenderá JsonDeserializer<T>
[com.fasterxml.jackson.databind.JsonDeserializer]
y en deserialize()
implementará su estrategia personalizada.
Utilice la @JsonDeserialize(using = CustomDeSerializer.class)
en el POJO de destino.
De esta forma, se pueden gestionar múltiples esquemas JSON con diferentes De-Serializers.
Al combinar Resting Versioning + Custom Serialization Strategy, cada API puede administrarse en su propio contexto sin tener que conectar múltiples configuraciones de Servlet de despachador.
Si puede vivir con un puerto diferente para cada contexto, solo tiene que sobrescribir los beans DispatcherServletAutoConfiguration
. Todo el resto de la magia funciona, varias partes, Jackson, etc. Puede configurar Servlet y Jackson / Multipart etc. para cada contexto secundario por separado e inyectar bean del contexto padre.
package test;
import static org.springframework.boot.autoconfigure.web.DispatcherServletAutoConfiguration.DEFAULT_DISPATCHER_SERVLET_BEAN_NAME;
import static org.springframework.boot.autoconfigure.web.DispatcherServletAutoConfiguration.DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.context.embedded.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
@Configuration
@EnableAutoConfiguration(exclude = {
Application.Context1.class,
Application.Context2.class
})
public class Application extends WebMvcConfigurerAdapter {
@Bean
public TestBean testBean() {
return new TestBean();
}
public static void main(String[] args) {
final SpringApplicationBuilder builder = new SpringApplicationBuilder().parent(Application.class);
builder.child(Context1.class).run();
builder.child(Context2.class).run();
}
public static class TestBean {
}
@Configuration
@EnableAutoConfiguration(exclude = {Application.class, Context2.class})
@PropertySource("classpath:context1.properties")
public static class Context1 {
@Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
DispatcherServlet dispatcherServlet() {
DispatcherServlet dispatcherServlet = new DispatcherServlet();
// custom config here
return dispatcherServlet;
}
@Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
ServletRegistrationBean dispatcherServletRegistration() {
ServletRegistrationBean registration = new ServletRegistrationBean(dispatcherServlet(), "/test1");
registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
// custom config here
return registration;
}
@Bean
Jackson2ObjectMapperBuilder jackson2ObjectMapperBuilder(TestBean testBean) {
System.out.println(testBean);
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
// custom config here
return builder;
}
}
@Configuration
@EnableAutoConfiguration(exclude = {Application.class, Context1.class})
@PropertySource("classpath:context2.properties")
public static class Context2 {
@Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
DispatcherServlet dispatcherServlet() {
DispatcherServlet dispatcherServlet = new DispatcherServlet();
// custom config here
return dispatcherServlet;
}
@Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
ServletRegistrationBean dispatcherServletRegistration() {
ServletRegistrationBean registration = new ServletRegistrationBean(dispatcherServlet(), "/test2");
registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
// custom config here
return registration;
}
@Bean
Jackson2ObjectMapperBuilder jackson2ObjectMapperBuilder(TestBean testBean) {
System.out.println(testBean);
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
// custom config here
return builder;
}
}
}
Los archivos context1/2.properties
actualmente solo contienen un server.port=8080/8081
pero puede establecer todas las otras propiedades de primavera para los contextos secundarios allí.