Categorygithub.com/GoLangWebSDK/rest
modulepackage
0.1.0
Repository: https://github.com/golangwebsdk/rest.git
Documentation: pkg.go.dev

# README

REST

REST Module allows you to quickly build controller based RESTfull API. It's built on top of net/http std lib multiplexer so no additional dependencies are required.

Quick Start

Quick example demonstrates how you can simply turn any struct into a rest controller by embbeding the base rest.Controller struct. The embbeding implements the RestHandler interface with the CRUD methods that are mapped to the request paths. To implement custom CRUD handlers just overwrite the interface methods like in the example.

Checkout the examples for more working examples.

Install

go get github.com/GoLangWebSDK/rest

Usage

package main

import (
	"fmt"
	"net/http"

	"github.com/GoLangWebSDK/rest"
)

func main() {
	router := rest.NewRouter()

	apiRouter := router.RoutePrefix("/api")

	apiRouter.Route("/users").Controller(NewUsersController())

	err := http.ListenAndServe(":8080", router.Mux)
	if err != nil {
		fmt.Println(err)
	}
}


type UsersController struct {
	rest.Controller
}

func NewUsersController(router *rest.Router) *UsersController {
	return &UsersController{
		Controller: rest.New(router)
	}
}

// Mapped to GET /api/users
func (ctrl *UsersController) Create(ctx *rest.Context) {
	fmt.Println("UsersController::Create")
}

// Mapped to GET /api/users/{id}
func (ctrl *UsersController) Read(ctx *rest.Context) {
	fmt.Println("UsersController::Read")
}

// Mapped to GET /api/users
func (ctrl *UsersController) ReadAll(ctx *rest.Context) {
	fmt.Println("UsersController::ReadAll")
}

// Mapped to PUT /api/users/{id}
func (ctrl *UsersController) Update(ctx *rest.Context) {
	fmt.Println("UsersController::Update")
}

// Mapped to DELETE /api/users/{id}
func (ctrl *UsersController) Destroy(ctx *rest.Context) {
	fmt.Println("UsersController::Destroy")
}

Controllers

Rest module comes with 2 base controllers you can use.

  • Controller - This is the base controller that all other controllers inherit from.
  • RestController - Helps you to express REST patterns as methods on a controller.

RestController


func main() {
	router := rest.NewRouter()

	router.StrictSlash(true)

	ctrl := rest.NewController(router)

	ctrl.Get("/hello", func(session *rest.Session) {
		fmt.Fprintf(session.Response, "Hello World!")
	})

	ctrl.Get("/check/", func(session *rest.Session) {
		fmt.Fprintf(session.Response, "Route is healthy!")
	})

	ctrl.Post("/create", func(session *rest.Session) {
		fmt.Fprintf(session.Response, "Post route is working!")		
	})

	ctrl.Put("/update", func(session *rest.Session) {
		fmt.Fprintf(session.Response, "Put route is working!")
	})

	ctrl.Delete("/delete", func(session *rest.Session) {
		fmt.Fprintf(session.Response, "Delete route is working!")
	})

	http.ListenAndServe(":8080", router)
}

(Base) Controller

Base controller is the defualt implementation of the RestHandler interface.

type RestHandler interface {
	Run() Controller
	Create(*Session)
	Read(*Session)
	ReadAll(*Session)
	Update(*Session)
	Destroy(*Session)
}

Every controller should embbed rest.Controller to become the handler for the REST requests. There 2 general ways you can use it:

Use the default methods (Create, Read, ReadAll, Update, Destroy)


type UsersController struct {
	rest.Controller
}

func NewUsersController(router *rest.Router) *UsersController {
	return &UsersController{
		Controller: rest.New(router),
	}
}

// Executes before any of the default methods
func (ctrl *UsersController) Run() Controller {
	return ctrl.Controller
}

// Mapped to GET /api/users
func (ctrl *UsersController) Create(ctx *rest.Context) {
	fmt.Println("UsersController::Create")
}

// Mapped to GET /api/users/{id}
func (ctrl *UsersController) Read(ctx *rest.Context) {
	fmt.Println("UsersController::Read")
}

// Mapped to GET /api/users
func (ctrl *UsersController) ReadAll(ctx *rest.Context) {
	fmt.Println("UsersController::ReadAll")
}

// Mapped to PUT /api/users/{id}
func (ctrl *UsersController) Update(ctx *rest.Context) {
	fmt.Println("UsersController::Update")
}

// Mapped to DELETE /api/users/{id}
func (ctrl *UsersController) Destroy(ctx *rest.Context) {
	fmt.Println("UsersController::Destroy")
}

Use custom routing and methods by using 'Run() Controller' to specify custom routes, handlers and overwrite the default handlers.


type UsersController struct {
	rest.Controller
}

func NewUsersController() *UsersController {
	return &UsersController{
		Controller: rest.New(router),
	}
}

func (ctrl *UsersController) Run() Controller {
	ctrl.Get("/", GetUser)
	
	return ctrl.Controller
}

func (ctrl *UsersController) GetUser(ctx *rest.Context) {
	fmt.Println("UsersController::GetUser")	
}

The Run() method is inspired by ruby sinatra framework that allows you to define your routing in a more DSL way. Build your API using only Run() Controller method and the Get, Post, Put and Delete handlers.


type UsersController struct {
	rest.Controller
}

func NewUsersController() *UsersController {
	return &UsersController{
		Controller: rest.New(router),
	}
}

