URL routing design of Go web development

Source: Internet
Author: User
Tags php web development reflection
Overview

Recently engaged in their own go web development framework, anyway, there is no intention to stash, so now take out the URL routing design this piece to write a blog. have done web development know, a good URL routing can make the user's browser address bar always have rules to follow, can let us develop the site easier to let search engine included, can let our developers more convenient MVC. When we use other web development frameworks, URL routing will certainly be a key feature of the framework or a promotional "selling point." So, the status of URL routing in a Web framework is still very important.

Back to go web development, how to use go to implement a URL routing function? How does the code write after implementation? Let's move on to a simple URL routing feature. How to use

Before we learn how to achieve this, we must first look at how to use it. It's really easy to use, because I've written a PHP Web development framework before, so our routing section is used like PHP (thinkphp). Let's take a look at the code.

Package main

import (
    "./app"
    "./controller"
)

func main () {
    app. static["/static"] = "./js"
    app. Autorouter (&controller. indexcontroller{})
    app. RunOn (": 8080")
}

Three lines of code, the role of the first line everyone should be clear, is to serve some static files (such as JS, CSS and other files), the second line of code is to register a controller, this line of code in PHP is not, after all, PHP is a dynamic language, a __ AutoLoad can complete the loading of classes, and go as a static language does not have this feature, so we still need to manually register (think about it, this can be like Java in the configuration file?) This function is left to add when optimizing later. and the last line of code didn't say, It's actually starting the server, where we're listening on port 8080.

The code above is very simple, let's see how the Indexcontroller wrote it.

Package Controller

Import (
    ".. /app "
  ". /funcs "
    " Html/template "
)

type indexcontroller struct {
    app. App
}

func (i *indexcontroller) Index () {
  i.data["name"] = "Qibin"
    i.data["email"] = " Qibin0506@gmail.com "
    //i.display ("./view/info.tpl ","./view/header.tpl ","./view/footer.tpl ")
    I. Displaywithfuncs (template. funcmap{"Look": Funcs. Lookup}, "./view/info.tpl", "./view/header.tpl", "./view/footer.tpl")
}

First we define a struct, which is an anonymous combination of the structure of the app (in terms of object-oriented words is inherited), but we give him a definition of the index method, which is exactly what we do not need to care about. How do you access it? Now run the code, in the browser input http://localhost:8080 or input http://localhost:8080/index/index can see what we output in the index method, specifically how to do, In fact, this is entirely the credit of URL routing, and here we begin to prepare to design such a URL routing feature. design of URL routing

The above autorouter looks amazing, what exactly did it do? Let's take a look at how this registered routing function is implemented.

package app import ("reflect" "strings") var mapping map[string]reflect. Type = Make (map[string]reflect. Type) Func Router (pattern string, t reflect. Type) {mapping[strings. ToLower (pattern)] = T} func Router (pattern string, app IApp) {REFV: = reflect. ValueOf (APP) Reft: = reflect. Indirect (REFV). Type () router (pattern, reft)} func autorouter (App IApp) {REFV: = reflect. ValueOf (APP) Reft: = reflect. Indirect (REFV). Type () RefName: = Strings. Trimsuffix (Strings. ToLower (Reft.name ()), "Controller") router (refName, reft)} 

First we define a map variable whose key is a string type, and we suspect that it must be a part of the URL that we entered in the browser, and then we get to the concrete to execute the structure. And what about his value? a reflect. What does the type do? Don't worry, let's take a look at the implementation code of Autorouter to understand. In Autorouter, first we use reflect. valueof to get the value of the struct we registered, and then we get the type, and finally we put the pair of string,type in the map. But the code here simply explains how to register in, and does not explain why to save the type Ah, here secretly tell you, in fact, for each visit, we find the corresponding controller is not also must not be directly called the structure of the method, Instead, a new instance is invoked by reflection. The specific code we'll talk about later.

So far, our route has been successfully registered, although we have some doubts about saving the type. Now let's start from the Runon function and slowly see how it is based on the routing registry to find the corresponding controller and its methods.

First look at the code of Runon.

Func RunOn (Port string) {
    server: = &http. server{
        Handler:newhandler (),
        Addr:    port,
    }

    log. Fatal (server. Listenandserve ())
}

This code is also very simple, for the familiar with the go web development students should be very familiar with the server handler we are through a newhandler function to return, this Newhandler do?

Func Newhandler () *handler {
    h: = &handler{}
    h.p.new = func () interface{} {
        return &context{}
    }< C4/>return h
}

First, a handler is constructed, and then a sync is given to the handler. Pool did the assignment, this thing is what, we will say in detail later, we come to feel relieved to see how this handler structure design.

Type handler struct {
    P sync. Pool
}

Very simple, for p above said, in the following we will say in detail, for handler we believe that it will certainly have a method called servehttp, to see it.

Func (H *handler) servehttp (w http. Responsewriter, R *http. Request) {
    if Servestatic (W, R) {
        return
    }

    ctx: = H.p.get (). ( *context)
    defer h.p.put (CTX)
    ctx. Config (W, R)

    controllername, MethodName: = H.findcontrollerinfo (R)
    Controllert, OK: = Mapping[controllername]
    if!ok {
        http. NotFound (W, R)
        return
    }

    REFV: = reflect. New (Controllert)
    Method: = Refv.methodbyname (methodName)
    if!method. IsValid () {
        http. NotFound (W, R)
        return
    }

    Controller: = Refv.interface (). ( IAPP)
    Controller. Init (CTX)
    method. Call (nil)
}

