如何在Spring MVC中基于http请求头启用json的动态漂亮打印?
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何在Spring MVC中基于http请求头启用json的动态漂亮打印?相关的知识,希望对你有一定的参考价值。
我想基于http参数动态地从Spring MVC Restcontrollers打印json响应(如此处建议:http://www.vinaysahni.com/best-practices-for-a-pragmatic-restful-api#pretty-print-gzip)。
我已经找到了通过静态配置进行漂亮打印的配置,但不是如何动态地进行打印?
When using Spring MVC for REST, how do you enable Jackson to pretty-print rendered JSON?
知道怎么做吗?
Introducing A New Media Type
您可以定义一个新的媒体类型,例如application/pretty+json
并注册一个转换为该媒体类型的新HttpMessageConverter
。事实上,如果客户端使用Accept: application/pretty+json
标头发送请求,我们的新HttpMessageConverter
将写入响应,否则,普通的旧MappingJackson2HttpMessageConverter
会这样做。
所以,扩展MappingJackson2HttpMessageConverter
如下:
public class PrettyPrintJsonConverter extends MappingJackson2HttpMessageConverter {
public PrettyPrintJsonConverter() {
setPrettyPrint(true);
}
@Override
public List<MediaType> getSupportedMediaTypes() {
return Collections.singletonList(new MediaType("application", "pretty+json"));
}
@Override
public boolean canWrite(Class<?> clazz, MediaType mediaType) {
boolean canWrite = super.canWrite(clazz, mediaType);
boolean canWritePrettily = mediaType != null &&
mediaType.getSubtype().equals("pretty+json");
return canWrite && canWritePrettily;
}
}
构造函数中的setPrettyPrint(true)
将为我们提供帮助。然后我们应该注册这个HttpMessageConverter
:
@EnableWebMvc
@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(new PrettyPrintJsonConverter());
}
}
正如我所说的,如果客户端使用application/pretty+json
Accept标头发送请求,我们的PrettyPrintJsonConverter
将会正常写入JSON表示。否则,MappingJackson2HttpMessageConverter
会将一个紧凑的JSON写入响应主体。
你可以用ResponseBodyAdvice
甚至拦截器实现同样的目标,但在我看来,注册一个全新的HttpMessageConverter
是更好的方法。
要使用?pretty = true参数切换到漂亮的渲染,我使用自定义MappingJackson2HttpMessageConverter
@Configuration
@RestController
public class MyController {
@Bean
MappingJackson2HttpMessageConverter currentMappingJackson2HttpMessageConverter() {
MappingJackson2HttpMessageConverter jsonConverter = new CustomMappingJackson2HttpMessageConverter();
return jsonConverter;
}
public static class Input {
public String pretty;
}
public static class Output {
@JsonIgnore
public String pretty;
}
@RequestMapping(path = "/api/test", method = {RequestMethod.GET, RequestMethod.POST})
Output test( @RequestBody(required = false) Input input,
@RequestParam(required = false, value = "pretty") String pretty)
{
if (input.pretty==null) input.pretty = pretty;
Output output = new Output();
output.pretty = input.pretty;
return output;
}
}
转换器:
public class CustomMappingJackson2HttpMessageConverter extends MappingJackson2HttpMessageConverter {
ObjectMapper objectMapper;
ObjectMapper prettyPrintObjectMapper;
public CustomMappingJackson2HttpMessageConverter() {
objectMapper = new ObjectMapper();
prettyPrintObjectMapper = new ObjectMapper();
prettyPrintObjectMapper.configure(SerializationFeature.INDENT_OUTPUT, true);
}
@Override
@SuppressWarnings("deprecation")
protected void writeInternal(Object object, Type type, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException {
JsonEncoding encoding = getJsonEncoding(outputMessage.getHeaders().getContentType());
JsonGenerator generator = this.objectMapper.getFactory().createGenerator(outputMessage.getBody(), encoding);
try {
writePrefix(generator, object);
Class<?> serializationView = null;
FilterProvider filters = null;
Object value = object;
JavaType javaType = null;
if (object instanceof MappingJacksonValue) {
MappingJacksonValue container = (MappingJacksonValue) object;
value = container.getValue();
serializationView = container.getSerializationView();
filters = container.getFilters();
}
javaType = getJavaType(type, null);
ObjectMapper currentMapper = objectMapper;
Field prettyField = ReflectionUtils.findField(object.getClass(), "pretty");
if (prettyField != null) {
Object prettyObject = ReflectionUtils.getField(prettyField, object);
if (prettyObject != null && prettyObject instanceof String) {
String pretty = (String)prettyObject;
if (pretty.equals("true"))
currentMapper = prettyPrintObjectMapper;
}
}
ObjectWriter objectWriter;
if (serializationView != null) {
objectWriter = currentMapper.writerWithView(serializationView);
}
else if (filters != null) {
objectWriter = currentMapper.writer(filters);
}
else {
objectWriter = currentMapper.writer();
}
if (javaType != null && javaType.isContainerType()) {
objectWriter = objectWriter.withType(javaType);
}
objectWriter.writeValue(generator, value);
writeSuffix(generator, object);
generator.flush();
}
catch (JsonProcessingException ex) {
throw new HttpMessageNotWritableException("Could not write content: " + ex.getMessage(), ex);
}
}
}
弗兰克
我喜欢Franck Lefebure's方法,但我不喜欢使用反射,所以这里是使用自定义PrettyFormattedBody
类型+非常格式化的数组/列表的解决方案:
弹簧配置:
@Bean
MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() {
return new CustomJsonResponseMapper();
}
custom JSON response mapper.Java:
public class CustomJsonResponseMapper extends MappingJackson2HttpMessageConverter {
private final ObjectMapper prettyPrintObjectMapper;
public CustomJsonResponseMapper() {
super();
prettyPrintObjectMapper = initiatePrettyObjectMapper();
}
protected ObjectMapper initiatePrettyObjectMapper() {
// clone and re-configure default object mapper
final ObjectMapper prettyObjectMapper = objectMapper != null ? objectMapper.copy() : new ObjectMapper();
prettyObjectMapper.configure(SerializationFeature.INDENT_OUTPUT, true);
// for arrays - use new line for every entry
DefaultPrettyPrinter pp = new DefaultPrettyPrinter();
pp.indentArraysWith(new DefaultIndenter());
prettyObjectMapper.setDefaultPrettyPrinter(pp);
return prettyObjectMapper;
}
@Override
protected void writeInternal(final Object objectToWrite, final Type type, final HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
// based on: if objectToWrite is PrettyFormattedBody with isPretty == true => use custom formatter
// otherwise - use the default one
final Optional<PrettyFormattedBody> prettyFormatted = Optional.ofNullable(objectToWrite)
.filter(o -> o instanceof PrettyFormattedBody)
.map(o -> (PrettyFormattedBody) objectToWrite);
final boolean pretty = prettyFormatted.map(PrettyFormattedBody::isPretty).orElse(false);
final Object realObject = prettyFormatted.map(PrettyFormattedBody::getBody).orElse(objectToWrite);
if (pretty) {
// this is basically full copy of super.writeInternal(), but with custom (pretty) object mapper
MediaType contentType = outputMessage.getHeaders().getContentType();
JsonEncoding encoding = getJsonEncoding(contentType);
以上是关于如何在Spring MVC中基于http请求头启用json的动态漂亮打印?的主要内容,如果未能解决你的问题,请参考以下文章
Ajax+Spring MVC实现跨域请求(JSONP)(转)
如何使用 Spring MVC 和 Spring Security 为资源处理程序启用 HTTP 缓存