ginregex

package module
v0.2.0 Latest Latest
Warning

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

Go to latest
Published: Sep 8, 2021 License: MIT Imports: 7 Imported by: 2

README

ginregex

This package implements an regular expression router for the gin http framework.

Usage

Install:

go get github.com/jxskiss/ginregex

Example:

r := gin.Default()

// Configure your normal non regular expression routes.
r.GET("/path_1", handler1)
r.POST("/path_2", handler2)

// Optionally you may configure your NoRoute handler, it won't affect the regex router.
r.NoRoute(noRouteHandler)

// Configure your regular expression routes.
regexRouter := ginregex.New(r, nil)
regexRouter.GET("^/.*$", regexHandler)

r.Run(":8080")

Or, you may use the Dispatch function for a small set of handlers:

r := gin.Default()
r.Any("/users/*any", ginregex.Dispatch(
	ginregex.NewMatcher("GET", `^/users/settings/$`, myMiddleware1, GetUserSettings),
	ginregex.NewMatcher("POST", `^/users/settings/$`, myMiddleware1, ModifyUserSettings),
	ginregex.NewMatcher("GET", `^/users/(?P<user_id>\d+)/$`, myMiddleware2, GetUserDetail),
))

Some notes:

  1. the RegexRouter is compatible with the NoRoute handler of gin.Engine, so you can optionally configure it anywhere as you want;

  2. the order of registering normal no-regex routes and regex routes does not matter;

  3. but registered regular expressions must be unique, duplicate regular expressions will cause panic;

  4. middleware added to the gin.Engine or the RegexRouter will be combined into the final regex handler;

  5. named capturing will be filled into gin.Context.Params, you can access them like normal gin path parameters in handler or binding functions;

  6. optionally a hook function (func(c *gin.Context, regexPattern string)) can be provided when building the RegexRouter, it will be called right after the request path being matched by a regular expression; you may use this feature to integrate with other gin middleware;

Change History

v0.2.0
  1. Update go mod version to go 1.15.
  2. Update gin version to 1.7.4, users which are still using gin 1.4 should not upgrade.
v0.1.1

New Dispatch function to dispatch requests to multiple different handlers under same path without using unsafe tricks.

v0.1.0

Initial public release, the code is used in production and considered stable.

Why

gin is an HTTP framework that claims high performance. Its main performance advantage comes from the httprouter that utilize trie and does not support regular expression. So why bother to build a regular expression router?

It starts with a fairly old story.

In the process of maintaining a legacy Python service that has passed several teams in succession, after many optimizations to the old service, the performance of the program and the maintainability of the project are still a headache. Well, everyone knows the pain of maintaining a long-lived and full of history debt project written in a dynamically typed language.

And over time, the company's technology stack has gradually shifted to the Go language. In order to solve the annoying historical problems, it is an obvious solution to rebuild the project in Go language gradually. But reconstruction is not a work we can accomplish at one stroke. Instead we must keep the service runs smoothly and be compatible with many old version clients that have been released to end-users.

First we build a gateway service using Go to take over all ingress requests, then we refactor the interfaces step by step. For the interfaces that has not been migrated, we reverse proxy the requests to the old service, here come the problems:

  1. There are hundreds of path routes, configure them one by one?

  2. Python (Flask) supports very flexible routing configuration, for example:

    • /users/settings/ to query and modify the currently logged in user settings
    • /users/some_biz_data/ to query some business data of the currently logged in user
    • /users/<int:user_id>/ to query the information of the specified user by identifier

    This routing configuration is not supported in gin (httprouter).

Therefore, we have no choice other than regular expression routing. I did not find a suitable open source implementation, so I decided to build one by myself.

Documentation

Overview

Package ginregex implements a regular expression router for the gin http framework.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Dispatch added in v0.1.1

func Dispatch(matchers ...*Matcher) gin.HandlerFunc

Dispatch can be used to dispatch requests to multiple different handlers under same path, using the given matchers. The matchers given order matters, if a path matches multiple matchers, only the first one (in the order they present) takes effect.

Compared to the `RegexRouter` in this package, `Dispatch` does not use unsafe tricks to hack the framework at runtime.

Usage example:

r := gin.Default()
r.Any("/users/*any", ginregex.Dispatch(
	ginregex.NewMatcher("GET", `^/users/settings/$`, myMiddleware1, GetUserSettings),
	ginregex.NewMatcher("POST", `^/users/settings/$`, myMiddleware1, ModifyUserSettings),
	ginregex.NewMatcher("GET", `^/users/(?P<user_id>\d+)/$`, myMiddleware2, GetUserDetail),
))

Types

type Hook

type Hook func(c *gin.Context, regexPattern string)

type Matcher added in v0.1.1

type Matcher struct {
	Method   string
	RE       *regexp.Regexp
	Handlers gin.HandlersChain
}

func NewMatcher added in v0.1.1

func NewMatcher(method, re string, handlers ...gin.HandlerFunc) *Matcher

NewMatcher returns a matcher to be used with the `Dispatch` function to dispatch requests using regular expressions. Named capturing will be filled into `gin.Context.Params`, you can access them like normal gin path parameters in your handler or binding functions.

The given re string must be a valid regular expression, else it will panic.

func (*Matcher) Handle added in v0.1.1

func (m *Matcher) Handle(c *gin.Context)

type RegexRouter

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

RegexRouter is a regular expression router to be used with gin http framework. It uses unsafe magic to patch gin.Engine and gin.Context on the fly.

If named capturing with the (?P<name>...) syntax presents in registered routes, the captured values will be filled into gin.Context.Params, so you can access them like normal gin path parameters in your handlers or binding functions.

func New

func New(engine *gin.Engine, hook Hook) *RegexRouter

New creates a regular expression router with the given gin.Engine.

To function properly, only one RegexRouter can be created for each gin.Engine pointer, if called multiple times using same gin.Engine pointer, it returns the RegexRouter previously created.

func (*RegexRouter) Any

func (r *RegexRouter) Any(regexPath string, handlers ...gin.HandlerFunc) *RegexRouter

Any registers a route that matches all the HTTP methods. GET, POST, PUT, PATCH, HEAD, OPTIONS, DELETE, CONNECT, TRACE.

func (*RegexRouter) DELETE

func (r *RegexRouter) DELETE(regexPath string, handlers ...gin.HandlerFunc) *RegexRouter

DELETE is a shortcut for router.Handle("DELETE", path, handle).

func (*RegexRouter) GET

func (r *RegexRouter) GET(regexPath string, handlers ...gin.HandlerFunc) *RegexRouter

GET is a shortcut for router.Handle("GET", path, handle).

func (*RegexRouter) HEAD

func (r *RegexRouter) HEAD(regexPath string, handlers ...gin.HandlerFunc) *RegexRouter

HEAD is a shortcut for router.Handle("HEAD", path, handle).

func (*RegexRouter) Handle

func (r *RegexRouter) Handle(httpMethod, regexPattern string, handlers ...gin.HandlerFunc) *RegexRouter

Handle registers a new request handle and middleware with the given path and method. The last handler should be the real handler, the other ones should be middleware that can and should be shared among different routes. See the example code in GitHub.

For GET, POST, PUT, PATCH and DELETE requests the respective shortcut functions can be used.

This function is intended for bulk loading and to allow the usage of less frequently used, non-standardized or custom methods (e.g. for internal communication with a proxy).

If the regex pattern does not start with "^", "^" will be added to the start.

func (*RegexRouter) OPTIONS

func (r *RegexRouter) OPTIONS(regexPath string, handlers ...gin.HandlerFunc) *RegexRouter

OPTIONS is a shortcut for router.Handle("OPTIONS", path, handle).

func (*RegexRouter) PATCH

func (r *RegexRouter) PATCH(regexPath string, handlers ...gin.HandlerFunc) *RegexRouter

PATCH is a shortcut for router.Handle("PATCH", path, handle).

func (*RegexRouter) POST

func (r *RegexRouter) POST(regexPath string, handlers ...gin.HandlerFunc) *RegexRouter

POST is a shortcut for router.Handle("POST", path, handle).

func (*RegexRouter) PUT

func (r *RegexRouter) PUT(regexPath string, handlers ...gin.HandlerFunc) *RegexRouter

PUT is a shortcut for router.Handle("PUT", path, handle).

func (*RegexRouter) Use

func (r *RegexRouter) Use(middleware ...gin.HandlerFunc) *RegexRouter

Use adds middleware to the router.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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