Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Implementing Versioning Support in Restful Web-Services

DZone's Guide to

Implementing Versioning Support in Restful Web-Services

· Integration Zone ·
Free Resource

SnapLogic is the leading self-service enterprise-grade integration platform. Download the 2018 GartnerMagic Quadrant for Enterprise iPaaS or play around on the platform, risk free, for 30 days.

Versioning Restful Web-services:

For maintaing backward compatibilty, at times you may need to support multiple versions of restful web-service resources (here after refered to as resources). It may be desirable at that point to be able to have resources versioned at package, class or individual method level. Annotations come handy in fulfillinhg this need. Here is summary of the steps to add versioning support:

  • Create a custom Annotation:

@Retention(RetentionPolicy.RUNTIME)

@Target({ElementType.TYPE, ElementType.PACKAGE, ElementType.METHOD})

public @interface Versioned{

    String[] versions();

}

  • Annotate Resources:
@Versioned(versions = {"1.0","2.0"})
@Path("user")
@Produces({"application/xml","application/json"})
public class UserResource{…}
  • Finally create an interceptor that intercepts requests, checks if supported version is passed in Accept header (e.g. Accept: application/xml;ver=1.0) and returns response code 415 for unsupported versions (example uses JBoss jax-rs implementation):

@Provider

@ServerInterceptor

public class VersionCheckInterceptor implements PreProcessInterceptor{

    private static final Pattern pattern = Pattern.compile("ver\\s*=\\s*([\\d]+.[\\d]+)");

    public static final String ALLOW_ALL = "ALLOW_ALL";

 

    @Override

    public ServerResponse preProcess(HttpRequest httpRequest, ResourceMethod resourceMethod) throws Failure, WebApplicationException{

        Versioned methodAnnotation = resourceMethod.getMethod().getAnnotation(Versioned.class);

        if(methodAnnotation != null && methodAnnotation.versions() != null && ArrayUtils.contains(methodAnnotation.versions(), ALLOW_ALL))return null;

        Versioned classAnnotation = resourceMethod.getResourceClass().getAnnotation(Versioned.class);

        Versioned packageAnnotation = resourceMethod.getResourceClass().getPackage().getAnnotation(Versioned.class);

        Versioned annotation = methodAnnotation != null?methodAnnotation:(classAnnotation != null?classAnnotation:packageAnnotation);

        if(annotation == null) return null;

        List<String> acceptHeaders = httpRequest.getHttpHeaders().getRequestHeader("Accept");

        boolean reqContainsSupportedVersion = false;

        for(String version : acceptHeaders){

            Matcher matcher = pattern.matcher(version);

            if(!matcher.find()) continue;

            String versNum = matcher.group(1);

            if(versNum == null) continue;

            versionPasssed = true;

            if(ArrayUtils.contains(supportedVersions, versNum)){

                reqContainsSupportedVersion = true;

            }

            break;

        }

        if(!reqContainsSupportedVersion) throw new NotFoundException ();

        return null;

    }

}

With SnapLogic’s integration platform you can save millions of dollars, increase integrator productivity by 5X, and reduce integration time to value by 90%. Sign up for our risk-free 30-day trial!

Topics:

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}