-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
30 changed files
with
795 additions
and
153 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
87 changes: 87 additions & 0 deletions
87
...n/java/io/micronaut/http/server/tck/tests/forms/FormBindingUsingMethodParametersTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
/* | ||
* 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 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.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 FormBindingUsingMethodParametersTest { | ||
private static final String SPEC_NAME = "FormBindingUsingMethodParametersTest"; | ||
|
||
@Test | ||
public void formBindingUsingMethodParameters() throws IOException { | ||
String body = "title=Building+Microservices&pages=100"; | ||
String expectedJson = "{\"title\":\"Building Microservices\",\"pages\":100}"; | ||
assertWithBody(body, expectedJson); | ||
|
||
body = "title=Building+Microservices&pages="; | ||
expectedJson = "{\"title\":\"Building Microservices\"}"; | ||
assertWithBody(body, expectedJson); | ||
} | ||
|
||
private static void assertWithBody(String 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(String title, @Nullable Integer pages) { | ||
return new Book(title, pages); | ||
} | ||
} | ||
|
||
@Introspected | ||
record Book(@NonNull String title, @Nullable Integer pages) { | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
33 changes: 33 additions & 0 deletions
33
inject-java/src/test/groovy/io/micronaut/inject/annotation/MyAnnotationX.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
package io.micronaut.inject.annotation; | ||
|
||
import java.lang.annotation.Documented; | ||
import java.lang.annotation.ElementType; | ||
import java.lang.annotation.Retention; | ||
import java.lang.annotation.Target; | ||
|
||
import static java.lang.annotation.RetentionPolicy.RUNTIME; | ||
|
||
@Documented | ||
@Retention(RUNTIME) | ||
@Target({ElementType.TYPE}) | ||
public @interface MyAnnotationX { | ||
|
||
Class<?> clazz1(); | ||
|
||
Class<?> clazz2(); | ||
|
||
Class<?> clazz3(); | ||
|
||
Class<?> clazz4(); | ||
|
||
Class<?> clazz5(); | ||
|
||
Class<?> clazz6(); | ||
|
||
Class<?> clazz7(); | ||
|
||
Class<?> clazz8(); | ||
|
||
Class<?> clazz9(); | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,154 +1,7 @@ | ||
:jackson-annotations: https://fasterxml.github.io/jackson-annotations/javadoc/2.9/ | ||
:jackson-databind: https://fasterxml.github.io/jackson-databind/javadoc/2.9/ | ||
:jackson-core: https://fasterxml.github.io/jackson-core/javadoc/2.9/ | ||
|
||
The most common data interchange format nowadays is JSON. | ||
|
||
By default, the api:http.annotation.Controller[] annotation specifies that the controllers in Micronaut framework consume and produce JSON by default. | ||
|
||
Since Micronaut Framework 4.0, users must choose how they want to serialize (Jackson Databind or Micronaut Serialization). Both approaches allow the usage of https://micronaut-projects.github.io/micronaut-serialization/latest/guide/index.html#jacksonAnnotations[Jackson Annotations]. | ||
|
||
With either approach, the Micronaut framework reads incoming JSON in a non-blocking manner. | ||
|
||
== Serialize using Micronaut Serialization | ||
|
||
https://micronaut-projects.github.io/micronaut-serialization/latest/guide/index.html#quickStart[Micronaut Serialization] offers reflection-free serialization using build-time <<introspection, Bean Introspections>>. It supports alternative formats such as https://micronaut-projects.github.io/micronaut-serialization/latest/guide/index.html#jsonpQuick[JSON-P or JSON-B]. You need to add the following dependencies: | ||
|
||
dependency:micronaut-serde-processor[groupId=io.micronaut.serde,scope=annotationProcessor] | ||
dependency:micronaut-serde-jackson[groupId=io.micronaut.serde] | ||
|
||
== Serialization using Jackson Databind | ||
|
||
To serialize using https://github.com/FasterXML/jackson[Jackson] Databind include the following dependency: | ||
|
||
dependency:micronaut-jackson-databind[] | ||
|
||
== JsonMapper | ||
|
||
You may be used to work with https://fasterxml.github.io/jackson-databind/javadoc/2.7/com/fasterxml/jackson/databind/ObjectMapper.html[Jackson's `ObjectMapper`]. However, we don't recommend using Jackson's `ObjectMapper` directly; instead you should use api:json.JsonMapper[], an API almost identical to Jackson's `ObjectMapper`. Moreover, both <<jsonBinding, Micronaut Serialization and Micronaut Jackson Databind>> implement api:json.JsonMapper[]. | ||
|
||
You can inject a bean of type `JsonMapper` or manually instantiate one via `JsonMapper.createDefault()`. | ||
|
||
== Binding using Reactive Frameworks | ||
|
||
From a developer perspective however, you can generally just work with Plain Old Java Objects (POJOs) and can optionally use a Reactive framework such as https://github.com/ReactiveX/RxJava[RxJava] or https://projectreactor.io[Project Reactor]. The following is an example of a controller that reads and saves an incoming POJO in a non-blocking way from JSON: | ||
|
||
snippet::io.micronaut.docs.server.json.PersonController[tags="class,single,endclass", indent=0, title="Using Reactive Streams to Read the JSON"] | ||
|
||
<1> The method receives a `Publisher` which emits the POJO once the JSON has been read | ||
<2> The `map` method stores the instance in a `Map` | ||
<3> An api:http.HttpResponse[] is returned | ||
|
||
Using cURL from the command line, you can POST JSON to the `/people` URI: | ||
|
||
.Using cURL to Post JSON | ||
---- | ||
$ curl -X POST localhost:8080/people -d '{"firstName":"Fred","lastName":"Flintstone","age":45}' | ||
---- | ||
|
||
== Binding Using CompletableFuture | ||
|
||
The same method as the previous example can also be written with the link:{jdkapi}/java.base/java/util/concurrent/CompletableFuture.html[CompletableFuture] API instead: | ||
|
||
snippet::io.micronaut.docs.server.json.PersonController[tags="class,future,endclass", indent=0, title="Using CompletableFuture to Read the JSON"] | ||
|
||
The above example uses the `thenApply` method to achieve the same as the previous example. | ||
|
||
== Binding using POJOs | ||
|
||
Note however you can just as easily write: | ||
|
||
snippet::io.micronaut.docs.server.json.PersonController[tags="class,regular,endclass", indent=0, title="Binding JSON POJOs"] | ||
|
||
The Micronaut framework only executes your method once the data has been read in a non-blocking manner. | ||
|
||
TIP: You can customize the output in various ways, such as using https://github.com/FasterXML/jackson-annotations/wiki/Jackson-Annotations[Jackson annotations]. | ||
|
||
== Jackson Configuration | ||
|
||
If you use <<jsonBinding, `micronaut-jackson-databind`>>, the Jackson's `ObjectMapper` can be configured through configuration with the api:io.micronaut.jackson.JacksonConfiguration[] class. | ||
|
||
All Jackson configuration keys start with `jackson`. | ||
|
||
|======= | ||
| dateFormat | String | The date format | ||
| locale | String | Uses link:{jdkapi}/java.base/java/util/Locale.html#forLanguageTag-java.lang.String-[Locale.forLanguageTag]. Example: `en-US` | ||
| timeZone | String |Uses link:{jdkapi}/java.base/java/util/TimeZone.html#getTimeZone-java.lang.String-[TimeZone.getTimeZone]. Example: `PST` | ||
| serializationInclusion | String | One of link:{jackson-annotations}com/fasterxml/jackson/annotation/JsonInclude.Include.html[JsonInclude.Include]. Example: `ALWAYS` | ||
| propertyNamingStrategy | String | Name of an instance of link:{jackson-databind}com/fasterxml/jackson/databind/PropertyNamingStrategy.html[PropertyNamingStrategy]. Example: `SNAKE_CASE` | ||
| defaultTyping | String | The global defaultTyping for polymorphic type handling from enum link:{jackson-databind}com/fasterxml/jackson/databind/ObjectMapper.DefaultTyping.html[ObjectMapper.DefaultTyping]. Example: `NON_FINAL` | ||
|======= | ||
|
||
Example: | ||
|
||
[configuration] | ||
---- | ||
jackson: | ||
serializationInclusion: ALWAYS | ||
---- | ||
|
||
=== Features | ||
|
||
If you use <<jsonBinding, `micronaut-jackson-databind`>>, all Jackson's features can be configured with their name as the key and a boolean to indicate enabled or disabled. | ||
|
||
|====== | ||
|serialization | Map | link:{jackson-databind}com/fasterxml/jackson/databind/SerializationFeature.html[SerializationFeature] | ||
|deserialization | Map | link:{jackson-databind}com/fasterxml/jackson/databind/DeserializationFeature.html[DeserializationFeature] | ||
|mapper | Map | link:{jackson-databind}com/fasterxml/jackson/databind/MapperFeature.html[MapperFeature] | ||
|parser | Map | link:{jackson-core}com/fasterxml/jackson/core/JsonParser.Feature.html[JsonParser.Feature] | ||
|generator | Map | link:{jackson-core}com/fasterxml/jackson/core/JsonGenerator.Feature.html[JsonGenerator.Feature] | ||
|factory | Map | link:{jackson-core}com/fasterxml/jackson/core/JsonFactory.Feature.html[JsonFactory.Feature] | ||
|====== | ||
|
||
Example: | ||
|
||
[configuration] | ||
---- | ||
jackson: | ||
serialization: | ||
indentOutput: true | ||
writeDatesAsTimestamps: false | ||
deserialization: | ||
useBigIntegerForInts: true | ||
failOnUnknownProperties: false | ||
---- | ||
|
||
=== Further customising `JsonFactory` | ||
|
||
If you use <<jsonBinding, `micronaut-jackson-databind`>>, there may be situations where you wish to customise the `JsonFactory` used by the `ObjectMapper` beyond the configuration of features (for example to allow custom character escaping). | ||
This can be achieved by providing your own `JsonFactory` bean, or by providing a `BeanCreatedEventListener<JsonFactory>` which configures the default bean on startup. | ||
|
||
=== Support for `@JsonView` | ||
|
||
If you use <<jsonBinding, `micronaut-jackson-databind`>>, you can use the `@JsonView` annotation on controller methods if you set `jackson.json-view.enabled` to `true` in your configuration file (e.g `application.yml`). | ||
|
||
Jackson's `@JsonView` annotation lets you control which properties are exposed on a per-response basis. See https://www.baeldung.com/jackson-json-view-annotation[Jackson JSON Views] for more information. | ||
|
||
=== Beans | ||
|
||
If you use <<jsonBinding, `micronaut-jackson-databind`>>, in addition to configuration, beans can be registered to customize Jackson. All beans that extend any of the following classes are registered with the object mapper: | ||
|
||
* link:{jackson-databind}com/fasterxml/jackson/databind/Module.html[Module] | ||
* link:{jackson-databind}com/fasterxml/jackson/databind/JsonDeserializer.html[JsonDeserializer] | ||
* link:{jackson-databind}com/fasterxml/jackson/databind/JsonSerializer.html[JsonSerializer] | ||
* link:{jackson-databind}com/fasterxml/jackson/databind/KeyDeserializer.html[KeyDeserializer] | ||
* link:{jackson-databind}com/fasterxml/jackson/databind/deser/BeanDeserializerModifier.html[BeanDeserializerModifier] | ||
* link:{jackson-databind}com/fasterxml/jackson/databind/ser/BeanSerializerModifier.html[BeanSerializerModifier] | ||
|
||
=== Service Loader | ||
|
||
Any modules registered via the service loader are also added to the default object mapper. | ||
|
||
=== Number Precision | ||
|
||
During JSON parsing, the framework may convert any incoming data to an intermediate object model. By default, this model uses `BigInteger`, `long` and `double` for numeric values. This means some information that could be represented by `BigDecimal` may be lost. For example, numbers with many decimal places that cannot be represented by `double` may be truncated, even if the target type for deserialization uses `BigDecimal`. Metadata on the number of trailing zeroes (`BigDecimal.precision()`), e.g. the difference between `0.12` and `0.120`, is also discarded. | ||
|
||
If you need full accuracy for number types, use the following configuration: | ||
|
||
[configuration] | ||
---- | ||
jackson: | ||
deserialization: | ||
useBigIntegerForInts: true | ||
useBigDecimalForFloats: true | ||
---- |
5 changes: 5 additions & 0 deletions
5
src/main/docs/guide/httpServer/jsonBinding/bindingUsingCompletableFuture.adoc
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
The same method as the previous example can also be written with the link:{jdkapi}/java.base/java/util/concurrent/CompletableFuture.html[CompletableFuture] API instead: | ||
|
||
snippet::io.micronaut.docs.server.json.PersonController[tags="class,future,endclass", indent=0, title="Using CompletableFuture to Read the JSON"] | ||
|
||
The above example uses the `thenApply` method to achieve the same as the previous example. |
Oops, something went wrong.