Sto aggiornando un'applicazione dalla piattaforma Spring versione 1.1.3.RELEASE a 2.0.1.RELEASE, che esegue il bump della versione di Spring Framework dalla 4.1.7 alla 4.2.4, e Jackson dal 2.4.6 al 2.6.4. Non sembrano esserci stati cambiamenti significativi nella gestione di Spring o Jackson delle implementazioni personalizzate HttpMessageConverter
, ma la serializzazione JSON personalizzata non si verifica e non sono stato in grado di determinarne il motivo. Le seguenti opere eccellenti nella precedente versione Primavera Piattaforma:Custom Jackson HttpMessageConverter non funziona più nella primavera 4.2
Modello
@JsonFilter("fieldFilter")
public class MyModel {
/*model fields and methods*/
}
Modello involucro
public class ResponseEnvelope {
private Set<String> fieldSet;
private Set<String> exclude;
private Object entity;
public ResponseEnvelope(Object entity) {
this.entity = entity;
}
public ResponseEnvelope(Object entity, Set<String> fieldSet, Set<String> exclude) {
this.fieldSet = fieldSet;
this.exclude = exclude;
this.entity = entity;
}
public Object getEntity() {
return entity;
}
@JsonIgnore
public Set<String> getFieldSet() {
return fieldSet;
}
@JsonIgnore
public Set<String> getExclude() {
return exclude;
}
public void setExclude(Set<String> exclude) {
this.exclude = exclude;
}
public void setFieldSet(Set<String> fieldSet) {
this.fieldSet = fieldSet;
}
public void setFields(String fields) {
Set<String> fieldSet = new HashSet<String>();
if (fields != null) {
for (String field : fields.split(",")) {
fieldSet.add(field);
}
}
this.fieldSet = fieldSet;
}
}
controller
@Controller
public class MyModelController {
@Autowired MyModelRepository myModelRepository;
@RequestMapping(value = "/model", method = RequestMethod.GET, produces = { MediaType.APPLICATION_JSON_VALUE })
public HttpEntity find(@RequestParam(required=false) Set<String> fields, @RequestParam(required=false) Set<String> exclude){
List<MyModel> objects = myModelRepository.findAll();
ResponseEnvelope envelope = new ResponseEnvelope(objects, fields, exclude);
return new ResponseEntity<>(envelope, HttpStatus.OK);
}
}
personalizzato HttpMessageConverter
public class FilteringJackson2HttpMessageConverter extends MappingJackson2HttpMessageConverter {
private boolean prefixJson = false;
@Override
public void setPrefixJson(boolean prefixJson) {
this.prefixJson = prefixJson;
super.setPrefixJson(prefixJson);
}
@Override
protected void writeInternal(Object object, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException {
ObjectMapper objectMapper = getObjectMapper();
JsonGenerator jsonGenerator = objectMapper.getFactory().createGenerator(outputMessage.getBody());
try {
if (this.prefixJson) {
jsonGenerator.writeRaw(")]}', ");
}
if (object instanceof ResponseEnvelope) {
ResponseEnvelope envelope = (ResponseEnvelope) object;
Object entity = envelope.getEntity();
Set<String> fieldSet = envelope.getFieldSet();
Set<String> exclude = envelope.getExclude();
FilterProvider filters = null;
if (fieldSet != null && !fieldSet.isEmpty()) {
filters = new SimpleFilterProvider()
.addFilter("fieldFilter", SimpleBeanPropertyFilter.filterOutAllExcept(fieldSet))
.setFailOnUnknownId(false);
} else if (exclude != null && !exclude.isEmpty()) {
filters = new SimpleFilterProvider()
.addFilter("fieldFilter", SimpleBeanPropertyFilter.serializeAllExcept(exclude))
.setFailOnUnknownId(false);
} else {
filters = new SimpleFilterProvider()
.addFilter("fieldFilter", SimpleBeanPropertyFilter.serializeAllExcept())
.setFailOnUnknownId(false);
}
objectMapper.setFilterProvider(filters);
objectMapper.writeValue(jsonGenerator, entity);
} else if (object == null){
jsonGenerator.writeNull();
} else {
FilterProvider filters = new SimpleFilterProvider().setFailOnUnknownId(false);
objectMapper.setFilterProvider(filters);
objectMapper.writeValue(jsonGenerator, object);
}
} catch (JsonProcessingException e){
e.printStackTrace();
throw new HttpMessageNotWritableException("Could not write JSON: " + e.getMessage());
}
}
}
configurazione
@Configuration
@EnableWebMvc
public class WebServicesConfig extends WebMvcConfigurerAdapter {
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
FilteringJackson2HttpMessageConverter jsonConverter = new FilteringJackson2HttpMessageConverter();
jsonConverter.setSupportedMediaTypes(MediaTypes.APPLICATION_JSON);
converters.add(jsonConverter);
}
// Other configurations
}
Ora sto ottenendo questa eccezione (che viene catturato da Primavera e loggati) e un errore 500 quando si effettua qualsiasi tipo di richiesta:
[main] WARN o.s.w.s.m.s.DefaultHandlerExceptionResolver - Failed to write HTTP message:
org.springframework.http.converter.HttpMessageNotWritableException: Could not write content:
Can not resolve PropertyFilter with id 'fieldFilter';
no FilterProvider configured (through reference chain:
org.oncoblocks.centromere.web.controller.ResponseEnvelope["entity"]->java.util.ArrayList[0]);
nested exception is com.fasterxml.jackson.databind.JsonMappingException:
Can not resolve PropertyFilter with id 'fieldFilter';
no FilterProvider configured (through reference chain:
org.oncoblocks.centromere.web.controller.ResponseEnvelope["entity"]->java.util.ArrayList[0])
Il metodoviene eseguito, ma non sembra che il convertitore personalizzato sia mai stato utilizzato durante le richieste. È possibile che un altro convertitore di messaggi possa impedire a questo di raggiungere la mia risposta? La mia comprensione è stata che l'override di configureMessageConverters
avrebbe impedito l'utilizzo di convertitori diversi da quelli registrati manualmente.
Nessuna modifica è stata apportata tra le versioni funzionanti e non funzionanti di questo codice, oltre all'aggiornamento delle versioni di dipendenza tramite la piattaforma Spring. C'è stata qualche modifica nella serializzazione JSON che mi manca nella documentazione?
Modifica
Ulteriori rendimenti di test risultati strani. Volevo testare per verificare le seguenti cose:
- È in corso la registrazione della mia
HttpMessageConverter
personalizzata? - Un altro convertitore lo sovrascrive/lo sostituisce?
- Si tratta di un problema solo con la configurazione di prova?
Così, ho aggiunto un test in più e ha dato un'occhiata in uscita:
@Autowired WebApplicationContext webApplicationContext;
@Before
public void setup(){
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
}
@Test
public void test() throws Exception {
RequestMappingHandlerAdapter adapter = (RequestMappingHandlerAdapter) webApplicationContext.getBean("requestMappingHandlerAdapter");
List<EntrezGene> genes = EntrezGene.createDummyData();
Set<String> exclude = new HashSet<>();
exclude.add("entrezGeneId");
ResponseEnvelope envelope = new ResponseEnvelope(genes, new HashSet<String>(), exclude);
for (HttpMessageConverter converter: adapter.getMessageConverters()){
System.out.println(converter.getClass().getName());
if (converter.canWrite(ResponseEnvelope.class, MediaType.APPLICATION_JSON)){
MockHttpOutputMessage message = new MockHttpOutputMessage();
converter.write((Object) envelope, MediaType.APPLICATION_JSON, message);
System.out.println(message.getBodyAsString());
}
}
}
... e funziona benissimo. L'oggetto envelope
e il suo contenuto sono serializzati e filtrati correttamente. Quindi, c'è un problema con la gestione delle richieste prima che raggiunga i convertitori di messaggi, oppure c'è stato un cambiamento nel modo in cui MockMvc
sta testando le richieste.
goccia un punto di interruzione in 'configureMessageConverters' e assicurarsi che sia in esecuzione. – chrylis
@chrylis: già provato, il metodo di configurazione è in esecuzione. – woemler
Potete fornire lo stacktrace completo? –