The code in this is actually the core code of our routing design, let's take a look at how this code is implemented in detail here. The first three lines of code are our support for static files.

And then we're going to use sync. Pool, first we take a context from the inside, and after this method has been executed to put this context in, what is the purpose of this? In fact, our site is not a single-line, so the servehttp here is not only for a user, and in our controller must also save Responsewriter and request information, So in order to prevent the request of the information will be rewritten by other requests, we choose to use the object pool here, when used to take out, run out after the use of the information to refresh each time before using, so as to avoid the need to request the information will be rewritten error. For Sync.pool, here's a quick explanation. And the top of it. Have we ever given him a new field assignment? The logic in this is that when we take it from this pool, if it doesn't, we'll create a new one with new, so there's a guarantee that the context is the only thing that we can get from the pool every time.

Continue to look at the code, and then we will be through the findcontrollerinfo from the URL to resolve the controller and the method we want to execute the name, go down, we have to create a new controller by reflection object, And through Methodbyname to get to the method to execute. Specific code:

REFV: = reflect. New (Controllert)
Method: = Refv.methodbyname (MethodName)

This explains why you should save reflect on top of it. Type. Finally we set the context to the controller and call the method we found. The general URL route is this, mainly through the go reflection mechanism to find the structure to be executed and the specific method to execute, and then call on it. However, one of the findcontrollerinfo we have yet to say is that it is relatively simple to implement by using the URL to find the controller and the name of the method we are going to execute. Take a look at the code:

Func (H *handler) Findcontrollerinfo (R *http. Request) (String, string) {
    path: = R.url. Path
    if strings. Hassuffix (Path, "/") {
        path = strings. Trimsuffix (Path, "/")
    }
    PathInfo: = Strings. Split (Path, "/")

    controllername: = Defcontroller
    If len (pathInfo) > 1 {
        controllername = pathinfo[1]
    }

    MethodName: = Defmethod
    If len (pathInfo) > 2 {
        methodName = strings. Title (Strings. ToLower (pathinfo[2])
    }

    return controllername, MethodName
}

Here first we get the URL of the PathInfo, for example, for request Http://localhost:8080/user/info, here we are going to get this user and info, but for http://localhost : 8080 or http://localhost:8080/user? We'll have the default, too.

Const (
    defcontroller = "index"
    defmethod     = "index"
)

By now, our URL route has basically been formed, but there are a few points we haven't shot yet, such as the apps and context we've seen so often. First, let's take a look at this context, what is the context? is actually our simple encapsulation of the request information.

Package App

import (
    "Net/http"
)

type IContext interface {
    Config (w http). Responsewriter, R *http. Request)
}

type Context struct {
    w http. Responsewriter
    R *http. Request
}

func (c *context) Config (w http. Responsewriter, R *http. Request) {
    C.W = w
    C.R = r
}

Here we simply encapsulate, just save the Responsewriter and request, every time we call the Config method to save the new Responsewriter and the request in.

And what about apps? The design is more flexible, in addition to a few of the methods used in the handler, the basic is "on-the-spot play."

Type IAPP Interface {
    Init (ctx *context)
    W () http. Responsewriter
    R () *http. Request
    Display (tpls. String)
    Displaywithfuncs (funcs template. Funcmap, Tpls ... string)
}

The method in this interface everyone should have guessed that the Init method we have used in the above Servehttp, and the W and R method is purely to facilitate the acquisition of Responsewriter and request, the following two display methods here is not much to say, is to encapsulate the template loading mechanism of go native. Let's see how the app implements this interface.

Type App struct {
    ctx  *context
    Data map[string]interface{}
}

func (a *app) Init (ctx *context) {
    a.ctx = ctx
    a.data = Make (map[string]interface{})
}

func (a *app) W () http. Responsewriter {
    return A.CTX.W
}

func (a *app) R () *http. Request {
    return A.CTX.R
}

func (a *app) Display (Tpls ... string) {
    If len (tpls) = = 0 {
        Return
  }

    Name: = FilePath. Base (tpls[0])

    t: = template. Must (template. Parsefiles (Tpls ...))
    T.executetemplate (A.W (), name, A.data)
}

func (a *app) Displaywithfuncs (funcs template. Funcmap, Tpls ... string) {
    If len (tpls) = = 0 {
        return
    }

    Name: = FilePath. Base (tpls[0])
    t: = template. Must (template. New (name). Funcs (Funcs). Parsefiles (Tpls ...))
    T.executetemplate (A.W (), name, A.data)
}

OK, the said above all said, and finally we have not seen is the static file support, here is also very simple.

var Static map[string]string = Make (map[string]string)

func servestatic (w http. Responsewriter, R *http. Request) bool {
    for prefix, static: = Range static {
        if strings. Hasprefix (R.url. Path, prefix) {
            file: = static + R.url. Path[len (prefix):]
            http. Servefile (W, R, file)
            return True
        }
    }

    return False
}

So far, one of our simple URL routes has been implemented, but our implementation is not perfect, such as custom routing rules are not supported, for PathInfo parameters we have not obtained, which can be completed in the completion stage. In the process of designing this route, we fully refer to some implementation methods of Beego. Reading and understanding someone's code is the right way to read the source when you encounter a problem.

Finally, let's end this article by running a screenshot.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.