Skip to content

Use custom filter to enforce API versions

srfrog edited this page Aug 9, 2014 · 3 revisions

Suppose you need to enforce an API version for a resource or a specific route.

The client requests the API version via the Accept header:

~$ curl -i -X GET -H 'Accept: application/vnd.relax+json;version=1.1' https://api.company.com

Go-Relax will create a request info variable "content.version" with value of "1.1".

Now, you have two ways to enforce versions.

  • Enforce within your application:
const API_VERSION "2.0"

func (r *MyResource) Read(ctx *relax.Context) {
  version := ctx.Info.Get("content.version")
  if version != API_VERSION && version != "current" {
    ctx.Error(400, "Invalid API version")
  }
  ctx.Respond("OK!")
}

This works fine, but leads to writing a lot of redundant if/else code.

  • Use a custom filter:
type APIVersionFilter struct {
  MinVersion float64
}
func (self *APIVersionFilter) Run(next relax.HandlerFunc) relax.HandlerFunc {
  if self.MinVersion == 0 {
    self.MinVersion = 1.5
  }
  return func(ctx *relax.Context) {
    if ctx.Info.Get("content.version") != "current" && ctx.Info.GetFloat("content.version") < self.MinVersion {
      ctx.Error(400, "API version requested is no longer supported.")
      return
    }
    // requested version is supported
    next(ctx)
  } 
}

You can use the custom filter everywhere as needed:

apiver := &APIVersionFilter{MinVersion: 1.2}

myservice := relax.NewService("/api")
// for the whole service:
myservice.Use(apiver)

// or just for resource(s)
res1 := myservice.Resource(tickets, apiver)

// or just for route(s)
res1.GET("{uint:ticketid}/something", tickets.Something, apiver)
res1.PUT("work/{uint:ticketid}", tickets.Hardwork, apiver)

// super-alpha-beta API features, require version=10
apiver.MinVersion = 10.0
res1.POST("another", tickets.Another, apiver)