#470 - Added HAL JSON UTF-8 variant to MediaTypes.

Added application/hal+json;charset=UTF-8 to the media types enum. Traverson now configures all HAL flavors to be supported by the registered HAL-specific HttpMessageConverter.

Removed nullability of media type in JsonPathLinkDiscoverer. This requires subclasses that want to support all media types to explicitly configure MediaType.ALL.

Heavily inspired by the PR submitted by @drummond but very significantly rewritten.
This commit is contained in:
Oliver Gierke
2017-03-28 00:18:02 -04:00
parent 05f687ef39
commit 312134ee82
8 changed files with 100 additions and 32 deletions

View File

@@ -22,6 +22,7 @@ import org.springframework.http.MediaType;
*
* @author Oliver Gierke
* @author Przemek Nowak
* @author Drummond Dawson
*/
public class MediaTypes {
@@ -34,4 +35,14 @@ public class MediaTypes {
* Public constant media type for {@code application/hal+json}.
*/
public static final MediaType HAL_JSON = MediaType.valueOf(HAL_JSON_VALUE);
/**
* A String equivalent of {@link MediaTypes#HAL_JSON_UTF8}.
*/
public static final String HAL_JSON_UTF8_VALUE = HAL_JSON_VALUE + ";charset=UTF-8";
/**
* Public constant media type for {@code application/hal+json;charset=UTF-8}.
*/
public static final MediaType HAL_JSON_UTF8 = MediaType.valueOf(HAL_JSON_UTF8_VALUE);
}

View File

@@ -21,6 +21,7 @@ import java.net.URI;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
@@ -130,13 +131,34 @@ public class Traverson {
List<HttpMessageConverter<?>> converters = new ArrayList<HttpMessageConverter<?>>();
converters.add(new StringHttpMessageConverter(Charset.forName("UTF-8")));
if (mediaTypes.contains(MediaTypes.HAL_JSON)) {
converters.add(getHalConverter());
List<MediaType> halFlavors = getHalJsonFlavors(mediaTypes);
if (!halFlavors.isEmpty()) {
converters.add(getHalConverter(halFlavors));
}
return converters;
}
/**
* Returns all HAL JSON compatible media types from the given list.
*
* @param mediaTypes must not be {@literal null}.
* @return
*/
private static List<MediaType> getHalJsonFlavors(Collection<MediaType> mediaTypes) {
List<MediaType> result = new ArrayList<MediaType>();
for (MediaType mediaType : mediaTypes) {
if (MediaTypes.HAL_JSON.isCompatibleWith(mediaType)) {
result.add(mediaType);
}
}
return result;
}
private static final RestOperations createDefaultTemplate(List<MediaType> mediaTypes) {
RestTemplate template = new RestTemplate();
@@ -150,7 +172,7 @@ public class Traverson {
*
* @return
*/
private static final HttpMessageConverter<?> getHalConverter() {
private static final HttpMessageConverter<?> getHalConverter(List<MediaType> halFlavours) {
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new Jackson2HalModule());
@@ -159,7 +181,7 @@ public class Traverson {
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
converter.setObjectMapper(mapper);
converter.setSupportedMediaTypes(Arrays.asList(MediaTypes.HAL_JSON));
converter.setSupportedMediaTypes(halFlavours);
return converter;
}

View File

@@ -293,7 +293,7 @@ class HypermediaSupportBeanDefinitionRegistrar implements ImportBeanDefinitionRe
MappingJackson2HttpMessageConverter halConverter = new TypeConstrainedMappingJackson2HttpMessageConverter(
ResourceSupport.class);
halConverter.setSupportedMediaTypes(Arrays.asList(HAL_JSON));
halConverter.setSupportedMediaTypes(Arrays.asList(HAL_JSON, HAL_JSON_UTF8));
halConverter.setObjectMapper(halObjectMapper);
List<HttpMessageConverter<?>> result = new ArrayList<HttpMessageConverter<?>>(converters.size());

View File

@@ -15,6 +15,8 @@
*/
package org.springframework.hateoas.core;
import net.minidev.json.JSONArray;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Array;
@@ -24,10 +26,9 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import net.minidev.json.JSONArray;
import org.springframework.hateoas.Link;
import org.springframework.hateoas.LinkDiscoverer;
import org.springframework.hateoas.MediaTypes;
import org.springframework.http.MediaType;
import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;
@@ -67,23 +68,31 @@ public class JsonPathLinkDiscoverer implements LinkDiscoverer {
}
private final String pathTemplate;
private final MediaType mediaType;
private final List<MediaType> mediaTypes;
/**
* Creates a new {@link JsonPathLinkDiscoverer} using the given path template supporting the given {@link MediaType}.
* The template has to contain a single {@code %s} placeholder which will be replaced by the relation type.
*
* @param pathTemplate must not be {@literal null} or empty and contain a single placeholder.
* @param mediaType the {@link MediaType} to support.
* @param mediaType the primary {@link MediaType}s to support.
* @param additional {@link MediaTypes} to support.
*/
public JsonPathLinkDiscoverer(String pathTemplate, MediaType mediaType) {
public JsonPathLinkDiscoverer(String pathTemplate, MediaType mediaType, MediaType... others) {
Assert.hasText(pathTemplate, "Path template must not be null!");
Assert.isTrue(StringUtils.countOccurrencesOf(pathTemplate, "%s") == 1,
"Path template must contain a single placeholder!");
Assert.notNull(mediaType, "Primary MediaType must not be null!");
Assert.notNull(others, "Other MediaTypes must not be null!");
this.pathTemplate = pathTemplate;
this.mediaType = mediaType;
List<MediaType> mediaTypes = new ArrayList<MediaType>(others.length + 1);
mediaTypes.add(mediaType);
mediaTypes.addAll(Arrays.asList(others));
this.mediaTypes = mediaTypes;
}
/*
@@ -178,6 +187,13 @@ public class JsonPathLinkDiscoverer implements LinkDiscoverer {
*/
@Override
public boolean supports(MediaType delimiter) {
return this.mediaType == null ? true : this.mediaType.isCompatibleWith(delimiter);
for (MediaType mediaType : this.mediaTypes) {
if (mediaType.isCompatibleWith(delimiter)) {
return true;
}
}
return false;
}
}