No more troubles with CORS using Gorilla/Mux and Go
Tags: webdev, go, opensource, programming
Published: 2022-03-25 05:15:13

## Introduction
Nowadays big backend solutions typically look like a **_set of microservices_** interacting with each other and with **_one or more frontend (typically browser) applications_**.
These services could be distributed across **_multiple virtual or physical machines_** and as a result every application (means server that hosts application) would get a **_different domain name / ip address_**.
A frontend application **_typically uses Javascript or Typescript to interact with the backend_**, but if a request goes to a different (from the frontend server) server a **_browser might assume that it poses a potential risk to security and block it_**. To solve this issue the backend API should provide headers with a **_valid origin_** (a domain name or ip where the frontend application is located). Today we gonna learn how to properly setup Gorilla/mux Web API to forever forget about CORS.


## What we should provide from backend
Consider the following REST resource i.e. user:
* `GET /api/user/` - for getting all users
* `GET /api/user/{id}/` - for getting single user by id
* `POST /api/user/` - for creating new user
* `PUT /api/user/{id}/` - for update existing user
* `DELETE /api/user/{id}/` - for delete existing user

Basically, we have to implement the following things:
1. Add response to OPTIONS to endpoints: `OPTIONS /api/user/` and 
 `OPTIONS /api/user/{id}/` with the following headers:
     - `Access-Control-Allow-Origin` - domain name/ip or * for any 
       origin;
     - `Access-Control-Allow-Headers` - here we could simply set *
     - `Access-Control-Allow-Methods` - we could simply list all 
       methods, but i think it is better to send `OPTIONS, 
       GET, POST ` as a value for `/api/user/` and `OPTIONS, GET, 
       PUT, DELETE` for `/api/user/{id}/`;
2. Add optional origin check on server side (if neccessary).