func (ctrl *UsersController) Run() Controller {
	ctrl.Get("/", func(session *rest.Session) {
		fmt.Println("UsersController::GetUsers")
	})

	ctrl.Get("/{id}", func(session *rest.Session) {
		fmt.Println("UsersController::GetUser")
	})

	ctrl.Post("/", func(session *rest.Session) {
		fmt.Println("UsersController::CerateUsers")
	})

	ctrl.Put("/{id}", func(session *rest.Session) {
		fmt.Println("UsersController::UpdateUser")
	})

	ctrl.Delete("/{id}", func(session *rest.Session) {
		fmt.Println("UsersController::DeleteUser")
	})

	return ctrl.Controller
}

Routing

Group and route the requests under a common path to your controller struct.

    router.Route("/users").Controller(NewUsersController(router))

Now all the requests with /users path prefix will be routed to the UsersController controller and all the routes in the controller will append /users with default or custom routes defined in the controller.

Path Prefix

If you need additional prefix for the routes use the RoutePrefix() method.


    apiRouter := router.RoutePrefix("/api")
    
    // Routed to /api/users
    apiRouter.Route("/users").Controller(NewUsersController(router))

To group and verions API routes:

    
    apiRouter := router.API("/v1")

    // Routed to /api/v1/users 
    apiRouter.Route("/users").Controller(NewUsersController(router))

Middlewares

REST supports middleware that can be used to intercept and modify the request and response.

    
    router.Use(Logger)

    router.Route("/users").Controller(NewUsersController(router))

    func Logger(next http.Handler) http.Handler {
	    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		    fmt.Println("Running Logger Middleware for Request: ", r.URL.Path)
		    next.ServeHTTP(w, r)
	    })
    }

Strict Slash

To handle trailing slashes use router.StrictSlash(false), defult is true.

    
    router.StrictSlash(false)

    router.Route("/users/").Controller(NewUsersController(router))

Strict slash set to true will strip all your routes with a trailing slash and redirect them to their non-trailing version. When set to false the routes will be left intact and you have to handle the trailing slash manually.

Strict slash is true:

/users/ route will be mapped as /users and incoming requests to /users/ will be redirected to /users.

Strict slash is false:

You need to manually handle the trailing slash.

    
    router.StrictSlash(false)
    
    router.Route("/users").Controller(NewUsersController(router))
    router.Route("/users/").Controller(NewUsersController(router))

Routes

REST module comes with Routes interface that allows loasing controller routes and middlewares anywhere in the codebase by implementing the interface.

    type Routes interface {
	    LoadRoutes(*Router)
	    LoadMiddleware(*Router)
    }

Ie. app.go:



import (
	"fmt"

	"github.com/GoLangWebSDK/rest"
)

type Routes struct{}

func NewRoutes() *Routes {
	return &Routes{}
}

var _ rest.Routes = &Routes{}

func (routes *Routes) LoadRoutes(router *rest.Router) {
	// router.API() will add a prefix /api to all subsequent routes.
	// It also allows you to set version, for example router.API("/v1"),
	// in which case the final path prefix will be /api/v1
	apiRouter := router.API()

	apiRouter.Route("/posts").Controller(NewPostsController(router))

	fmt.Println("Current route final path: ", apiRouter.CurrentRoute.FinalPath)

}

func (routes *Routes) LoadMiddleware(router *rest.Router) {
	router.Use(
		Logger,
		Auth,
	)
}

main.go



func main() {
	fmt.Println("Spinning up server...")
	router := rest.NewRouter()
	routes := NewRoutes()

	router.Load(routes)

	fmt.Println("Server running on port 8080")
	err := http.ListenAndServe(":8080", router)
	if err != nil {
		fmt.Println(err)
	}
}

Session

The REST session is a decorator around the http.Request and http.ResponseWriter that keeps all the data and some usefull methods for the current request in a single rest session.


    type Session struct {
	    JsonDecoder *json.Decoder
	    Request     *http.Request
	    Response    http.ResponseWriter
    }

Session allows fetching path values from the request, it is also used to decode incoming request bodies and to format JSON responses with proper status code.

	ctrl.Post("/users/create", func(session *rest.Session) {
		fmt.Println("UsersController::Create")
		var requestBody struct {
			Username string `json:"username"`
			Password string `json:"password"`
		}

		err := session.JsonDecode(&requestBody)
		if err != nil {
			session.SetStatus(http.StatusBadRequest)
			return
		}

		if requestBody.Password == "" || requestBody.Username == "" {
			session.SetStatus(http.StatusBadRequest)
			return
		}

		var responseJson struct {
			Status bool   `json:"status"`
			Msg    string `json:"msg"`
		}

		responseJson.Status = true
		responseJson.Msg = fmt.Sprintf("User %s created", requestBody.Username)

		session.JsonResponse(http.StatusOK, responseJson)
	})

session.GetID() - return the session ID as uint, will search for {id} in the URL path if no value is provided.


    ctrl.Get("/{id}/posts/{post}", func(session *rest.Session) {
        userID := session.GetID()
        postID := session.GetID("post")
    })

  • set the reponse status - session.SetStatus
  • set response header - session.SetHeader,
  • reponde with JSON, and set Content-type application/json - session.JsonResponse
  • fetch path parameters - session.GetParam("paramName")

Fetch common params like slug or uuid:


    ctrl.Get("/{uuid}/posts/{slug}", func(session *rest.Session) {
        userUUID := session.GetUUID()
        postSlug := session.GetSlug()
    })

# Packages

No description provided by the author

# Functions

No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author

# Structs

No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author

# Interfaces

No description provided by the author
No description provided by the author

# Type aliases

No description provided by the author
No description provided by the author