VRaptor

Accepting URLs ending with a slash

by Tomaz Lavieri, applied to VRaptor 4

For those who had some trouble to determine urls like:

@Path("product/{product.id}")
public void view(Product product) {...}

when typing a URI /product/1/ and the link didn’t work, here’s two simple ways to fix the problem.

Note: This is not necessarily a problem, because the URL /abc is different of /abc/, so the behavior to give 404 is expected.

The first way is put two kind of URLs (with or without slash) in the @Path annotation, like that:

@Path(value = {"product/{product.id}", "product/{product.id}/"})
public void view(Product product) {...}

Note this change only will work to method with @Path annotation. The others methods will not be affected by this change.

The other way is write the class below. Thus, unlike the first, will cause the two URLs (with or without slash) are equivalent.

import javax.enterprise.context.RequestScoped;
import javax.enterprise.event.Event;
import javax.enterprise.inject.Specializes;
import javax.inject.Inject;
import javax.servlet.FilterChain;

import br.com.caelum.vraptor.Result;
import br.com.caelum.vraptor.events.ControllerNotFound;
import br.com.caelum.vraptor.http.MutableRequest;
import br.com.caelum.vraptor.http.MutableResponse;
import br.com.caelum.vraptor.http.route.ControllerNotFoundException;
import br.com.caelum.vraptor.http.route.Router;
import br.com.caelum.vraptor.view.Results;

@RequestScoped
@Specializes
public class Error404 extends DefaultControllerNotFoundHandler {

    private final Router router;

    private final Result result;

    /**
     * @deprecated CDI eyes only
     */
    protected Error404() {
        this(null, null, null);
    }

    @Inject
    public Error404(Router router, Result result, Event<ControllerNotFound> event) {
        super(event);
        this.router = router;
        this.result = result;
    }
    
    @Override
    public void couldntFind(FilterChain chain, MutableRequest request, MutableResponse response) {
        try {
            String uri = request.getRequestedUri();
            if (uri.endsWith("/")) {
                tryMovePermanentlyTo(request, uri.substring(0, uri.length()-1));
            } else {
                tryMovePermanentlyTo(request, uri + "/");
            }
        } catch (ControllerNotFoundException ex) {
            super.couldntFind(chain, request, response);
        }
    }

    private void tryMovePermanentlyTo(MutableRequest request, String newUri) {
        router.parse(newUri, HttpMethod.of(request), request);
        result.use(Results.status()).movedPermanentlyTo(newUri);
    }
}