DRYing up controller - method that returns entity or redirects (in Java, Spring)

Bert Pie :

I have a controller that has a few methods that get an optional of entity from service, checks if is present and proceeds with some other actions or redirects with message "Entity not found".

It looks like that:

@GetMapping("action")
public String method(@PathVariable Long id,
                     final RedirectAttributes redirectAttributes){
    Optional<Entity> eOpt = entityService.findById(id);
    if(eOpt.isEmpty()){
        alertHandler.set(redirectAttributes, Status.ENTITY_NOT_FOUND);
        return "redirect:/entity/list"
    }
    Entity e = eOpt.get();

    // other actions that are using e

    return "view-name";
}

The six lines repeat in a few methods and for different entities too. Is there a way to assign it to some private method? The only thing I came up with is using a private method like:

private Optional<Entity> getEntityOpt(Long id){
    Optional<Entity> eOpt = entityService.findById(id);
    if(eOpt.isEmpty()){
        alertHandler.set(redirectAttributes, Status.ENTITY_NOT_FOUND);
    }
    return Optional.empty();
}

This only saves me one line in mapped methods, so I don't have to set up alert message. Otherwise I still have to check again if the Optional is empty to redirect it.

So I guess the question really is - can I set up the private method to either return entity or redirect like:

Entity e = getEntityOrRedirect(Long id);

or maybe you have different ways to handle that problem. Or maybe it is what it is and you have to repeat yourself...

Jakub Ch. :

You may treat empty Optional as an exceptional situation.

In that case you may provide your own RuntimeException containing path to redirect.

public class EntityNotFoundException extends RuntimeException {

    private final String fallbackView;

    public EntityNotFoundException(final String fallbackView) {
        this.fallbackView = fallbackView;
    }

    public String getFallbackView() {
        return fallbackView;
    }

Then provide a method annotated with @ExceptionHandler to your controller class (or if the situation is common for multiple controllers then provide such method to class annotated with @ControllerAdvice). Your exception handler should catch just defined exception and do a redirect.

@ExceptionHandler(EntityNotFoundException.class)
public String redirectOnEntityNotFoundException(final EntityNotFoundException exception, 
                                                final RedirectAttributes redirectAttributes) {
    alertHandler.set(redirectAttributes, Status.ENTITY_NOT_FOUND);
    return exception.getFallbackView();
}

Finally you achieved some kind of getEntityOrRedirect. Now you may use the above setup as following:

@GetMapping("action")
public String method(@PathVariable Long id){

    Entity e = entityService.findById(id)
                            .orElseThrow(() -> new EntityNotFoundException("redirect:/entity/list"));

    // other actions that are using e

    return "view-name";
}

Code not tested so apologize for typos in advance.

Note I believe it would work for Spring >= 4.3.5 as otherwise RedirectAttributes wouldn't be resolved for @ExceptionHandler (as stated here)

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=152673&siteId=1