io.micronaut.http.bind.DefaultRequestBinderRegistry Maven / Gradle / Ivy
/*
* Copyright 2017-2020 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.bind;
import io.micronaut.core.annotation.NonNull;
import io.micronaut.core.bind.ArgumentBinder;
import io.micronaut.core.bind.annotation.Bindable;
import io.micronaut.core.convert.ArgumentConversionContext;
import io.micronaut.core.convert.ConversionContext;
import io.micronaut.core.convert.ConversionError;
import io.micronaut.core.convert.ConversionService;
import io.micronaut.core.type.Argument;
import io.micronaut.core.util.CollectionUtils;
import io.micronaut.core.util.clhm.ConcurrentLinkedHashMap;
import io.micronaut.http.HttpHeaders;
import io.micronaut.http.HttpParameters;
import io.micronaut.http.HttpRequest;
import io.micronaut.http.HttpRequestWrapper;
import io.micronaut.http.PushCapableHttpRequest;
import io.micronaut.http.annotation.Body;
import io.micronaut.http.bind.binders.AnnotatedRequestArgumentBinder;
import io.micronaut.http.bind.binders.ContinuationArgumentBinder;
import io.micronaut.http.bind.binders.CookieObjectArgumentBinder;
import io.micronaut.http.bind.binders.CookieAnnotationBinder;
import io.micronaut.http.bind.binders.DefaultBodyAnnotationBinder;
import io.micronaut.http.bind.binders.DefaultUnmatchedRequestArgumentBinder;
import io.micronaut.http.bind.binders.HeaderAnnotationBinder;
import io.micronaut.http.bind.binders.PartAnnotationBinder;
import io.micronaut.http.bind.binders.PathVariableAnnotationBinder;
import io.micronaut.http.bind.binders.PendingRequestBindingResult;
import io.micronaut.http.bind.binders.QueryValueArgumentBinder;
import io.micronaut.http.bind.binders.RequestArgumentBinder;
import io.micronaut.http.bind.binders.RequestAttributeAnnotationBinder;
import io.micronaut.http.bind.binders.RequestBeanAnnotationBinder;
import io.micronaut.http.bind.binders.TypedRequestArgumentBinder;
import io.micronaut.http.cookie.Cookie;
import io.micronaut.http.cookie.Cookies;
import jakarta.inject.Inject;
import jakarta.inject.Singleton;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import static io.micronaut.core.util.KotlinUtils.KOTLIN_COROUTINES_SUPPORTED;
/**
* Default implementation of the {@link RequestBinderRegistry} interface.
*
* @author Graeme Rocher
* @since 1.0
*/
@Singleton
public class DefaultRequestBinderRegistry implements RequestBinderRegistry {
private static final long CACHE_MAX_SIZE = 30;
private final Map, RequestArgumentBinder> byAnnotation = new LinkedHashMap<>();
private final Map byTypeAndAnnotation = new LinkedHashMap<>();
private final Map byType = new LinkedHashMap<>();
private final ConversionService conversionService;
private final Map> argumentBinderCache =
new ConcurrentLinkedHashMap.Builder>().maximumWeightedCapacity(CACHE_MAX_SIZE).build();
private final List> unmatchedBinders = new ArrayList<>();
private final DefaultUnmatchedRequestArgumentBinder defaultUnmatchedRequestArgumentBinder;
/**
* @param conversionService The conversion service
* @param binders The request argument binders
*/
public DefaultRequestBinderRegistry(ConversionService conversionService, RequestArgumentBinder... binders) {
this(conversionService, Arrays.asList(binders));
}
public DefaultRequestBinderRegistry(ConversionService conversionService, List binders) {
this(conversionService, binders, new DefaultBodyAnnotationBinder(conversionService));
}
/**
* @param conversionService The conversion service
* @param binders The request argument binders
* @param bodyAnnotationBinder The body annotation binder
*/
@Inject
public DefaultRequestBinderRegistry(
ConversionService conversionService,
List binders,
DefaultBodyAnnotationBinder bodyAnnotationBinder
) {
this.conversionService = conversionService;
if (CollectionUtils.isNotEmpty(binders)) {
for (RequestArgumentBinder binder : binders) {
addArgumentBinder(binder);
}
}
byAnnotation.put(Body.class, bodyAnnotationBinder);
registerDefaultAnnotationBinders(byAnnotation);
byType.put(Argument.of(HttpHeaders.class).typeHashCode(), (RequestArgumentBinder) (argument, source) -> () -> Optional.of(source.getHeaders()));
byType.put(Argument.of(HttpRequest.class).typeHashCode(), (RequestArgumentBinder>) (argument, source) -> convertBodyIfNecessary(bodyAnnotationBinder, argument, source, false));
byType.put(Argument.of(PushCapableHttpRequest.class).typeHashCode(), (RequestArgumentBinder>) (argument, source) -> {
if (source instanceof PushCapableHttpRequest>) {
return convertBodyIfNecessary(bodyAnnotationBinder, argument, source, true);
} else {
return ArgumentBinder.BindingResult.unsatisfied();
}
});
byType.put(Argument.of(HttpParameters.class).typeHashCode(), (RequestArgumentBinder) (argument, source) -> () -> Optional.of(source.getParameters()));
byType.put(Argument.of(Cookies.class).typeHashCode(), (RequestArgumentBinder) (argument, source) -> () -> Optional.of(source.getCookies()));
byType.put(Argument.of(Cookie.class).typeHashCode(), new CookieObjectArgumentBinder());
defaultUnmatchedRequestArgumentBinder = new DefaultUnmatchedRequestArgumentBinder<>(
List.of(
new QueryValueArgumentBinder<>(conversionService),
new RequestAttributeAnnotationBinder<>(conversionService)
),
unmatchedBinders,
List.of(bodyAnnotationBinder)
);
}
@SuppressWarnings("rawtypes")
@Override
public void addArgumentBinder(ArgumentBinder> binder) {
if (binder instanceof AnnotatedRequestArgumentBinder, ?> annotatedRequestArgumentBinder) {
Class extends Annotation> annotationType = annotatedRequestArgumentBinder.getAnnotationType();
if (binder instanceof TypedRequestArgumentBinder> typedRequestArgumentBinder) {
Argument argumentType = typedRequestArgumentBinder.argumentType();
byTypeAndAnnotation.put(new TypeAndAnnotation(argumentType, annotationType), (RequestArgumentBinder) binder);
List> superTypes = typedRequestArgumentBinder.superTypes();
if (CollectionUtils.isNotEmpty(superTypes)) {
for (Class> superType : superTypes) {
byTypeAndAnnotation.put(new TypeAndAnnotation(Argument.of(superType), annotationType), (RequestArgumentBinder) binder);
}
}
} else {
byAnnotation.put(annotationType, annotatedRequestArgumentBinder);
}
} else if (binder instanceof TypedRequestArgumentBinder> typedRequestArgumentBinder) {
byType.put(typedRequestArgumentBinder.argumentType().typeHashCode(), typedRequestArgumentBinder);
}
}
@Override
public void addUnmatchedRequestArgumentBinder(RequestArgumentBinder © 2015 - 2025 Weber Informatics LLC | Privacy Policy