Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save UnquietCode/6323695 to your computer and use it in GitHub Desktop.
Save UnquietCode/6323695 to your computer and use it in GitHub Desktop.

Revisions

  1. UnquietCode created this gist Aug 23, 2013.
    98 changes: 98 additions & 0 deletions CustomRequestMappingHandlerMapping.java
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,98 @@
    package com.sb.server.web;

    import org.springframework.core.annotation.AnnotationUtils;
    import org.springframework.http.HttpStatus;
    import org.springframework.web.servlet.View;
    import org.springframework.web.servlet.mvc.condition.PatternsRequestCondition;
    import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
    import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
    import org.springframework.web.servlet.view.RedirectView;

    import java.lang.reflect.Method;

    /**
    * Custom implementation of annotation-based handler mapping which
    * looks for the {@link RedirectTrailingSlash} annotation and registers
    * a handler to cover the case of the url with or without the slash.
    * The redirect is handled as 301 Redirect
    *
    * @author Ben Fagin
    * @version 2013-08-22
    */
    public class CustomRequestMappingHandlerMapping extends RequestMappingHandlerMapping {

    @Override
    protected void registerHandlerMethod(Object handler, Method method, RequestMappingInfo mapping) {
    super.registerHandlerMethod(handler, method, mapping);

    RedirectTrailingSlash redirectAnnotation = AnnotationUtils.findAnnotation(method, RedirectTrailingSlash.class);
    if (redirectAnnotation == null) { return; }

    for (String pattern : mapping.getPatternsCondition().getPatterns()) {
    registerRedirectHandler(pattern, redirectAnnotation.code(), mapping);
    }
    }

    private void registerRedirectHandler(String originalURL, int statusCode, RequestMappingInfo info) {
    // skip when there's an extension
    if (originalURL.contains(".")) { return; }

    final String otherURL;

    // redirect when not trailing
    if (originalURL.endsWith("/")) {
    otherURL = originalURL.substring(0, originalURL.length()-1);
    }

    // redirect when trailing
    else {
    otherURL = originalURL+"/";
    }

    RedirectHandler handler = new RedirectHandler(originalURL, statusCode);
    registerHandlerMethod(handler, handlerMethod, synthesizeMapping(info, otherURL));
    }

    private RequestMappingInfo synthesizeMapping(RequestMappingInfo existing, String url) {
    String[] patterns = resolveEmbeddedValuesInPatterns(new String[]{url});

    PatternsRequestCondition patternsMatcher = new PatternsRequestCondition(
    patterns, getUrlPathHelper(), getPathMatcher(),
    useSuffixPatternMatch(), false, getFileExtensions()
    );

    return new RequestMappingInfo(
    patternsMatcher,
    existing.getMethodsCondition(),
    existing.getParamsCondition(),
    existing.getHeadersCondition(),
    existing.getConsumesCondition(),
    existing.getProducesCondition(),
    existing.getCustomCondition()
    );
    }

    private static final Method handlerMethod; static {
    try {
    handlerMethod = RedirectHandler.class.getMethod("handle");
    } catch (Exception ex) {
    throw new RuntimeException(ex);
    }
    }

    private static class RedirectHandler {
    private final String redirectURL;
    private final int statusCode;

    public RedirectHandler(String redirectURL, int statusCode) {
    this.redirectURL = redirectURL;
    this.statusCode = statusCode;
    }

    public View handle() {
    RedirectView view = new RedirectView(redirectURL);
    view.setStatusCode(HttpStatus.valueOf(statusCode));
    return view;
    }
    }
    }
    27 changes: 27 additions & 0 deletions RedirectTrailingSlash.java
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,27 @@
    package com.sb.server.web;

    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;

    /**
    * Marker annotation which says that a given method should
    * use a redirect when a trailing slash version is encountered.
    *
    * The method should already be annotated with
    * {@link org.springframework.web.bind.annotation.RequestMapping}.
    *
    * @author Ben Fagin
    * @version 2013-08-22
    * @see CustomRequestMappingHandlerMapping
    */
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface RedirectTrailingSlash {

    /**
    * The status code to use for the redirect.
    */
    int code() default 301;
    }
    27 changes: 27 additions & 0 deletions WebConfiguration.java
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,27 @@
    package com.sb.server.web;

    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
    import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

    /**
    * @author Ben Fagin
    * @version 2013-04-22
    */
    @Configuration
    public class WebConfiguration extends WebMvcConfigurationSupport {

    @Bean
    @Override
    public RequestMappingHandlerMapping requestMappingHandlerMapping() {
    CustomRequestMappingHandlerMapping handlerMapping = new CustomRequestMappingHandlerMapping();
    handlerMapping.setOrder(0);
    handlerMapping.setInterceptors(getInterceptors());
    handlerMapping.setContentNegotiationManager(mvcContentNegotiationManager());
    handlerMapping.setUseTrailingSlashMatch(false);
    handlerMapping.setUseSuffixPatternMatch(false);

    return handlerMapping;
    }
    }