Building a RESTful API with Revel.go

Revel.go is also a fully-fledged web framework like Python's Django. It is older than Gin, and is termed as a high productivity web framework. It is an asynchronous, modular, and stateless framework. Unlike the go-restful and Gin frameworks where we created the project ourselves, Revel generates a scaffold for working directly.

Install Revel.go using the following command:

go get github.com/revel/revel  

In order to run the scaffold tool, we should install one more supplementary package:

go get github.com/revel/cmd/revel

Make sure that $GOPATH/bin is in your PATH variable. Some external packages install the binary in the $GOPATH/bin directory. If it is in the path, we can access executables system-wide. Here, Revel installs a binary called revel. On Ubuntu or macOS X, you can do it using the following command:

export PATH=$PATH:$GOPATH/bin

Add the preceding line to ~/.bashrc to save the setting. On Windows, you need to directly call the executable by its location. Now we are ready to start with Revel. Let us create a new project called railAPIRevel in github.com/narenaryan:

revel new railAPIRevel

This creates a project scaffold without writing a single line of code. This is how web frameworks abstract things for quick prototyping. A Revel project layout tree looks like this:

    conf/             Configuration directory
app.conf Main app configuration file
routes Routes definition file

app/ App sources
init.go Interceptor registration
controllers/ App controllers go here
views/ Templates directory

messages/ Message files

public/ Public static assets
css/ CSS files
js/ Javascript files
images/ Image files

tests/ Test suites

Out of all those boilerplate directories, three things are important for creating an API. Those are:

  • app/controllers
  • conf/app.conf
  • conf/routes

Controllers are the logic containers that execute the API logic. app.conf allows us to set the host, port, and dev mode/production mode and so on. routes defines the triple of the endpoint, REST verb, and function handler (here, controller's function). This means to define a function in the controller and attach it to a route in the routes file.

Let us use the same example we have seen for go-restful, creating an API for trains. But here, due to the redundancy, we will drop the database logic. We will see shortly how to build GET, POST, and DELETE actions for the API using Revel. Now, modify the routes file to this:

# Routes Config
#
# This file defines all application routes (Higher priority routes first)
#

module:testrunner
# module:jobs


GET /v1/trains/:train-id App.GetTrain
POST /v1/trains App.CreateTrain
DELETE /v1/trains/:train-id App.RemoveTrain

The syntax may look a bit new. It is a configuration file where we simply define a route in this format:

VERB       END_POINT         HANDLER

We haven't defined handlers yet. In the endpoint, the path parameters are accessed using the :param notation. This means for the GET request in the file, train-id will be passed as the path parameter. Now, move to the controllers folder and modify the  existing controller in  app.go file to this:

package controllers
import (
"log"
"net/http"
"strconv"
"github.com/revel/revel"
)
type App struct {
*revel.Controller
}
// TrainResource is the model for holding rail information
type TrainResource struct {
ID int `json:"id"`
DriverName string `json:"driver_name"`
OperatingStatus bool `json:"operating_status"`
}
// GetTrain handles GET on train resource
func (c App) GetTrain() revel.Result {
var train TrainResource
// Getting the values from path parameters.
id := c.Params.Route.Get("train-id")
// use this ID to query from database and fill train table....
train.ID, _ = strconv.Atoi(id)
train.DriverName = "Logan" // Comes from DB
train.OperatingStatus = true // Comes from DB
c.Response.Status = http.StatusOK
return c.RenderJSON(train)
}
// CreateTrain handles POST on train resource
func (c App) CreateTrain() revel.Result {
var train TrainResource
c.Params.BindJSON(&train)
// Use train.DriverName and train.OperatingStatus to insert into train table....
train.ID = 2
c.Response.Status = http.StatusCreated
return c.RenderJSON(train)
}
// RemoveTrain implements DELETE on train resource
func (c App) RemoveTrain() revel.Result {
id := c.Params.Route.Get("train-id")
// Use ID to delete record from train table....
log.Println("Successfully deleted the resource:", id)
c.Response.Status = http.StatusOK
return c.RenderText("")
}

We created API handlers in the file app.go. Those handler names should match the ones we mentioned in the routes file. We can create a Revel controller using a struct with *revel.Controller as its member. Then, we can attach any number of handlers to it. The controller holds the information of incoming HTTP requests so that we can use the information such as query parameters, path parameters, JSON body, form data, and so on in our handler.

We are defining TrainResource to work as a data holder. In GetTrain, we are fetching the path parameters using the c.Params.Route.Get function. The argument to that function is the path parameter we specified in the route file (here, train-id). The value will be a string. We need to convert it to Int type to map it with train.ID. Then, we are setting the response status as 200 OK using the c.Response.Status variable (not function). c.RenderJSON takes a struct and transforms it into the JSON body. 

In CreateTrain, we are adding the POST request logic. We are creating a new TrainResource struct and passing it to a function called c.Params.BindJSON. What BindJSON does is it plucks the parameters from the JSON POST body and tries to find matching fields in the struct and fill them. When we marshal a Go struct to JSON, field names will be translated to keys as it is. But, if we attach the `jason:"id"` string format to any struct field, it explicitly says that the JSON that is marshaled from this struct should have the key id, not ID. This is a good practice in Go while working with JSON. Then, we are adding a status of 201 created to the HTTP response. We are returning the train struct, which will be converted into JSON internally.

The RemoveTrain handler logic is similar to that of GET. A subtle difference is that nothing is sent in the body. As we previously mentioned, database CRUD logic is omitted from the preceding example. It is an exercise for readers to try adding SQLite3 logic by observing what we have done in the go-restful and Gin sections.

Finally, the default port on which the Revel server runs is 9000. The configuration to change the port number is in the conf/app.conf file. Let us follow the tradition of running our app on 8000. So, modify the http port section of the file to the following. This tells the Revel server to run on a different port:

......
# The IP address on which to listen.
http.addr =

# The port on which to listen.
http.port = 8000 # Change from 9000 to 8000 or any port

# Whether to use SSL or not.
http.ssl = false
......

Now, we can run our Revel API server using this command:

revel run github.com/narenaryan/railAPIRevel

Our app server starts at http://localhost:8000. Now, let us make a few API requests:

CURL -X GET "http://10.102.78.140:8000/v1/trains/1"

Output
=======
{
"id": 1,
"driver_name": "Logan",
"operating_status": true
}

POST request:

curl -X POST 
http://10.102.78.140:8000/v1/trains
-H 'cache-control: no-cache'
-H 'content-type: application/json'
-d '{"driver_name":"Magneto", "operating_status": true}'

Output
======
{
"id": 2,
"driver_name": "Magneto",
"operating_status": true
}

DELETE is the same as GET but no body is returned. Here, the code is illustrated to show how to handle the request and response. Remember, Revel is more than a simple API framework. It is a fully-fledged web framework similar to Django (Python) or Ruby on Rails. We have got templating, tests, and many more inbuilt in Revel.

Make sure that you create a new Revel project for GOPATH/user. Otherwise, your Revel command-line tool may not find the project while running the project.

There is middleware support in all the web frameworks we saw in this chapter. go-restful names its middleware Filters, whereas Gin names it custom middleware. Revel calls its middleware interceptors. A middleware reads or writes the request and response before and after a function handler respectively. In Chapter 3, Working with Middleware and RPC, we discuss more about middleware.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.135.201.217