use of io.micronaut.http.uri.UriMatchVariable in project micronaut-core by micronaut-projects.
the class QueryValueArgumentBinder method bind.
/**
* Binds the argument with {@link QueryValue} annotation to the request
* (Also binds without annotation if request body is not permitted).
*
* It will first try to convert to ConvertibleMultiValues type and if conversion is successful, add the
* corresponding parameters to the request. (By default the conversion will be successful if the {@link Format}
* annotation is present and has one of the supported values - see
* {@link io.micronaut.core.convert.converters.MultiValuesConverterFactory} for specific converters). Otherwise
* the uri template will be used to deduce what will be done with the request. For example, simple parameters are
* converted to {@link String}
*/
@Override
public BindingResult<T> bind(ArgumentConversionContext<T> context, HttpRequest<?> source) {
ConvertibleMultiValues<String> parameters = source.getParameters();
Argument<T> argument = context.getArgument();
AnnotationMetadata annotationMetadata = argument.getAnnotationMetadata();
boolean hasAnnotation = annotationMetadata.hasAnnotation(QueryValue.class);
HttpMethod httpMethod = source.getMethod();
boolean permitsRequestBody = HttpMethod.permitsRequestBody(httpMethod);
BindingResult<T> result;
if (hasAnnotation || !permitsRequestBody) {
// First try converting from the ConvertibleMultiValues type and if conversion is successful, return it.
// Otherwise use the given uri template to deduce what to do with the variable
Optional<T> multiValueConversion;
if (annotationMetadata.hasAnnotation(Format.class)) {
multiValueConversion = conversionService.convert(parameters, context);
} else {
multiValueConversion = Optional.empty();
}
if (multiValueConversion.isPresent()) {
result = () -> multiValueConversion;
} else {
String parameterName = annotationMetadata.stringValue(QueryValue.class).orElse(argument.getName());
// If we need to bind all request params to command object
// checks if the variable is defined with modifier char *, eg. ?pojo*
boolean bindAll = source.getAttribute(HttpAttributes.ROUTE_MATCH, UriMatchInfo.class).map(umi -> {
UriMatchVariable uriMatchVariable = umi.getVariableMap().get(parameterName);
return uriMatchVariable != null && uriMatchVariable.isExploded();
}).orElse(false);
if (bindAll) {
Object value;
// Only maps and POJOs will "bindAll", lists work like normal
if (Iterable.class.isAssignableFrom(argument.getType())) {
value = doResolve(context, parameters, parameterName);
if (value == null) {
value = Collections.emptyList();
}
} else {
value = parameters.asMap();
}
result = doConvert(value, context);
} else {
result = doBind(context, parameters, parameterName);
}
}
} else {
result = BindingResult.EMPTY;
}
return result;
}
use of io.micronaut.http.uri.UriMatchVariable in project micronaut-core by micronaut-projects.
the class QueryValueClientArgumentRequestBinder method bind.
/**
* If value can be converted to ConvertibleMultiValues, then use it and add it to the uriContext.queryParameters.
* The ConvertibleMultiValues converters are found in
* {@link io.micronaut.core.convert.converters.MultiValuesConverterFactory} and perform conversion only when the
* {@link io.micronaut.core.convert.format.Format} annotation has one of the supported values.
* Otherwise if the {@link io.micronaut.core.convert.format.Format} annotation is present, it is converted to {@link String}. If none of these
* are satisfied, the{@link io.micronaut.http.uri.UriTemplate} decides what to do with the given value which
* is supplied as an Object (it is added to uriContext.pathParameter).
*
* <br> By default value is converted to ConvertibleMultiValues when the {@link io.micronaut.core.convert.format.Format} annotation is present and has
* one of the defined above formats. Otherwise empty optional is returned.
*
* <br> The default {@link io.micronaut.http.uri.UriTemplate} will convert the value to String and to parameters.
* Optionally, the value can be formatted if the path template states so.
*/
@Override
public void bind(@NonNull ArgumentConversionContext<Object> context, @NonNull ClientRequestUriContext uriContext, @NonNull Object value, @NonNull MutableHttpRequest<?> request) {
String parameterName = context.getAnnotationMetadata().stringValue(QueryValue.class).filter(StringUtils::isNotEmpty).orElse(context.getArgument().getName());
final UriMatchVariable uriVariable = uriContext.getUriTemplate().getVariables().stream().filter(v -> v.getName().equals(parameterName)).findFirst().orElse(null);
if (uriVariable != null) {
if (uriVariable.isExploded()) {
uriContext.setPathParameter(parameterName, value);
} else {
String convertedValue = conversionService.convert(value, ConversionContext.STRING.with(context.getAnnotationMetadata())).filter(StringUtils::isNotEmpty).orElse(null);
if (convertedValue != null) {
uriContext.setPathParameter(parameterName, convertedValue);
} else {
uriContext.setPathParameter(parameterName, value);
}
}
} else {
ArgumentConversionContext<ConvertibleMultiValues> conversionContext = context.with(Argument.of(ConvertibleMultiValues.class, context.getArgument().getName(), context.getAnnotationMetadata()));
final Optional<ConvertibleMultiValues<String>> multiValues = conversionService.convert(value, conversionContext).map(values -> (ConvertibleMultiValues<String>) values);
if (multiValues.isPresent()) {
Map<String, List<String>> queryParameters = uriContext.getQueryParameters();
// Add all the parameters
multiValues.get().forEach((k, v) -> {
if (queryParameters.containsKey(k)) {
queryParameters.get(k).addAll(v);
} else {
queryParameters.put(k, v);
}
});
} else {
conversionService.convert(value, ConversionContext.STRING.with(context.getAnnotationMetadata())).ifPresent(v -> uriContext.addQueryParameter(parameterName, v));
}
}
}
use of io.micronaut.http.uri.UriMatchVariable in project micronaut-core by micronaut-projects.
the class PathVariableAnnotationBinder method bind.
@Override
public BindingResult<T> bind(ArgumentConversionContext<T> context, HttpRequest<?> source) {
ConvertibleMultiValues<String> parameters = source.getParameters();
Argument<T> argument = context.getArgument();
AnnotationMetadata annotationMetadata = argument.getAnnotationMetadata();
boolean hasAnnotation = annotationMetadata.hasAnnotation(PathVariable.class);
String parameterName = annotationMetadata.stringValue(PathVariable.class).orElse(argument.getName());
// If we need to bind all request params to command object
// checks if the variable is defined with modifier char *
// eg. ?pojo*
final Optional<UriMatchInfo> matchInfo = source.getAttribute(HttpAttributes.ROUTE_MATCH, UriMatchInfo.class);
boolean bindAll = matchInfo.flatMap(umi -> umi.getVariables().stream().filter(v -> v.getName().equals(parameterName)).findFirst().map(UriMatchVariable::isExploded)).orElse(false);
BindingResult<T> result;
// be manipulated to override POST or JSON variables
if (hasAnnotation && matchInfo.isPresent()) {
final ConvertibleValues<Object> variableValues = ConvertibleValues.of(matchInfo.get().getVariableValues());
if (bindAll) {
Object value;
// Only maps and POJOs will "bindAll", lists work like normal
if (Iterable.class.isAssignableFrom(argument.getType())) {
value = doResolve(context, variableValues, parameterName);
if (value == null) {
value = Collections.emptyList();
}
} else {
value = parameters.asMap();
}
result = doConvert(value, context);
} else {
result = doBind(context, variableValues, parameterName);
}
} else {
// noinspection unchecked
result = BindingResult.EMPTY;
}
return result;
}
use of io.micronaut.http.uri.UriMatchVariable in project micronaut-core by micronaut-projects.
the class NullableParameterRule method validate.
@Override
public RouteValidationResult validate(List<UriMatchTemplate> templates, ParameterElement[] parameters, MethodElement method) {
List<String> errorMessages = new ArrayList<>();
boolean isClient = method.hasAnnotation("io.micronaut.http.client.annotation.Client");
// Optional variables can be required in clients
if (!isClient) {
Map<String, UriMatchVariable> variables = new HashMap<>();
Set<UriMatchVariable> required = new HashSet<>();
for (UriMatchTemplate template : templates) {
for (UriMatchVariable variable : template.getVariables()) {
if (!variable.isOptional() || variable.isExploded()) {
required.add(variable);
}
variables.compute(variable.getName(), (key, var) -> {
if (var == null) {
if (variable.isOptional() && !variable.isExploded()) {
return variable;
} else {
return null;
}
} else {
if (!var.isOptional() || var.isExploded()) {
if (variable.isOptional() && !variable.isExploded()) {
return variable;
} else {
return var;
}
} else {
return var;
}
}
});
}
}
for (UriMatchVariable variable : required) {
if (templates.stream().anyMatch(t -> !t.getVariableNames().contains(variable.getName()))) {
variables.putIfAbsent(variable.getName(), variable);
}
}
for (UriMatchVariable variable : variables.values()) {
Arrays.stream(parameters).flatMap(p -> getTypedElements(p).stream()).filter(p -> p.getName().equals(variable.getName())).forEach(p -> {
ClassElement type = p.getType();
boolean hasDefaultValue = p.findAnnotation(Bindable.class).flatMap(av -> av.stringValue("defaultValue")).isPresent();
if (!isNullable(p) && type != null && !type.isAssignable(Optional.class) && !hasDefaultValue) {
errorMessages.add(String.format("The uri variable [%s] is optional, but the corresponding method argument [%s %s] is not defined as an Optional or annotated with a Nullable annotation.", variable.getName(), p.getType().toString(), p.getName()));
}
});
}
}
return new RouteValidationResult(errorMessages.toArray(new String[0]));
}
Aggregations