Skip to content

Support @ModelAttribute in method arguments of @HttpExchange interfaces #35615

@hermannpencole

Description

@hermannpencole

For me it is normal to be able to use beans on @HttpExchange interfaces methods.
It will be greate better if this code could work directly on Spring.

 @GetExchange(url = "/myUrl")
 ResponseEntity<Void> getEFiles(@ModelAttribute MyBean bean);

public record MyBean (
    Boolean myBool,
    @BindParam("another.name") String myStr
) {}

Please could you add org.springframework.web.service.invoker.ModelAttributeArgumentResolver.


because it's a shame to have the same code in each of my applications
factoryBuilder.customArgumentResolver(ClientConfig::resolve);

/**
     * Resolves the specified argument to form a set of HTTP request parameters if the {@link ModelAttribute} annotation is present
     * on the parameter. Transforms the object's properties to HTTP request parameters, considering custom field-level parameters
     * annotated with {@link BindParam}, if available.
     *
     * @param argument        the object to be resolved into request parameters; can be null
     * @param parameter       the {@link MethodParameter} containing the method argument and associated metadata
     * @param requestValues   the {@link HttpRequestValues.Builder} to which resolved request parameters will be added
     * @return true if the resolution was successful or skipped due to a null argument, false if the {@link ModelAttribute}
     *         annotation is not present
     */
    static boolean resolve(Object argument, MethodParameter parameter, HttpRequestValues.Builder requestValues) {

        ModelAttribute annotation = parameter.getParameterAnnotation(ModelAttribute.class);
        if (annotation == null) {
            return false;
        }

        if (argument == null) {
            return true;
        }

        // Create a map to store custom parameter names
        Map<String, String> customParamNames = new HashMap<>();

        // Retrieve all @BindParam annotations
        Class<?> clazz = argument.getClass();
        for (Field field : clazz.getDeclaredFields()) {
            BindParam bindParam = field.getAnnotation(BindParam.class);
            if (bindParam != null) {
                customParamNames.put(field.getName(), bindParam.value());
            }
        }

        // Convert object to query parameters
        BeanWrapper wrapper = PropertyAccessorFactory.forBeanPropertyAccess(argument);
        for (PropertyDescriptor descriptor : wrapper.getPropertyDescriptors()) {
            String propertyName = descriptor.getName();
            if (!"class".equals(propertyName)) {
                Object value = wrapper.getPropertyValue(propertyName);
                if (value != null) {
                    // Use a custom name if it exists, otherwise use the property name
                    String paramName = customParamNames.getOrDefault(propertyName, propertyName);
                    requestValues.addRequestParameter(paramName, value.toString());
                }
            }
        }

        return true;
    }

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions