stgin

package module
v0.3.5 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 11, 2022 License: MIT Imports: 16 Imported by: 1

README

STGIN

STgin is a functional rest framework that provides easy APIs in order to maintain your application RESTful API server.

A comparison between STgin and go-gin, writing a simple API:

// Given response type as
type HealthCheckResponse struct {
	DBConnection    bool    `json:"database_connection"`
	Message         string  `json:"message"`
}
// and request type as
type HealthCheckRequest struct {
	Whatever        string   `json:"whatever"`
}

STgin implementation:

health := stgin.POST("/health", func(request stgin.RequestContext) stgin.Status {
    var reqBody HealthCheckRequest
    request.Body.JSONInto(&reqBody)
    // do something with reqBody
    var response HealthCheckResponse = GetHealth()
    if response.DBConnection {
        return stgin.Ok(&response) 
    } else {
        return stgin.InternalServerError(&response)
    }
})

go-gin implementation:

r.POST("/health", func(c *gin.Context) {
    var reqBody HealthCheckRequest
    bodyBytes, err := ioutil.ReadAll(c.Request.Body)
	// potential error handling
    err = json.Unmarshal(bodyBytes, &reqBody)
	// potential error handling
	// do something with reqBody
    var response HealthCheckResponse = GetHealth()
    jsonResponse, _ := json.Marshal(response)
    if response.DBConnection {
    	c.Status(200)
    	_, writeError := c.Writer.Write(jsonResponse)
    	// potential error handling
    } else {
    	c.Status(500)
    	_, writeError = c.Writer.Write(jsonResponse)
    	// potential error handling
    }
})

Or just easily add headers with a receiver function instead of manually writing headers:

stgin.Ok(&body).WithHeaders(...)

Structure

The structure of STgin types and interfaces is pretty simple, a Server may have several Controllers, and each controller may have serveral Routes.

    
    -Server =>
        -Controller 1 ->
            -Route 1
            -Route 2
        -Cotroller 2 ->
            -Route 1

Server: Is run on the specified port, contains the controllers.

Controller: Contains routes which are exposed to the server, has a name, and may have a route prefix (i.e., /home)

Route: Holds route specifications (i.e., method, path, API action)

RequestContext: Holds the information about the requests, such as uri, body, headers, ...

Status: Is a wrapper around an actual http response, holds status code, response headers, response body, ... (i.e., Ok, BadRequest, ...)

API: Is a type alias for a function which accepts a request context and returns a status.

Path Parameters

  • How to define?

    When defining a route, there are 2 possible values between any 2 slashes, a literal string (like ".../home/..."), or a path parameter specification. Path parameters must have a name, and an optional type which the parameter might have (string is used as default, if no type or an invalid type is provided).

            //same as $username:string
    stgin.GET("/users/$username/purchases/$purchase_id:int". ...)
    
    // "/users/$username/purchases/$purchase_id" also accepts the correct route like "/users/John/purchases/12",
    // but in case an alphanumeric value is passed as purchase_id, conversion from string to int in the api is manual
    // and needs manual error checking
    
  • How to get?

    username, exists := request.GetPathParam("username")
    // or if you're sure about the existence, 
    username := request.MustGetPathParam("username")
    

Custom Actions

STgin does not provide actions about stuff like Authentication, because simple authentication is not useful most of the time, and you may need customized authentications.

For instance:

type AuthInfo struct {
	Username    string      `json:"username"`
	AccountId   int         `json:"account_id"`
	Roles       []string    `json:"roles"`
}

func authenticate(rc stgin.RequestContext) (AuthInfo, bool) {
    if name, found := rc.GetQuery("user"); !found {
    	...
    } else {
        ...
    }
}

func Authenticated(rc stgin.RequestContext, action func(AuthInfo) stgin.Status) stgin.Status {
    authInfo, isAuthenticated := authenticate(rc)
    if !isAuthenticated {
        return stgin.Unauthorized(...)
    } else {
        return action(authInfo)
    }
}

