Documentation

Overview

A MVC binder for free-style of function handler with *gin.Context

Abstract

There are may tedious processes for coding on web service:

1. Type conversion from HTTP query parameter to desired type in GoLang.
2. Binding body of HTTP POST to JSON object in GoLang.
	2.1. Perform post-process(e.x. trim text) of binding data
	2.2. Perform data validation of binding data
3. Convert the result data to JSON response.

Gin has provided foundation features on simple and versatile web application, this framework try to enhance the building of web application on instinct way.

MVC Handler

A MVC handler can be any function with ruled types of parameter and defined returned types.

type MvcHandler interface{}

You can define handler of supported:

func(req *http.Request, params *gin.Params) OutputBody {
	/* ... */
	return TextOutputBody("Hello World")
}

func(
	data *struct {
		Name string `mvc:"query[name]"`
		Age int `mvc:"query[age]"`
		SessionId string `mvc:"header[session_id"`
	}
) OutputBody {
	/* ... */
	return TextOutputBody("Hello World")
}

Build Gin HandlerFunc

After you define the MVC handler, you could use "MvcBuilder.BuildHandler()" to convert your handler to "gin.HandlerFunc".

mvcBuilder := NewMvcBuilder(NewDefaultMvcConfig())
engine.Get("/your-web-service", mvcBuilder.BuildHandler(your_mvc_handler))

Parameters of Handler

Supported types:

"ContextBinder" - Feeds the context to implementation of Bind(*gin.Context) function.

This type of value woule be checked by ogin.ConformAndValidateStruct automatically.

'json.Unmarshaler' - If the type of value is json.Unmarshaler, use the UnmarshalJSON([]byte) function of the value

This type of value woule be checked by ogin.ConformAndValidateStruct automatically.

"<struct>" - See parameter tags for automatic binding

This type of value woule be checked by ogin.ConformAndValidateStruct automatically.

"*gin.Context" - The context object of current request

"gin.ResponseWriter" - See gin.ResponseWriter

"gin.Params" - See gin.Params

"*http.Request" - See http.Request

"http.ResponseWriter" - See http.ResponseWriter

"*url.URL" - See url.URL

"*multipart.Reader" - See multipart.Reader; Once you use *multipart.Form, the reader would reach EOF.

"*multipart.Form" - See multipart.Form

"*validator.Validate" - See go-playground/validator.v9

"types.ConversionService" - See ConversionService

Return value of Handler

"OutputBody" is the main definition for output of web service, it has build-in functions for certain types of output:

"JsonOutputBody()" - Uses gin.Context.JSON function to perform output
"TextOutputBody()" - Uses gin.Context.String function to generate output body(by fmt.Sprintf("%v"))
"HtmlOutputBody()," XmlOutputBody(), YamlOutpuBody() - Calls function of gin.Context, respectively.

"json.Marshaler" - If the type of returned value is json.Marshaler, use JsonOutputBody() as output type

"string" - If the type of returned value is string, use TextOutputBody() as output type

"fmt.Stringer" - As same as string

"*model.Paging" - Output the content of paging to HTTP header

Tagging Struct

There are various definition of tags could be used on struct:

type MyData struct {
	Name string `mvc:"query[name] default[NO-NAME]"`
	H1 string `mvc:"header[h1]"`
	FormV1 int `mvc:"form[v1]"`
	FormV3 []string `mvc:"form[v2]"` // As slice
	File1 multipart.File `mvc:"file[f1]"`
	File2 multipart.File `mvc:"file[f2]"`
}

Default Value

mvc:"query[param_name_1] default[20]" - Gives value 20 if the value of binding is empty
mvc:"query[param_name_1] default[20,40,30]" - Gives value [20, 40, 30](as array, no space)if the value of binding is empty

Parameters, Heaer, Cookie, and From

mvc:"query[param_name_1]" - Use query parameter param_name_1 as binding value
mvc:"query[?param_name_1]" - Must be bool type, used to indicate whether or not has viable value for this parameter
mvc:"cookie[ck_1]" - Use the value of cookie ck_1 as binding value
mvc:"cookie[?ck_2]" - Must be bool type, used to indicate whether or not has viable value for this parameter
mvc:"param[pm_1]" - Use the value of URI parameter pm_1 as binding value
mvc:"form[in_1]" - Use the form value of in_1 as binding value
mvc:"form[?in_2]" - Must be bool type, used to indicate whether or not has viable value for this parameter
mvc:"header[Content-Type]" - Use the header value of Content-Type as binding value
mvc:"header[?pg_id]" - Must be bool type, used to indicate whether or not has viable value for this parameter
mvc:"key[key-1]" - Use the key value of key-1 as binding value
mvc:"key[?key-3]" - Must be bool type, used to indicate whether or not has viable value for this parameter

By default, if the value of binding is existing, the framework would use the default value of binding type.

HTTP

mvc:"req[ClientIp]" - The IP of client, the type of value could be string or ​net.IP
mvc:"req[ContentType]" - The content type of request, must be string
mvc:"req[Referer]" - The "Referer" of request, must be string
mvc:"req[UserAgent]" - The "User-Agent" of request, must be string
mvc:"req[Method]" - The method of request, must be string
mvc:"req[Url]" - The url of reqeust, must be string or ​url.URL
mvc:"req[Proto]" - The protocol version for incoming server requests, must be string
mvc:"req[ProtoMajor]" - The protocol version for incoming server requests, must be int
mvc:"req[ProtoMinor]" - The protocol version for incoming server requests, must be int
mvc:"req[ContentLength]" - The ContentLength? records the length of the associated content, must be int64
mvc:"req[Host]" - For server requests Host specifies the host on which the URL is sought, must be string
mvc:"req[RemoteAddr]" - RemoteAddr allows HTTP servers and other software to record the network address that
	sent the request, usually for logging, must be string
mvc:"req[RequestURI]" - RequestURI is the unmodified Request-URI of the Request-Line (RFC 2616, Section 5.1) as
	sent by the client to a server, must be string

PAGING

Must be type of "*model.Paging"

mvc:"pageSize[50]" - The default value of page size is 50
mvc:"pageOrderBy[name:age]" - The default value of "orderBy" property of paging object is 'name:age'

SECURITY

mvc:"basicAuth[username]" - The username of BasicAuth?, See RFC-2617
mvc:"basicAuth[password]" - The password of BasicAuth?, See RFC-2617

FILE UPLOAD

mvc:"file[f1]" - The file of request by key value, must be "multipart.File"(or "[]multipart.File")
    You don't have to close this resource, Gin MVC would do your favour.
mvc:"fileHeader[f1]" - The file header of request by key value, must be "*multipart.FileHeader"(or "[]*multipart.FileHeader")

Data Conversion

This framework depends on ConversionService to perform type conversion

Data Validation

For struct type of input parameter, this framework would use "MvcConfig.Validator" and "common/conform" to perform post-process on the parameter and validate it.

See go-playground/validator: https://godoc.org/gopkg.in/go-playground/validator.v9

See leebenson/conform: https://github.com/leebenson/conform

Index

Examples

Constants

This section is empty.

Variables

View Source
var NotFoundOutputBody = OutputBodyFunc(func(c *gin.Context) {
	ogin.JsonNoRouteHandler(c)
})

Functions

This section is empty.

Types

type ContextBinder

type ContextBinder interface {
	Bind(*gin.Context)
}

Main interface for binding context

type ContextBinderFunc

type ContextBinderFunc func(*gin.Context)

Function type of "ContextBuilder"

func (ContextBinderFunc) Bind

func (f ContextBinderFunc) Bind(context *gin.Context)

type MvcBuilder

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

Builder object with internal objects

func NewMvcBuilder

func NewMvcBuilder(newConfig *MvcConfig) *MvcBuilder

Constructs a new builder with configuration

func (*MvcBuilder) BuildHandler

func (b *MvcBuilder) BuildHandler(handlerFunc MvcHandler) gin.HandlerFunc
Example (HttpGet)
mvcBuilder := NewMvcBuilder(NewDefaultMvcConfig())

gin.SetMode(gin.ReleaseMode)
engine := gin.New()
engine.GET(
	"/get-1",
	mvcBuilder.BuildHandler(
		func(
			data *struct {
				V1 int8  `mvc:"query[v1]"`
				V2 int32 `mvc:"query[v2]"`
			},
		) string {
			return fmt.Sprintf("V1: %d. V2: %d", data.V1, data.V2)
		},
	),
)

req := httptest.NewRequest(http.MethodGet, "/get-1?v1=20&v2=40", nil)
resp := httptest.NewRecorder()
engine.ServeHTTP(resp, req)

fmt.Println(resp.Body.String())
Output:

V1: 20. V2: 40
Example (HttpPost)
mvcBuilder := NewMvcBuilder(NewDefaultMvcConfig())

gin.SetMode(gin.ReleaseMode)
engine := gin.New()
engine.POST(
	"/post-1",
	mvcBuilder.BuildHandler(
		func(
			data *struct {
				V1 int8    `mvc:"form[v1]"`
				V2 []int32 `mvc:"form[v2]"`
			},
		) string {
			return fmt.Sprintf("v1: %d. v2: %d,%d", data.V1, data.V2[0], data.V2[1])
		},
	),
)

/**
 * Form data
 */*/
form := url.Values{
	"v1": []string{"17"},
	"v2": []string{"230", "232"},
}
// :~)

req := httptest.NewRequest(http.MethodPost, "/post-1", strings.NewReader(form.Encode()))
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
resp := httptest.NewRecorder()
engine.ServeHTTP(resp, req)

fmt.Println(resp.Body.String())
Output:

v1: 17. v2: 230,232
Example (Json)
/*
	type sampleCar struct {
		Name string `json:"name"`
		Age int `json:"age"`
	}
	func (car *sampleCar) Bind(c *gin.Context) {
		ogin.BindJson(c, car)
	}
*/
	}
*/

mvcBuilder := NewMvcBuilder(NewDefaultMvcConfig())

gin.SetMode(gin.ReleaseMode)
engine := gin.New()
engine.POST(
	"/json-1",
	mvcBuilder.BuildHandler(
		func(car *sampleCar) OutputBody {
			return JsonOutputBody(car)
		},
	),
)

rawJson, _ := json.Marshal(&sampleCar{"GTA-99", 3})

req := httptest.NewRequest(http.MethodPost, "/json-1", bytes.NewReader(rawJson))
req.Header.Set("Content-Type", "application/json")
resp := httptest.NewRecorder()
engine.ServeHTTP(resp, req)

fmt.Println(resp.Body.String())
Output:

{"name":"GTA-99","age":3}
Example (Paging)
mvcBuilder := NewMvcBuilder(NewDefaultMvcConfig())

gin.SetMode(gin.ReleaseMode)
engine := gin.New()
engine.GET(
	"/paging-1",
	mvcBuilder.BuildHandler(
		func(
			p *struct {
				// Loads paging from header
				Paging *model.Paging
			},
		) (*model.Paging, string) {
			p.Paging.TotalCount = 980

			// Output paging in header
			return p.Paging, fmt.Sprintf("Position: %d", p.Paging.Position)
		},
	),
)

req := httptest.NewRequest(http.MethodGet, "/paging-1", nil)
// Ask for page of 4th
req.Header.Set("page-pos", "4")
resp := httptest.NewRecorder()
engine.ServeHTTP(resp, req)

fmt.Println(resp.Body.String())
fmt.Printf("total-count: %s", resp.Header().Get("total-count"))
Output:

Position: 4
total-count: 980

type MvcConfig

type MvcConfig struct {
	// ConversionServer used to perform type conversion
	ConvertService ot.ConversionService
	// Validator object used to perform data validations
	Validator *validator.Validate
}

Defines configuration of MVC framework

func NewDefaultMvcConfig

func NewDefaultMvcConfig() *MvcConfig

Constructs default configuration of MVC framework

type MvcHandler

type MvcHandler interface{}

Could be any type of function

type OutputBody

type OutputBody interface {
	Output(*gin.Context)
}

Main interface for generating response

func HtmlOutputBody

func HtmlOutputBody(name string, v interface{}) OutputBody

Uses "(*gin.Context).HTML(http.StatusOK, name, v)" to perform response

func HtmlOutputBody2

func HtmlOutputBody2(code int, name string, v interface{}) OutputBody

Uses "(*gin.Context).HTML(code, name, v)" to perform response

func HtmlOutputOrNotFound

func HtmlOutputOrNotFound(name string, v interface{}) OutputBody

Output the value or not found error if the value is not viable

func JsonOutputBody

func JsonOutputBody(v interface{}) OutputBody

Uses "(*gin.Context).JSON(http.StatusOK, v)" to perform response

func JsonOutputBody2

func JsonOutputBody2(code int, v interface{}) OutputBody

Uses "(*gin.Context).JSON(code, v)" to perform response

func JsonOutputOrNotFound

func JsonOutputOrNotFound(v interface{}) OutputBody

Output the value or not found error if the value is not viable

func TextOutputBody

func TextOutputBody(v interface{}) OutputBody

Uses "(*gin.Context).String(http.StatusOK, v)" to perform response

func TextOutputBody2

func TextOutputBody2(code int, v interface{}) OutputBody

Uses "(*gin.Context).String(code, v)" to perform response

func TextOutputOrNotFound

func TextOutputOrNotFound(v interface{}) OutputBody

Output the value or not found error if the value is not viable

func XmlOutputBody

func XmlOutputBody(v interface{}) OutputBody

Uses "(*gin.Context).XML(http.StatusOK, v)" to perform response

func XmlOutputBody2

func XmlOutputBody2(code int, v interface{}) OutputBody

Uses "(*gin.Context).XML(code, v)" to perform response

func XmlOutputOrNotFound

func XmlOutputOrNotFound(v interface{}) OutputBody

Output the value or not found error if the value is not viable

func YamlOutputBody

func YamlOutputBody(v interface{}) OutputBody

Uses "(*gin.Context).YAML(http.StatusOK, v)" to perform response

func YamlOutputBody2

func YamlOutputBody2(code int, v interface{}) OutputBody

Uses "(*gin.Context).YAML(code, v)" to perform response

func YamlOutputOrNotFound

func YamlOutputOrNotFound(v interface{}) OutputBody

Output the value or not found error if the value is not viable

type OutputBodyFunc

type OutputBodyFunc func(*gin.Context)

Function version of "OutputBody"

func (OutputBodyFunc) Output

func (f OutputBodyFunc) Output(context *gin.Context)

As implementation of "OutputBody"