Un'altra soluzione se si utilizza Gson
formattatore (full pull request reference):
Primavera Config (definire 2 fagioli):
@Bean
public Gson gson() {
return new GsonBuilder()
.setFieldNamingPolicy(FieldNamingPolicy.IDENTITY)
.disableHtmlEscaping()
.create();
}
/**
* @return same as {@link #gson()}, but with <code>{@link Gson#prettyPrinting} == true</code>, e.g. use indentation
*/
@Bean
public Gson prettyGson() {
return new GsonBuilder()
.setFieldNamingPolicy(FieldNamingPolicy.IDENTITY)
.setPrettyPrinting()
.disableHtmlEscaping()
.create();
}
/**
* Custom JSON objects mapper: uses {@link #gson()} as a default JSON HTTP request/response mapper
* and {@link #prettyGson()} as mapper for pretty-printed JSON objects. See {@link PrettyGsonMessageConverter} for
* how pretty print is requested.
* <p>
* <b>Note:</b> {@link FieldNamingPolicy#IDENTITY} field mapping policy is important at least for
* {@link PaymentHandleResponse#getPayment()} method. See respective documentation for details.
*
* @return default HTTP request/response mapper, based on {@link #gson()} bean.
*/
@Bean
public GsonHttpMessageConverter gsonMessageConverter() {
return new PrettyGsonMessageConverter(gson(), prettyGson());
}
PrettyGsonMessageConverter.java:
/**
* Custom Gson response message converter to allow JSON pretty print, if requested.
* <p>
* The class extends default Spring {@link GsonHttpMessageConverter} adding {@link #prettyGson} mapper and processing
* {@link PrettyFormattedBody} instances.
*/
public class PrettyGsonMessageConverter extends GsonHttpMessageConverter {
/**
* JSON message converter with configured pretty print options, which is used when a response is expected to be
* pretty printed.
*/
private final Gson prettyGson;
/**
* @see GsonHttpMessageConverter#jsonPrefix
*/
private String jsonPrefix;
/**
* @param gson default (minified) JSON mapper. This value is set to {@code super.gson} property.
* @param prettyGson pretty configure JSON mapper, which is used if the body expected to be pretty printed
*/
public PrettyGsonMessageConverter(final Gson gson, final Gson prettyGson) {
super();
this.setGson(gson);
this.prettyGson = prettyGson;
}
/**
* Because base {@link GsonHttpMessageConverter#jsonPrefix} is private, but is used in overloaded
* {@link #writeInternal(Object, Type, HttpOutputMessage)} - we should copy this value.
*
* @see GsonHttpMessageConverter#setJsonPrefix(String)
*/
@Override
public void setJsonPrefix(String jsonPrefix) {
super.setJsonPrefix(jsonPrefix);
this.jsonPrefix = jsonPrefix;
}
/**
* Because base {@link GsonHttpMessageConverter#jsonPrefix} is private, but is used in overloaded
* {@link #writeInternal(Object, Type, HttpOutputMessage)} - we should copy this value.
*
* @see GsonHttpMessageConverter#setPrefixJson(boolean)
*/
@Override
public void setPrefixJson(boolean prefixJson) {
super.setPrefixJson(prefixJson);
this.jsonPrefix = (prefixJson ? ")]}', " : null);
}
/**
* Allow response JSON pretty print if {@code objectToWrite} is a {@link PrettyFormattedBody} instance with
* <code>{@link PrettyFormattedBody#isPretty() isPretty} == true</code>.
*
* @param objectToWrite if the value is {@link PrettyFormattedBody} instance with
* <code>{@link PrettyFormattedBody#isPretty() isPretty} == true</code> - use
* {@link #prettyGson} for output writing. Otherwise use base
* {@link GsonHttpMessageConverter#writeInternal(Object, Type, HttpOutputMessage)}
* @param type the type of object to write (may be {@code null})
* @param outputMessage the HTTP output message to write to
* @throws IOException in case of I/O errors
* @throws HttpMessageNotWritableException in case of conversion errors
*/
@Override
protected void writeInternal(@Nullable final Object objectToWrite,
@Nullable final Type type,
@Nonnull final HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException {
// based on: if objectToWrite is PrettyFormattedBody && isPretty == true => use custom formatter
// otherwise - use the default base GsonHttpMessageConverter#writeInternal(Object, Type, HttpOutputMessage)
Optional<PrettyFormattedBody> prettyFormatted = Optional.ofNullable(objectToWrite)
.filter(o -> o instanceof PrettyFormattedBody)
.map(o -> (PrettyFormattedBody) objectToWrite);
boolean pretty = prettyFormatted.map(PrettyFormattedBody::isPretty).orElse(false);
Object realObject = prettyFormatted.map(PrettyFormattedBody::getBody).orElse(objectToWrite);
if (pretty) {
// this is basically full copy of super.writeInternal(), but with custom (pretty) gson mapper
Charset charset = getCharset(outputMessage.getHeaders());
OutputStreamWriter writer = new OutputStreamWriter(outputMessage.getBody(), charset);
try {
if (this.jsonPrefix != null) {
writer.append(this.jsonPrefix);
}
if (type != null) {
this.prettyGson.toJson(realObject, type, writer);
} else {
this.prettyGson.toJson(realObject, writer);
}
writer.close();
} catch (JsonIOException ex) {
throw new HttpMessageNotWritableException("Could not write JSON: " + ex.getMessage(), ex);
}
} else {
// use default writer if isPretty property is not specified
super.writeInternal(realObject, type, outputMessage);
}
}
/**
* To ensure the message converter supports {@link PrettyFormattedBody} instances
*
* @param clazz response body class
* @return <b>true</b> if the {@code clazz} is {@link PrettyFormattedBody} or {@code super.supports(clazz) == true}
*/
@Override
protected boolean supports(Class<?> clazz) {
return PrettyFormattedBody.class.equals(clazz) || super.supports(clazz);
}
/**
* Just a copy-paste of {@link GsonHttpMessageConverter#getCharset(HttpHeaders)} because it is private, but used in
* {@link #writeInternal(Object, Type, HttpOutputMessage)}
*
* @param headers output message HTTP headers
* @return a charset from the {@code headers} content type or {@link GsonHttpMessageConverter#DEFAULT_CHARSET}
* otherwise.
*/
private Charset getCharset(HttpHeaders headers) {
if (headers == null || headers.getContentType() == null || headers.getContentType().getCharset() == null) {
return DEFAULT_CHARSET;
}
return headers.getContentType().getCharset();
}
}
PrettyFormattedBody.java:
public final class PrettyFormattedBody {
private final Object body;
private final boolean pretty;
private PrettyFormattedBody(@Nonnull final Object body, final boolean pretty) {
this.body = body;
this.pretty = pretty;
}
public Object getBody() {
return body;
}
public boolean isPretty() {
return pretty;
}
public static PrettyFormattedBody of(@Nonnull final Object body, final boolean pretty) {
return new PrettyFormattedBody(body, pretty);
}
}
e infine - il controller stesso:
@RequestMapping(
value = {"/health", "/"},
produces = APPLICATION_JSON_VALUE)
public ResponseEntity<?> checkHealth(@RequestParam(required = false) String pretty,
@Autowired ApplicationInfo applicationInfo) {
Map<String, Object> tenantResponse = new HashMap<>();
tenantResponse.put(APP_INFO_KEY, applicationInfo);
return new ResponseEntity<>(PrettyFormattedBody.of(tenantResponse, pretty != null),
HttpStatus.OK);
}
Buon approccio. L'approccio alla testata è completo? "getHeader()" è uno dei diversi metodi per ottenere un valore di intestazione. Penso, devo sovrascrivere anche gli altri metodi. Destra? –
Non ne sono sicuro, ma dovresti controllare un po 'del codice sorgente per scoprire quali metodi vengono usati per determinare il 'HttpMessageConverter'. Stavo solo cercando di darti l'idea di base .. –