Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Custom WebArgumentResolver like @PathVariable

I would like to use a custom WebArgumentResolver for id -> entity. Easy enough if I use request parameters: use the parameter key to determine the entity type and look up accordingly.

But I would like it to be like the @PathVariable annotation.

eg.

http://mysite.xzy/something/enquiryId/itemId would trigger this method

@RequestMapping(value = "/something/{enquiry}/{item}")
public String method(@Coerce Enquiry enquiry, @Coerce Item item) 

@Coerce annotations would tell the WebArgumentResolver to use the particular service based on it's type.

Problem is working out which uri part belongs to entity.

Can someone explain how PathVariable annotation does it. And is it possible to emulate it with my custom annotation.

Thanks.

like image 956
dom farr Avatar asked Feb 20 '11 22:02

dom farr


2 Answers

You can use @InitBinder to let spring know how to coerce a given String to your custom type.

You'd want something like:

@RequestMapping(value = "/something/{enquiry}")
public String method(@PathVariable Enquiry enquiry) {...}


@InitBinder
public void initBinder(WebDataBinder binder) {
    binder.registerCustomEditor(Enquiry.class, new PropertyEditorSupport() {
        @Override
        public String getAsText() {
            return ((Enquiry) this.getValue()).toString();
        }

        @Override
        public void setAsText(String text) throws IllegalArgumentException {
            setValue(new Enquiry(text));
        }
    });
}
like image 96
Tim Helmstedt Avatar answered Nov 02 '22 08:11

Tim Helmstedt


For a generic approach where you don't need to specify one PropertyEditor for each entity you could use a ConditionalGenericConverter:

public final class GenericIdToEntityConverter implements ConditionalGenericConverter {
    private static final Logger log = LoggerFactory.getLogger(GenericIdToEntityConverter.class);

    private final ConversionService conversionService;

    @PersistenceContext
    private EntityManager entityManager;

    @Autowired
    public GenericIdToEntityConverter(ConversionService conversionService) {
        this.conversionService = conversionService;
    }

    public Set<ConvertiblePair> getConvertibleTypes() {
        return ImmutableSet.of(new ConvertiblePair(Number.class, AbstractEntity.class),
                new ConvertiblePair(CharSequence.class, AbstractEntity.class));

    }

    public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
        return AbstractEntity.class.isAssignableFrom(targetType.getType())
        && this.conversionService.canConvert(sourceType, TypeDescriptor.valueOf(Long.class));
    }

    public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
        if (source == null) {
            return null;
        }

        Long id = (Long) this.conversionService.convert(source, sourceType, TypeDescriptor.valueOf(Long.class));

        Object entity = entityManager.find(targetType.getType(), id);
        if (entity == null) {
            log.info("Did not find an entity with id {} of type {}", id,  targetType.getType());
            return null;
        }

        return entity;
    }

}
like image 37
Aleksander Blomskøld Avatar answered Nov 02 '22 09:11

Aleksander Blomskøld