Today’s article is about the first one, you can find the second one `github.com\gorilla\handlers`. it It has a middleware that restricts access only to specific origins. Perhaps you noticed that your backend is not always required to respond to OPTIONS request because **_OPTIONS request is not being sent by browsers_** for [simple requests](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS). But in the above example we have to do this. When your **_api is quite big you have to constantly add preflight handlers_** (see example below) and every once in a while **_you could forget to do that especially when you have just finished designing some complicated endpoints_**. Using our (**_Wissance LLC_**) solution ([open source github package](https://github.com/Wissance/gwuu/blob/master/api/rest/gorilla_cors.go)) you can forget about adding preflight handlers because [our package](https://github.com/Wissance/gwuu) does this automatically. **_Please give us a star if you find our package useful for you (it’s very important to us)._**

## How to make all these works easily

We implemented our own `HandlerFunc` that has a signature similar to `mux.Router.HandlerFunc` but with some minor differences:
1. We are passing a pointer to `mux.Router` as a first parameter. We did this because we have a requirement to work with subrouters too, take a look at [this unit test](https://github.com/Wissance/gwuu/blob/master/api/rest/gorilla_cors_test.go) you can use a reference. 
2. We are passing handler methods parameters as a last variardic parameter instead of calling `.Methods()` when assigning route handler, we think this also makes things simpler.

Basically for auto preflight handling we need to create `WebApiHandler` instance and pass the required origin value to it as its second argument  (`Access-Control-Allow-Origin` header **_supports only single value_**) and use our `HandlerFunc` instead of gorilla/mux, see example: 
```go
        handler := NewWebApiHandler(true, AnyOrigin)
	// Get only method
        baseUrl := "http://127.0.0.1:8998"
	configResource := baseUrl + "/api/config/"
	handler.HandleFunc(handler.Router, configResource, 
                           webApi.GetConfigHandler,"GET")
	// full crud
	functionResourceRoot := baseUrl + "/api/function/"
	handler.HandleFunc(handler.Router, functionResourceRoot, 
                           webApi.GetAllFunctions, "GET")
	handler.HandleFunc(handler.Router, functionResourceRoot, 
                           webApi.CreateFunction, "POST")
	functionResourceById := baseUrl + "/api/function/{id:[0-9]+}/"
	handler.HandleFunc(handler.Router, functionResourceById, 
                           webApi.GetFunctionById, "GET")
	handler.HandleFunc(handler.Router, functionResourceById, 
                           webApi.UpdateFunction, "PUT")
	handler.HandleFunc(handler.Router, functionResourceById, 
                           webApi.DeleteFunction, "DELETE")
```
For the snippet above we have to add 3 additional preflight Handlers: 
* `http://127.0.0.1:8998/api/config/`;
* `http://127.0.0.1:8998/api/function/`;
* `http://127.0.0.1:8998/api/function/{id}`;

Before we created our package we always were writing handlers like this:

```go
        router.HandleFunc(functionResourceRoot, 
                          webApi.PreflightRoot).Methods("OPTIONS")
	router.HandleFunc(functionResourceById, 
                          webApi.PreflightByID).Methods("OPTIONS")

        func (webApi *WebApiContext) PreflightRoot(respWriter http.ResponseWriter, request *http.Request) {
	        rest.EnableCors(&respWriter)
	        respWriter.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS")
}

        func (webApi *WebApiContext) PreflightByID(respWriter http.ResponseWriter, request *http.Request) {
	        rest.EnableCors(&respWriter)
	        respWriter.Header().Set("Access-Control-Allow-Methods", "GET, PUT, DELETE, OPTIONS")
}
```

We also should show what the `webApi` object is and how some of its request handler functions look:
```go
type WebApiContext struct {
	Db     *gorm.DB // passing gorm to Context
	Config *config.AppConfig
        // other fields
}

func (webApi *WebApiContext) GetAllFunctions(respWriter http.ResponseWriter, request *http.Request) {
         // return here array of functions, body omitted
}

func (webApi *WebApiContext) GetFunctionById(respWriter http.ResponseWriter, request *http.Request) {
         // return function by id, body omitted
}
```

Let's see how our code changed after we added the library:
```go
        webApi := rest.WebApiContext{Db: appContext.ModelContext.Context, Config: appContext.Config, WebApiHandler: gr.NewWebApiHandler(true, gr.AnyOrigin)}
	 
        webApi.WebApiHandler.Router.Use( keycloakAuthService.KeycloakAuthMiddleware)
	webApi.WebApiHandler.Router.Use(r.InspectorMiddleware)
	router := webApiContext.WebApiHandler.Router
	router.StrictSlash(true)

	// function resource
	webApi.WebApiHandler.HandleFunc(router, baseUri+"/function/", webApi.GetAllFunctions, "GET")
	webApi.WebApiHandler.HandleFunc(router, baseUri+"/function/find/", webApi.FindFunctions, "GET").Queries("query", "{query}")
	webApi.WebApiHandler.HandleFunc(router, baseUri+"/function/name/", webApi.GetFunctionsNames, "GET")
	webApi.WebApiHandler.HandleFunc(router, baseUri+"/function/{id:[0-9]+}/", webApi.GetFunctionById, "GET")
	webApi.WebApiHandler.HandleFunc(router, baseUri+"/function/{id:[0-9]+}/body/", webApi.GetFunctionWithBodyById, "GET")
	webApi.WebApiHandler.HandleFunc(router, baseUri+"/function/", webApi.CreateFunction, "POST")
	webApi.WebApiHandler.HandleFunc(router, baseUri+"/function/{id:[0-9]+}/", webApi.UpdateFunction, "PUT")
	webApi.WebApiHandler.HandleFunc(router, baseUri+"/function/{id:[0-9]+}/", webApi.DeleteFunction, "DELETE")
	webApi.WebApiHandler.HandleFunc(router, baseUri+"/function/filter/", webApi.FilterFunctions, "GET")
```

## Conclusion
We made this package not because we have a lot of spare time on our hands, but because we were forced to do that because of a huge amount of CORS-related errors we were getting every time we worked on something big. Now that we have this solution CORS is no longer a problem.