From a56af9eea9f2afb17786cb921a4243f81a040b8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Mikul=C3=A1=C5=A1ek?= Date: Fri, 12 Jul 2024 09:22:34 +0200 Subject: [PATCH] feat: allow to opt out links uri encoding --- lib/src/main/asciidoc/configuration.adoc | 1 + .../hateoas/jsonapi/JsonApiConfiguration.java | 18 +++++++++++++++++- .../jsonapi/JsonApiLinksSerializer.java | 12 ++++++++++-- 3 files changed, 28 insertions(+), 3 deletions(-) diff --git a/lib/src/main/asciidoc/configuration.adoc b/lib/src/main/asciidoc/configuration.adoc index 7b2edbfe..60deb551 100644 --- a/lib/src/main/asciidoc/configuration.adoc +++ b/lib/src/main/asciidoc/configuration.adoc @@ -33,6 +33,7 @@ see https://jsonapi.org/format/#auto-id--link-objects. To serialize the Spring H Be aware that using the default, Spring HATEOAS complex links are rendered in a backward-incompatible way (related to version 1.x.x of this library that only supports JSON:API 1.0), since client might expect properties like title in the meta section. | true +| LinksNotUrlEncoded | Set of links' rels which are not URL encoded when serializing. Empty by default. | empty set |=== TIP: Since the JSON:API recommendation contains square brackets in the request parameter names, diff --git a/lib/src/main/java/com/toedter/spring/hateoas/jsonapi/JsonApiConfiguration.java b/lib/src/main/java/com/toedter/spring/hateoas/jsonapi/JsonApiConfiguration.java index e95ffde6..71b8ebdd 100644 --- a/lib/src/main/java/com/toedter/spring/hateoas/jsonapi/JsonApiConfiguration.java +++ b/lib/src/main/java/com/toedter/spring/hateoas/jsonapi/JsonApiConfiguration.java @@ -21,11 +21,14 @@ import lombok.AllArgsConstructor; import lombok.Getter; import lombok.With; +import org.springframework.hateoas.LinkRelation; import org.springframework.lang.Nullable; import org.springframework.util.Assert; +import java.util.HashSet; import java.util.LinkedHashMap; import java.util.Map; +import java.util.Set; import java.util.function.Consumer; @@ -262,6 +265,19 @@ public enum AffordanceType { @Getter private final boolean jsonApiCompliantLinks; + /** + * By default, JSON:API links are serialized as URL encoded. + * + * If you set this configuration, Spring HATEOAS links with set relations won't be URI encoded. + * This can be useful for instance to avoid double uri encoding when you pass the links to the model already + * URL encoded. + * @param linksNotUrlEncoded The new value of this configuration's linksNotUrlEncoded + * @return The default is empty set. + */ + @With + @Getter + private final Set linksNotUrlEncoded; + @With(AccessLevel.PRIVATE) private final Map, String> typeForClass; @@ -361,8 +377,8 @@ public JsonApiConfiguration() { this.affordancesRenderedAsLinkMeta = AffordanceType.NONE; this.jsonApi11LinkPropertiesRemovedFromLinkMeta = true; this.jsonApiCompliantLinks = true; + this.linksNotUrlEncoded = new HashSet<>(); this.objectMapperCustomizer = customObjectMapper -> { }; // Default to no action. } } - diff --git a/lib/src/main/java/com/toedter/spring/hateoas/jsonapi/JsonApiLinksSerializer.java b/lib/src/main/java/com/toedter/spring/hateoas/jsonapi/JsonApiLinksSerializer.java index 6a5680a1..d5611212 100644 --- a/lib/src/main/java/com/toedter/spring/hateoas/jsonapi/JsonApiLinksSerializer.java +++ b/lib/src/main/java/com/toedter/spring/hateoas/jsonapi/JsonApiLinksSerializer.java @@ -36,9 +36,11 @@ import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.ArrayList; +import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Set; import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_EMPTY; import static com.toedter.spring.hateoas.jsonapi.MediaTypes.JSON_API; @@ -47,6 +49,7 @@ class JsonApiLinksSerializer extends AbstractJsonApiSerializer { private static final ObjectMapper objectMapper = new ObjectMapper(); private JsonApiConfiguration.AffordanceType affordanceType; private boolean removeHateoasLinkPropertiesFromMeta; + private Set linksNotUrlEncoded = new HashSet<>(); public JsonApiLinksSerializer() { super(Links.class); @@ -56,6 +59,7 @@ public JsonApiLinksSerializer() { public void setJsonApiConfiguration(JsonApiConfiguration jsonApiConfiguration) { this.affordanceType = jsonApiConfiguration.getAffordancesRenderedAsLinkMeta(); this.removeHateoasLinkPropertiesFromMeta = jsonApiConfiguration.isJsonApi11LinkPropertiesRemovedFromLinkMeta(); + this.linksNotUrlEncoded = jsonApiConfiguration.getLinksNotUrlEncoded(); } @Override @@ -82,7 +86,7 @@ public void serialize(Links value, JsonGenerator gen, SerializerProvider provide private void serializeLinkWithRelation(JsonGenerator gen, Link link) throws IOException { if (isSimpleLink(link)) { - gen.writeStringField(link.getRel().value(), UriUtils.encodeQuery(link.getHref(), StandardCharsets.UTF_8)); + gen.writeStringField(link.getRel().value(), uriEncodeLinkHref(link)); } else { gen.writeObjectFieldStart(link.getRel().value()); writeComplexLink(gen, link); @@ -91,7 +95,7 @@ private void serializeLinkWithRelation(JsonGenerator gen, Link link) throws IOEx } private void writeComplexLink(JsonGenerator gen, Link link) throws IOException { - gen.writeStringField("href", UriUtils.encodeQuery(link.getHref(), StandardCharsets.UTF_8)); + gen.writeStringField("href", uriEncodeLinkHref(link)); Map attributes = getAttributes(link); if (link.getTitle() != null) { gen.writeStringField("title", link.getTitle()); @@ -119,6 +123,10 @@ private boolean isSimpleLink(Link link) { return getAttributes(link).size() == 0; } + private String uriEncodeLinkHref(Link link) { + return linksNotUrlEncoded.contains(link.getRel()) ? link.getHref() : UriUtils.encodeQuery(link.getHref(), StandardCharsets.UTF_8); + } + private Map getAttributes(Link link) { final Map attributeMap = objectMapper.convertValue(link, Map.class);