Skip to content

Commit

Permalink
doc: HTTP Client cannot use Form & @JsonProperty (#11558)
Browse files Browse the repository at this point in the history
* doc:  HTTP Client cannot use Form & @JsonProperty

See: #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}`
  • Loading branch information
sdelamo authored Feb 5, 2025
1 parent 8548eea commit 3029163
Show file tree
Hide file tree
Showing 3 changed files with 100 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -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<String> 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) {
}

}
2 changes: 2 additions & 0 deletions src/main/docs/guide/httpServer/formData.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -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 <<httpClient, Micronaut HTTP Client>>.

In practice this means that to bind regular form data, the only change required to the <<bindingUsingPOJOs, previous JSON binding code>> is updating the api:http.MediaType[] consumed:

snippet::io.micronaut.docs.server.form.PersonController[tags="class,formbinding,endclass", title="Binding Form Data to POJOs"]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down

0 comments on commit 3029163

Please sign in to comment.