PetOperations.java

    The save method when called will perform an HTTP POST with the following JSON by default:

    Example Produced JSON

    1. {"name":"Dino", "age":10}

    You may however want to customize what is sent as the body, the parameters, URI variables and so on. The annotation is very flexible in this regard and supports the same HTTP Annotations as Micronaut’s HTTP server.

    For example, the following defines a URI template and the name parameter is used as part of the URI template, whilst is used to declare that the contents to send to the server are represented by the Pet POJO:

    PetOperations.java

    1. @Post("/{name}")
    2. Single<Pet> save(
    3. @NotBlank String name, (1)
    4. @Body @Valid Pet pet) (2)

    The following table summarizes the parameter annotations, their purpose, and provides an example:

    Type Based Binding Parameters

    Some parameters are recognized by their type instead of their annotation. The following table summarizes the parameter types, their purpose, and provides an example:

    Custom Binding

    The ClientArgumentRequestBinder API is what is responsible for binding client arguments to the request. Custom binder classes registered as beans will automatically be used during the binding process. Annotation based binders are searched for first, with type based binders being searched if a binder was not found.

    Binding By Annotation

    To control how an argument is bound to the request based on the annotation applied to the argument, create a bean of type AnnotatedClientArgumentRequestBinder. Any custom annotations must be annotated with .

    In this example, see the following client:

    Client With @Metadata Argument

    1. @Client("/")
    2. public interface MetadataClient {
    3. @Get("/client/bind")
    4. String get(@Metadata Map<String, Object> metadata);
    5. }

    Client With @Metadata Argument

    1. @Client("/")
    2. interface MetadataClient {
    3. @Get("/client/bind")
    4. String get(@Metadata Map metadata)
    5. }

    Client With @Metadata Argument

    1. @Client("/")
    2. interface MetadataClient {
    3. @Get("/client/bind")
    4. operator fun get(@Metadata metadata: Map<String, Any>): String
    5. }

    @Metadata Annotation

    @Metadata Annotation

    1. import io.micronaut.core.bind.annotation.Bindable
    2. import java.lang.annotation.*
    3. @Documented
    4. @Retention(RetentionPolicy.RUNTIME)
    5. @Target(ElementType.PARAMETER)
    6. @Bindable
    7. @interface Metadata {
    8. }

    @Metadata Annotation

    1. import io.micronaut.core.bind.annotation.Bindable
    2. @MustBeDocumented
    3. @Retention(AnnotationRetention.RUNTIME)
    4. @Target(AnnotationTarget.VALUE_PARAMETER)
    5. @Bindable
    6. annotation class Metadata

    Without any additional code, the client will attempt to convert the metadata to a string and append it as a query parameter. In this case that isn’t the desired effect so a custom binder must be created.

    The following binder will handle arguments passed to clients that are annotated with the @Metadata annotation and then mutate the request to contain the desired headers. The implementation could be modified to accept more types of data other than Map.

    Annotation Argument Binder

    1. import edu.umd.cs.findbugs.annotations.NonNull;
    2. import io.micronaut.core.convert.ArgumentConversionContext;
    3. import io.micronaut.core.naming.NameUtils;
    4. import io.micronaut.core.util.StringUtils;
    5. import io.micronaut.http.MutableHttpRequest;
    6. import io.micronaut.http.client.bind.AnnotatedClientArgumentRequestBinder;
    7. import io.micronaut.http.client.bind.ClientRequestUriContext;
    8. import org.jetbrains.annotations.NotNull;
    9. import javax.inject.Singleton;
    10. import java.util.Map;
    11. @Singleton
    12. public class MetadataClientArgumentBinder implements AnnotatedClientArgumentRequestBinder<Metadata> {
    13. @NotNull
    14. @Override
    15. public Class<Metadata> getAnnotationType() {
    16. return Metadata.class;
    17. }
    18. @Override
    19. public void bind(@NotNull ArgumentConversionContext<Object> context,
    20. @NonNull ClientRequestUriContext uriContext,
    21. @NotNull Object value,
    22. @NotNull MutableHttpRequest<?> request) {
    23. if (value instanceof Map) {
    24. for (Map.Entry<?, ?> entry: ((Map<?, ?>) value).entrySet()) {
    25. request.header("X-Metadata-" + key, entry.getValue().toString());
    26. }
    27. }
    28. }
    29. }

    Annotation Argument Binder

    1. import io.micronaut.core.convert.ArgumentConversionContext
    2. import io.micronaut.core.naming.NameUtils
    3. import io.micronaut.core.util.StringUtils
    4. import io.micronaut.http.MutableHttpRequest
    5. import io.micronaut.http.client.bind.AnnotatedClientArgumentRequestBinder
    6. import io.micronaut.http.client.bind.ClientRequestUriContext
    7. import org.jetbrains.annotations.NotNull
    8. import javax.inject.Singleton
    9. @Singleton
    10. class MetadataClientArgumentBinder implements AnnotatedClientArgumentRequestBinder<Metadata> {
    11. Class<Metadata> annotationType = Metadata
    12. @Override
    13. void bind(@NotNull ArgumentConversionContext<Object> context,
    14. @NonNull ClientRequestUriContext uriContext,
    15. @NotNull Object value,
    16. @NotNull MutableHttpRequest<?> request) {
    17. if (value instanceof Map) {
    18. for (def entry: ((Map) value).entrySet()) {
    19. String key = NameUtils.hyphenate(StringUtils.capitalize(entry.key.toString()), false)
    20. request.header("X-Metadata-" + key, entry.value.toString())
    21. }
    22. }
    23. }
    24. }

    Annotation Argument Binder

    1. import io.micronaut.core.convert.ArgumentConversionContext
    2. import io.micronaut.core.naming.NameUtils
    3. import io.micronaut.core.util.StringUtils
    4. import io.micronaut.http.MutableHttpRequest
    5. import io.micronaut.http.client.bind.AnnotatedClientArgumentRequestBinder
    6. import io.micronaut.http.client.bind.ClientRequestUriContext
    7. import javax.inject.Singleton
    8. @Singleton
    9. class MetadataClientArgumentBinder : AnnotatedClientArgumentRequestBinder<Metadata> {
    10. override fun getAnnotationType(): Class<Metadata> {
    11. return Metadata::class.java
    12. }
    13. override fun bind(context: ArgumentConversionContext<Any>,
    14. uriContext: ClientRequestUriContext,
    15. value: Any,
    16. request: MutableHttpRequest<*>) {
    17. if (value is Map<*, *>) {
    18. for ((key1, value1) in value) {
    19. val key = NameUtils.hyphenate(StringUtils.capitalize(key1.toString()), false)
    20. request.header("X-Metadata-$key", value1.toString())
    21. }
    22. }
    23. }
    24. }
    Binding By Type

    To bind to the request based on the type of the argument, create a bean of type .

    In this example, see the following client:

    Client With Metadata Argument

    Client With Metadata Argument

    1. @Client("/")
    2. interface MetadataClient {
    3. @Get("/client/bind")
    4. String get(Metadata metadata)
    5. }

    Client With Metadata Argument

    1. @Client("/")
    2. interface MetadataClient {
    3. @Get("/client/bind")
    4. }

    Without any additional code, the client will attempt to convert the metadata to a string and append it as a query parameter. In this case that isn’t the desired effect so a custom binder must be created.

    The following binder will handle arguments passed to clients that are of the Metadata type and then mutate the request to contain the desired headers.

    Typed Argument Binder

    1. import edu.umd.cs.findbugs.annotations.NonNull;
    2. import io.micronaut.core.convert.ArgumentConversionContext;
    3. import io.micronaut.http.MutableHttpRequest;
    4. import io.micronaut.http.client.bind.ClientRequestUriContext;
    5. import io.micronaut.http.client.bind.TypedClientArgumentRequestBinder;
    6. import javax.inject.Singleton;
    7. @Singleton
    8. public class MetadataClientArgumentBinder implements TypedClientArgumentRequestBinder<Metadata> {
    9. @Override
    10. @NonNull
    11. public Argument<Metadata> argumentType() {
    12. return Argument.of(Metadata.class);
    13. }
    14. @Override
    15. public void bind(@NonNull ArgumentConversionContext<Metadata> context,
    16. @NonNull ClientRequestUriContext uriContext,
    17. @NonNull Metadata value,
    18. @NonNull MutableHttpRequest<?> request) {
    19. request.header("X-Metadata-Version", value.getVersion().toString());
    20. request.header("X-Metadata-Deployment-Id", value.getDeploymentId().toString());
    21. }
    22. }

    Typed Argument Binder

    1. import edu.umd.cs.findbugs.annotations.NonNull
    2. import io.micronaut.core.convert.ArgumentConversionContext
    3. import io.micronaut.core.type.Argument
    4. import io.micronaut.http.MutableHttpRequest
    5. import io.micronaut.http.client.bind.ClientRequestUriContext
    6. import io.micronaut.http.client.bind.TypedClientArgumentRequestBinder
    7. import javax.inject.Singleton
    8. @Singleton
    9. class MetadataClientArgumentBinder implements TypedClientArgumentRequestBinder<Metadata> {
    10. @Override
    11. @NonNull
    12. Argument<Metadata> argumentType() {
    13. return Argument.of(Metadata)
    14. }
    15. @Override
    16. void bind(@NonNull ArgumentConversionContext<Metadata> context,
    17. @NonNull ClientRequestUriContext uriContext,
    18. @NonNull Metadata value,
    19. @NonNull MutableHttpRequest<?> request) {
    20. request.header("X-Metadata-Version", value.version.toString())
    21. request.header("X-Metadata-Deployment-Id", value.deploymentId.toString())
    22. }
    23. }
    1. import io.micronaut.core.convert.ArgumentConversionContext
    2. import io.micronaut.core.type.Argument
    3. import io.micronaut.http.MutableHttpRequest
    4. import io.micronaut.http.client.bind.ClientRequestUriContext
    5. import io.micronaut.http.client.bind.TypedClientArgumentRequestBinder
    6. import javax.inject.Singleton
    7. @Singleton
    8. class MetadataClientArgumentBinder : TypedClientArgumentRequestBinder<Metadata?> {
    9. override fun argumentType(): Argument<Metadata?> {
    10. return Argument.of(Metadata::class.java)
    11. }
    12. override fun bind(context: ArgumentConversionContext<Metadata?>,
    13. uriContext: ClientRequestUriContext,
    14. value: Metadata,
    15. request: MutableHttpRequest<*>) {
    16. request.header("X-Metadata-Version", value.version.toString())
    17. request.header("X-Metadata-Deployment-Id", value.deploymentId.toString())
    18. }
    19. }