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

Modernize your application architectures with microservices and APIs with best practices from this free virtual summit series. Brought to you in partnership with CA Technologies.

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;

    }

}

The Integration Zone is proudly sponsored by CA Technologies. Learn from expert microservices and API presentations at the Modernizing Application Architectures Virtual Summit Series.

Topics:

Opinions expressed by DZone contributors are their own.

THE DZONE NEWSLETTER

Dev Resources & Solutions Straight to Your Inbox

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.

X

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

{{ parent.tldr }}

{{ parent.urlSource.name }}