@RequestMapping extensions

AcrossWebModule adds several extensions to the out-of-the-box Spring MVC functionality.

@CustomRequestMapping

Default @RequestMapping implementations can be further specified by adding custom request conditions using @CustomRequestMapping annotations. The latter allows you to restrict a @RequestMapping on any annotation attribute value on the handler method, or even any attribute value that is available on the HttpServletRequest.

A @CustomRequestMapping allows you to specify a number of CustomRequestCondition classes. When building the request mapping info, AcrossWebModule will create beans of every CustomRequestCondition and will supply them with the AnnotatedElement that represents the handler method or handler type they were configured on. Multiple CustomRequestCondition classes can be specified and they can be combined on both handler type and handler method, just like the regular @RequestMapping.

You can also use @CustomRequestMapping as a meta-annotation, which is often much easier in use.

When adding multiple CustomRequestCondition to a mapping, the mapping will only be applied if all conditions match.
Example @CustomRequestMapping

The following example creates an additional request mapping condition that will only use that mapping if the request has a specific referrer header value.

@Controller
public class TestController {
    /**
     * Example mapping that accepts only GET requests made to the
     * fromGoogle path if the request referrer header contains
     * http://www.google.com.
     */
    @GetMapping("/fromGoogle")
    @ReferrerMapping("http://www.google.com")
    public String google() {
        ...
    }
}

/**
 * Custom mapping that will only match if any of the referrers are set.
 * If both type and method level annotation is present, the method level annotation
 * attributes will replace the type level attributes.  This is taken care of in the
 * combine() method of the ReferrerRequestCondition.
 */
@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@CustomRequestMapping(ReferrerRequestCondition.class)
private @interface ReferrerMapping
{
    // possible header values - the mapping will apply if the request
    // referrer has any of these values
    String[] value();
}

/**
 * Condition implementation.  This will be instantiated as a bean,
 * so wiring other components is possible as well as long as they are
 * visible in the AcrossWebModule context.
 */
private static class ReferrerRequestCondition extends AbstractCustomRequestCondition<ReferrerRequestCondition>
{
    private String[] referrers = new String[0];

    // get the configured headers from the annotation
    @Override
    public void setAnnotatedElement( AnnotatedElement element ) {
        referrers = AnnotatedElementUtils.findMergedAnnotation( element, ReferrerMapping.class )
                                         .value();
    }

    @Override
    protected Collection<String> getContent() {
        return Arrays.asList( referrers );
    }

    @Override
    protected String getToStringInfix() {
        return " || ";
    }

    // no relevant combination - other simply replaces this one
    @Override
    public ReferrerRequestCondition combine( ReferrerRequestCondition other ) {
        return other;
    }

    // return self if headers matches, else there is no matching condition
    @Override
    public ReferrerRequestCondition getMatchingCondition( HttpServletRequest request ) {
        if ( StringUtils.containsAny( request.getHeader( HttpHeaders.REFERER ), referrers ) ) {
            return this;
        }
        return null;
    }

    // always consider them equal
    // there is no relevant ordering if headers are different
    @Override
    public int compareTo( ReferrerRequestCondition other, HttpServletRequest request ) {
        return 0;
    }
}

Prefixed request mappings

AcrossWebModule contains a PrefixingRequestMappingHandlerMapping implementation. The PrefixingRequestMappingHandlerMapping allows request mappings to be prefixed at runtime. Core infrastructure is the PrefixingRequestMappingHandlerMapping and the PrefixingHandlerMappingConfigurer. The latter can be used to add interceptors only to request mappings attached to a specific PrefixingRequestMappingHandlerMapping instance.

PrefixingRequestMappingHandlerMapping also fully supports custom request conditions.

Examples of prefixing request mappings can be found in the AdminWebModule and DebugWebModule.