go-restful first working example

In a previous post, I discussed the design of go-restful which is a package for building REST-style WebServices using the Google Go programming language.

Today, I completed the implementation of that package which provides the basics:

  • Creating a WebService with Routes (mapping between Http Request and a Go function)
  • Each Route requires information about the Http method (GET,POST,…), URL Path (/users..), Mime-types and the function it binds to
  • Functions get passed in a Request and a Response
  • The Request is used to access Path and Query parameters , Headers and request content (XML, JSON,…)
  • The Response is used to set Status, Headers, and response content
  • Both Request and Response can Unmarshal and Marshal objects to and from XML,JSON using the standard packages
All of this can best be illustrated with a small example ; a Webservice for CRUD operations on User objects. We start by creating the file userservice.go inside its own folder userservice.

package userservice

import (
    "github.com/emicklei/go-restful"
    "log"
)

type User struct {
    Id, Name string
}

Type User represents our domain object.

Next section in this file is the API specification of the WebService. The API is a collection of Route objects which specify how an incoming Http Request is mapped to a function.

func New() *restful.WebService {
    service := new(restful.WebService)
    service.
        Path("/users").
        Consumes(restful.MIME_XML, restful.MIME_JSON).
        Produces(restful.MIME_XML, restful.MIME_JSON)
        
    service.Route(service.GET("/{user-id}").To(FindUser))
    service.Route(service.POST("").To(UpdateUser))
    service.Route(service.PUT("/{user-id}").To(CreateUser))
    service.Route(service.DELETE("/{user-id}").To(RemoveUser))
        
    return service
}

First, the service is initialized with a root URL path for all routes, the MIME-types each route can receive (Consumes) and the MIME-types it can respond (Produces). It is possible to specify this for each individual Route. Next, the service specifies what Routes it can provide. The function call GET(“/{user-id}”) is just a shortcut for Method(“GET”).Path(“/{user-id}”) and creates a RouteBuilder object. Using the RouteBuilder we specify that the route with method GET will be handled by the function FindUser.

Next section in this file will be the function definitions for each Route.

func FindUser(request *restful.Request, response *restful.Response) {
    id := request.PathParameter("user-id")
    // here you would fetch user from some persistence system
    usr := &User{Id: id, Name: "John Doe"}
    response.WriteEntity(usr)
}

The signature for functions that are used in Routes are all the same and contain a restful Request,Response pair. Request is a wrapper for a http Request that provides convenience methods. Response is a wrapper on the actual http ResponseWriter. This design gives you as a developer complete access to the underlying http structures and access to common restful functions such as WriteEntity. The WriteEntity function will inspect the Accept header of the request to determine the Content-Type of the response and how to Marshal the object (in this case the User).

The remainder of the userservice.go file will contain the other function definitions.

func UpdateUser(request *restful.Request, response *restful.Response) {
    usr := new(User)
    err := request.ReadEntity(&usr)
    // here you would update the user with some persistence system
    if err == nil {
        response.WriteEntity(usr)
    } else {
        response.WriteError(http.StatusInternalServerError,err)
    }
}

func CreateUser(request *restful.Request, response *restful.Response) {
    usr := User{Id: request.PathParameter("user-id")}
    err := request.ReadEntity(&usr)
    // here you would create the user with some persistence system
    if err == nil {
        response.WriteEntity(usr)
    } else {
        response.WriteError(http.StatusInternalServerError,err)
    }
}

func RemoveUser(request *restful.Request, response *restful.Response) {
    // here you would delete the user from some persistence system
}

Now,we have completed the specification and implementation of the UserService. The next snippet shows an example how to use this service in an application.

package main

import (
    "github.com/emicklei/go-restful"
    "log"
    "net/http"
    "userservice"
)

func main() {
    restful.Add(userservice.New())
    log.Fatal(http.ListenAndServe(":8080", nil))
}

The go-restful project source can be found on github and is documented on go.pkgdoc.org

comments powered by Disqus