This article describes routing control for Gin.

In the previous article, Golang implemented a Web service directly using its own NET/HTTP, with routing capabilities. However, NET/HTTP is lacking in both functionality and performance. For example, in terms of function, nowadays many RESTful interfaces are defined in a way that uses different request methods to achieve different semantics: GET, POST, HEAD, DELETE, OPTION and so on. How to register a specific handler for the request method separately with NET/HTTP has also become a troublesome problem. RESTful apis, for example, rely heavily on request paths. Many parameters are put into the request URI, and the parameters that support these URIs are also a problem.

Implementation principle of Route

Let’s start by looking at how NET/ HTTP implements routing. In the last example

http.HandleFunc("/", handler)
Copy the code

This statement registers the function handler for path/handling through the http.HandleFunc function. Take a look at its internal implementation

func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
	DefaultServeMux.HandleFunc(pattern, handler)
}
Copy the code

You can see that the HandleFunc function of DefaultServeMux is called. DefaultServeMux:

var defaultServeMux ServeMux

type ServeMux struct {
	mu    sync.RWMutex
	m     map[string]muxEntry
	hosts bool // whether any patterns contain hostnames
}

// DefaultServeMux is the default ServeMux used by Serve.
var DefaultServeMux = &defaultServeMux
Copy the code

Mu is the read and write semaphore, and M is a map structure that stores the mapping between paths and handlers.

// HandleFunc registers the handler function for the given pattern.
func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
	if handler == nil {
		panic("http: nil handler")
	}
	mux.Handle(pattern, HandlerFunc(handler))
}

// Handle registers the handler for the given pattern.
// If a handler already exists for pattern, Handle panics.
func (mux *ServeMux) Handle(pattern string, handler Handler) {
	mux.mu.Lock()
	defer mux.mu.Unlock()

	if pattern == "" {
		panic("http: invalid pattern")}if handler == nil {
		panic("http: nil handler")}if _, exist := mux.m[pattern]; exist {
		panic("http: multiple registrations for " + pattern)
	}

	if mux.m == nil {
		mux.m = make(map[string]muxEntry)
	}
	mux.m[pattern] = muxEntry{h: handler, pattern: pattern}

	ifpattern[0] ! ='/' {
		mux.hosts = true}}Copy the code

If the handler is null, add the path and handler to servemux.m.

When the request is processed, the ServerHTTP function is actually called:

Func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) {handler := sh.srv. handler // This is true because we passed nilifHandler == nil {handler = DefaultServeMux} // Omitted some code handler.ServeHTTP(rw, req)}Copy the code

That is, when we start the Web service

http.ListenAndServe(": 8080", nil)
Copy the code

If the second handler is passed nil, then DefaultServeMux is enabled by default, and we’ve already added the route to DefaultServeMux’s map.

In other words, if we want to implement our own routing function, we just need to define the route in the default format and pass our custom route to the second parameter of ListenAndServe at startup.

httprouter

Httprouter is a readymade efficient route implementation, now many go Web frameworks use httprouter to support the route, the corresponding source address: github.com/julienschmi… . We’ll start with httprouter to rewrite the web service from the previous article.

package main

import (
	"fmt"
	"log"
	"net/http"

	"github.com/julienschmidt/httprouter"
)

func handler(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
	fmt.Fprintf(w, "Hi there")
}

func main() {
	router := httprouter.New()
	router.GET("/", handler)
	log.Fatal(http.ListenAndServe(": 8080", router))
}

Copy the code

We first generated a New routing service httprouter.new () and then added handling for GET requests to /. The router is passed to ListenAndServe as the second parameter during startup.

Also, with Httprouter, we can easily and efficiently support other operations such as POST and HEAD, as well as quickly implement the function of routing parameters. For details, see httprouter.

The next article mimics Gin’s implementation of prefix routing.