From 30291638400522b9ddc158d4bbd4caed8c5ff7ce Mon Sep 17 00:00:00 2001 From: Sergio del Amo Date: Wed, 5 Feb 2025 13:26:25 +0100 Subject: [PATCH] doc: HTTP Client cannot use Form & @JsonProperty (#11558) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * doc: HTTP Client cannot use Form & @JsonProperty See: https://github.com/micronaut-projects/micronaut-core/issues/1853 This pull-requests adds a TCK test, and it documents that although the server binding of a form-url encoded payload supports Jackson annotations such as @JsonProperty, the HTTP does not. The JDK HTTP Client does not even support form URL encoded content type. * annotate with @JsonProperty also title Without it in native image it renders `{“paginas":100}` instead of `{"title":"Building Microservices","paginas":100}` --- .../forms/FormsJacksonAnnotationsTest.java | 97 +++++++++++++++++++ src/main/docs/guide/httpServer/formData.adoc | 2 + .../netty/tests/JdkHttpServerTestSuite.java | 1 + 3 files changed, 100 insertions(+) create mode 100644 http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/forms/FormsJacksonAnnotationsTest.java diff --git a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/forms/FormsJacksonAnnotationsTest.java b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/forms/FormsJacksonAnnotationsTest.java new file mode 100644 index 00000000000..1cb5ebef40b --- /dev/null +++ b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/forms/FormsJacksonAnnotationsTest.java @@ -0,0 +1,97 @@ +/* + * Copyright 2017-2023 original authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.micronaut.http.server.tck.tests.forms; + +import com.fasterxml.jackson.annotation.JsonProperty; +import io.micronaut.context.annotation.Requires; +import io.micronaut.core.annotation.Introspected; +import io.micronaut.core.annotation.NonNull; +import io.micronaut.core.annotation.Nullable; +import io.micronaut.http.HttpRequest; +import io.micronaut.http.HttpStatus; +import io.micronaut.http.MediaType; +import io.micronaut.http.annotation.Body; +import io.micronaut.http.annotation.Consumes; +import io.micronaut.http.annotation.Controller; +import io.micronaut.http.annotation.Post; +import io.micronaut.http.tck.AssertionUtils; +import io.micronaut.http.tck.HttpResponseAssertion; +import io.micronaut.http.tck.TestScenario; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.util.Optional; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +@SuppressWarnings({ + "java:S5960", // We're allowed assertions, as these are used in tests only + "checkstyle:MissingJavadocType", + "checkstyle:DesignForExtension" +}) +public class FormsJacksonAnnotationsTest { + private static final String SPEC_NAME = "FormsJacksonAnnotationsTest"; + private static final String JSON_WITH_PAGES = "{\"title\":\"Building Microservices\",\"paginas\":100}"; + private static final String JSON_WITHOUT_PAGES = "{\"title\":\"Building Microservices\"}"; + + @Test + public void serverFormSubmissionsSupportJacksonAnnotations() throws IOException { + String body = "title=Building+Microservices&paginas=100"; + assertWithBody(body, JSON_WITH_PAGES); + + body = "title=Building+Microservices&pages="; + assertWithBody(body, JSON_WITHOUT_PAGES); + } + + @Test + public void httpClientFormSubmissionsDoesNotSupportJacksonAnnotations() throws IOException { + Book book = new Book("Building Microservices", 100); + // Jackson annotations (@JsonProperty) are not supported by the HTTP Client and form-url encoded payload. + assertWithBody(book, JSON_WITHOUT_PAGES); + } + + private static void assertWithBody(Object body, String expectedJson) throws IOException { + TestScenario.builder() + .specName(SPEC_NAME) + .request(HttpRequest.POST("/book/save", body).contentType(MediaType.APPLICATION_FORM_URLENCODED_TYPE)) + .assertion((server, request) -> + AssertionUtils.assertDoesNotThrow(server, request, HttpResponseAssertion.builder() + .status(HttpStatus.OK) + .assertResponse(httpResponse -> { + Optional bodyOptional = httpResponse.getBody(String.class); + assertTrue(bodyOptional.isPresent()); + assertEquals(expectedJson, bodyOptional.get()); + }) + .build())) + .run(); + } + + @Requires(property = "spec.name", value = SPEC_NAME) + @Controller("/book") + static class SaveController { + @Consumes(MediaType.APPLICATION_FORM_URLENCODED) + @Post("/save") + Book save(@Body Book book) { + return book; + } + } + + @Introspected + record Book(@JsonProperty("title") @NonNull String title, @JsonProperty("paginas") @Nullable Integer pages) { + } + +} diff --git a/src/main/docs/guide/httpServer/formData.adoc b/src/main/docs/guide/httpServer/formData.adoc index 946c21cb928..ded2b746797 100644 --- a/src/main/docs/guide/httpServer/formData.adoc +++ b/src/main/docs/guide/httpServer/formData.adoc @@ -2,6 +2,8 @@ To make data binding model customizations consistent between form data and JSON, The advantage of this approach is that the same Jackson annotations used for customizing JSON binding can be used for form submissions. +IMPORTANT: Form URL encoded content type and Jackson annotations are not supported by the <>. + In practice this means that to bind regular form data, the only change required to the <> is updating the api:http.MediaType[] consumed: snippet::io.micronaut.docs.server.form.PersonController[tags="class,formbinding,endclass", title="Binding Form Data to POJOs"] diff --git a/test-suite-http-server-tck-jdk/src/test/java/io/micronaut/http/server/tck/netty/tests/JdkHttpServerTestSuite.java b/test-suite-http-server-tck-jdk/src/test/java/io/micronaut/http/server/tck/netty/tests/JdkHttpServerTestSuite.java index 345436d9169..f94e1a8b0a8 100644 --- a/test-suite-http-server-tck-jdk/src/test/java/io/micronaut/http/server/tck/netty/tests/JdkHttpServerTestSuite.java +++ b/test-suite-http-server-tck-jdk/src/test/java/io/micronaut/http/server/tck/netty/tests/JdkHttpServerTestSuite.java @@ -11,6 +11,7 @@ @SuiteDisplayName("HTTP Server TCK for Javanet client") @ExcludeClassNamePatterns({ "io.micronaut.http.server.tck.tests.FilterProxyTest", // There's no proxy client for the JDK client + "io.micronaut.http.server.tck.tests.forms.FormsJacksonAnnotationsTest" // it seems application/x-www-form-urlencoded is not yet supported by the JDK client }) @ExcludeTags("multipart") // Multipart not supported by HttpClient public class JdkHttpServerTestSuite {