// In the apis section
myAPI := stgin.GET("/test", func(request stgin.RequestContext) stgin.Status {
    return Authenticated(request, func(authInfo AuthInfo) stgin.Status {
        return stgin.Ok(&Greet(authInfo.Username))
    })
})

Listeners

Listeners are functions, which can affect the request and response based on the defined behavior. For instance, a ResponseListener is a function which receives a response, and returns a response, it can be used when you want to apply something to all the responses in server layer or controller layer (i.e., providing CORS headers). There are 3 types of listeners:

  • RequestListener: func(RequestContext) RequestContext [Can be used to mutate request before the controller receives it]
  • ResponseListener: func(Status) Status [Can be used to add/remove additional information to a raw controller response]
  • APIListener: func(RequestContext, Status) [Can be used to do stuff like logging, ...]

There are some listeners provided inside the STgin package which can be used inside a server or a controller [API watcher/logger, recovery].

Custom Recovery

An ErrorHandler can be provided by the developer, to provide custom error handling behavior. Definition of an ErrorHandler function is pretty straight forward, you just define a function which takes the request and the error, and decides what to return as the status.

var myErrorHandler stgin.ErrorHandler = func(request RequestContext, err any) stgin.Status {
    if myCustomErr, isCustomErr := err.(CustomErr); isCustomErr {
        return stgin.BadRequest(...)
    } else {
        return stgin.InternalServerError(...)
    }
}

TODOs

  • Add most common statuses as predefined functions
  • Add support for cookies

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type API

type API = func(c RequestContext) Status

type APIListener added in v0.2.3

type APIListener = func(RequestContext, Status)
var WatchAPIs APIListener = func(request RequestContext, status Status) {
	now := time.Now()
	difference := fmt.Sprint(now.Sub(request.receivedAt))
	statusString := fmt.Sprintf("%v%d%v", getColor(status.StatusCode), status.StatusCode, colored.ResetPrevColor)
	stginLogger.InfoF("%v -> %v | %v | %v", request.Method, request.Url, statusString, difference)
}

type Controller

type Controller struct {
	Name string
	// contains filtered or unexported fields
}

func NewController

func NewController(name string) *Controller

func (*Controller) AddAPIListeners added in v0.2.3

func (controller *Controller) AddAPIListeners(listeners ...APIListener)

func (*Controller) AddRequestListeners

func (controller *Controller) AddRequestListeners(listeners ...RequestListener)

func (*Controller) AddResponseListener

func (controller *Controller) AddResponseListener(listeners ...ResponseListener)

func (*Controller) AddRoutes

func (controller *Controller) AddRoutes(routes ...Route)

func (*Controller) SetRoutePrefix

func (controller *Controller) SetRoutePrefix(prefix string)

type ErrorHandler added in v0.2.4

type ErrorHandler = func(request RequestContext, err any) Status

type MalformedRequestContext added in v0.2.5

type MalformedRequestContext struct {
	// contains filtered or unexported fields
}

func (MalformedRequestContext) Error added in v0.2.5

func (mrc MalformedRequestContext) Error() string

type Param added in v0.2.6

type Param struct {
	// contains filtered or unexported fields
}

func MatchAndExtractPathParams added in v0.3.5

func MatchAndExtractPathParams(pattern, uri string) ([]Param, bool)

type Params added in v0.2.6

type Params = []Param

type ParseError added in v0.2.5

type ParseError struct {
	// contains filtered or unexported fields
}

func (ParseError) Error added in v0.2.5

func (ije ParseError) Error() string

type RequestBody

type RequestBody struct {
	// contains filtered or unexported fields
}

func (*RequestBody) JSONInto added in v0.2.5

func (body *RequestBody) JSONInto(a any)

func (*RequestBody) SafeJSONInto added in v0.2.5

func (body *RequestBody) SafeJSONInto(a any) error

func (*RequestBody) SafeXMLInto added in v0.2.5

func (body *RequestBody) SafeXMLInto(a any) error

func (*RequestBody) XMLInto added in v0.2.5

func (body *RequestBody) XMLInto(a any)

