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;

    }

}

Download A Buyer's Guide to Application and Data Integration, your one-stop-shop for research, checklists, and explanations for an application and data integration solution.

Topics:

Opinions expressed by DZone contributors are their own.

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

{{ parent.tldr }}

{{ parent.urlSource.name }}