Friday 12 December 2014

Cross-Domain API Error!!!!

Cross Domain being a pain??

I have been playing around running my own application infrastructure. I have API's and web clients to maintain.

I have looked and used many different scenario of hosting. The one I have finalised on is Heroku. I'll explain later why i find this a fitting solution for my team. Anyway the way I am running Heroku is having the API hosted by a dyno, and the web client hosted on a separate dyno. This is fine from a deployment point of view, however you may quickly find yourself in the horrible world of cross-domian hell when calling your API from a javascript browser hosted behind a different domain.

Firstly I'll explain a little as to why you get this problem. If you are passing a custom header in your request the browser will check with the API if its all good to send this information. On calls made from your browser code to API's it needs o ensure they are allowed to make these requests on this domain. This is done via a OPTIONS request firing before every API request you make from your browser. This will hit your API. The fun now begins, you as owner of the API can allow access to different domains (or all domains if it is felt necessary)

I am running Go as my back end API layer if you have not guessed from my other blogs. To handle my routing I use Gorilla's pat package
{go get gorilla/pat}
Their documentation shows you can route requests for Get, Put, Post and Delete. As you can see there is no request router for OPTIONS. This you may think is a problem. Do not 'panic' yet. The Pat package also has an Add function. Looking into the implementation of there code you can see that the Get, Post and the others all use proxy the Add function. This means we can add our own REST verb to the router. The syntax for the Add function is

func (r *Router) Add(meth, pat string, h http.Handler) *mux.Route

This is slightly different to the Get request

func (r *Router) Get(pat string, h http.HandlerFunc) *mux.Route

Instead of taking the http.HandlerFunc, Add takes just a http.Handler
If like me you have created a HomeHandler to return status.ok on your API route request you can simply cast the same func as so:

r.Add("OPTIONS", "/", http.HandlerFunc(handlers.HomeHandler))

Your home handler now needs to send its access control

func HomeHandler(rw http.ResponseWriter, r *http.Request) {

log.Println("Home Handler Called")

rw.Header().Set("Access-Control-Allow-Origin", "*")
rw.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT")
rw.Header().Set("Access-Control-Allow-Headers", "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, ApiKey")
rw.WriteHeader(http.StatusOK)
}

As you can see I have ApiKey as an allowed header. This is all why I'm having to handle the OPTIONS call.

You will need to set up an OPTIONS handler for each path you are using custom headers.

Hope this as wells helps save someones development time in the feature.