type RequestContext

type RequestContext struct {
	Url         string
	QueryParams map[string][]string
	PathParams  Params
	Headers     http.Header
	Body        *RequestBody

	Method        string
	ContentLength int64
	Host          string
	MultipartForm func() *multipart.Form
	Scheme        string
	RemoteAddr    string
	// contains filtered or unexported fields
}

func (RequestContext) GetPathParam

func (c RequestContext) GetPathParam(name string) (string, bool)

func (RequestContext) GetQueries

func (c RequestContext) GetQueries(name string) []string

func (RequestContext) GetQuery

func (c RequestContext) GetQuery(name string) (string, bool)

func (RequestContext) MustGetPathParam added in v0.3.2

func (c RequestContext) MustGetPathParam(name string) string

type RequestListener

type RequestListener = func(RequestContext) RequestContext

type ResponseListener

type ResponseListener = func(Status) Status

type Route

type Route struct {
	Path   string
	Method string
	Action API
	// contains filtered or unexported fields
}

func DELETE

func DELETE(path string, api API) Route

func GET

func GET(path string, api API) Route

func OPTIONS added in v0.2.2

func OPTIONS(path string, api API) Route

func PATCH

func PATCH(path string, api API) Route

func POST

func POST(path string, api API) Route

func PUT

func PUT(path string, api API) Route

type RouteCreationStage

type RouteCreationStage struct {
	// contains filtered or unexported fields
}

func OnDelete

func OnDelete(path string) RouteCreationStage

func OnGET

func OnGET(path string) RouteCreationStage

func OnOptions added in v0.2.2

func OnOptions(path string) RouteCreationStage

func OnPOST

func OnPOST(path string) RouteCreationStage

func OnPUT

func OnPUT(path string) RouteCreationStage

func OnPatch

func OnPatch(path string) RouteCreationStage

func OnPath

func OnPath(path string) RouteCreationStage

func (RouteCreationStage) Do

func (stage RouteCreationStage) Do(api API) Route

func (RouteCreationStage) WithMethod

func (stage RouteCreationStage) WithMethod(method string) RouteCreationStage

type Server

type Server struct {
	Controllers []*Controller
	// contains filtered or unexported fields
}

func NewServer

func NewServer(port int) *Server

func (*Server) AddAPIListeners added in v0.2.3

func (server *Server) AddAPIListeners(listeners ...APIListener)

func (*Server) AddRequestListeners

func (server *Server) AddRequestListeners(listeners ...RequestListener)

func (*Server) AddResponseListeners

func (server *Server) AddResponseListeners(listeners ...ResponseListener)

func (*Server) MethodNowAllowedAction added in v0.2.2

func (server *Server) MethodNowAllowedAction(action API)

func (*Server) NotFoundAction added in v0.2.2

func (server *Server) NotFoundAction(action API)

func (*Server) Register

func (server *Server) Register(controllers ...*Controller)

func (*Server) SetErrorHandler added in v0.2.5

func (server *Server) SetErrorHandler(action ErrorHandler)

func (*Server) Start

func (server *Server) Start() error

func (*Server) Stop added in v0.3.5

func (server *Server) Stop()

type Status

type Status struct {
	StatusCode int
	Entity     any
	Headers    http.Header
}

func BadRequest

func BadRequest(body any) Status

func CreateResponse

func CreateResponse(statusCode int, body any) Status

func Created

func Created(body any) Status

func Forbidden

func Forbidden(body any) Status

func Found

func Found(location string) Status

func InternalServerError

func InternalServerError(body any) Status

func MethodNotAllowed added in v0.2.2

func MethodNotAllowed(body any) Status

func MovedPermanently

func MovedPermanently(location string) Status

func NotFound

func NotFound(body any) Status

func Ok

func Ok(body any) Status

func PermanentRedirect

func PermanentRedirect(location string) Status

func Unauthorized

func Unauthorized(body any) Status

func (Status) WithHeaders

func (status Status) WithHeaders(headers http.Header) Status

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL