'How to use Spring Converter for some controllers only?

I have c converter which works:

public class StringToLongConverter implements Converter<String, Long> {
    @Override
    public Long convert(String source) {
        Long myDecodedValue = ...
        return myDecodedValue;
    }
}

In web configuration I have:

@Override
public void addFormatters (FormatterRegistry registry) {
    registry.addConverter(new StringToLongConverter());
}

Everything is good but it works for all controllers and I need it to be executed only for some controllers.

//I need this controller to get myvalue from converter
@RequestMapping(value = "{myvalue}", method = RequestMethod.POST)
public ResponseEntity myvalue1(@PathVariable Long myvalue) {

    return new ResponseEntity<>(HttpStatus.OK);
}

//I need this controller to get myvalue without converter
@RequestMapping(value = "{myvalue}", method = RequestMethod.POST)
public ResponseEntity myvalue2(@PathVariable Long myvalue) {

    return new ResponseEntity<>(HttpStatus.OK);
}

Can we specify which converters or parameters should be used with custom converter and which should not?



Solution 1:[1]

Normally speaking, a registered Converter is bound to an input source and an output destination. In your case <String, Long>. The default Spring converter you used will apply the conversion on each matching source-destination pair.

To gain more control over when to conditionally apply the conversion, a ConditionalGenericConverter can be used. The interface contains 3 methods:

  • boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType), to determine whether the conversion should be applied

  • Set<ConvertiblePair> getConvertibleTypes() to return a set of source-destination pairs the conversion can be applied to

  • Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) the method in which the actual conversion takes places.

I've set up a small Spring project to play around with the use of a ConditionalGenericConverter:

RequiresConversion.java:

// RequiresConversion is a custom annotation solely used in this example
// to annotate an attribute as "convertable"
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface RequiresConversion {
}

SomeConverter.java:

@Component
public class SomeConverter implements ConditionalGenericConverter {

    @Override
    public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
        // Verify whether the annotation is present
        return targetType.getAnnotation(RequiresConversion.class) != null;
    }

    @Override
    public Set<ConvertiblePair> getConvertibleTypes() {
        return Collections.singleton(new ConvertiblePair(String.class, Long.class));
    }

    @Override
    public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
        // Conversion logic here
        // In this example it strips "value" from the source string
        String sourceValue = ((String) source).replace("value", "");
        return Long.valueOf(sourceValue);
    }
}

SomeController.java:

@RestController
public class SomeController {

    // The path variable used will be converted, resulting in the "value"-prefix 
    // being stripped in SomeConverter
    // Notice the custom '@RequiresConversion' annotation
    @GetMapping(value = "/test/{myvalue}")
    public ResponseEntity myvalue(@RequiresConversion @PathVariable Long myvalue) {
        return new ResponseEntity<>(HttpStatus.OK);
    }

    // As the @RequiresConversion annotation is not present,
    // the conversion is not applied to the @PathVariable
    @GetMapping(value = "/test2/{myvalue}")
    public ResponseEntity myvalue2(@PathVariable Long myvalue) {
        return new ResponseEntity<>(HttpStatus.OK);
    }
}

The conversion will occur on http://localhost:8080/test/value123 , resulting in a 123 Long value. However, as the custom annotation @RequiresConversion is not present on the second mapping, the conversion on http://localhost:8080/test2/value123 will be skipped.

You could also inverse the annotation by renaming it to SkipConversion and verifying whether the annotation is absent in the matches() method.

Hope this helps!

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1