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; } } }