URL routing design of Go web development

Source: Internet
Author: User
Tags php web development
This is a creation in Article, where the information may have evolved or changed.

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 mainimport (    "./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, one __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 be added later when optimization. and the last line of code doesn't say, it's actually starting the server. Here we are listening on port 8080.

The code above is simple, let's take a look at how that is IndexController written.

 PackageControllerImport(".. /app "  ".. /funcs "    "Html/template")typeIndexcontrollerstruct{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 struct App (in object-oriented terms, which is inherited), but we define a method for him, and Index we don't have to care about what we do. How do we get to the access? Now run the code, in the browser input http:/ /localhost:8080 or input http://localhost:8080/index/index can see the content we Index output in the method, specifically how to do, in fact, this is completely the credit of the URL route, Let's start by preparing to design such a URL routing feature.

Design of URL Routing

What AutoRouter does it look like? Let's take a look at how this is done with the ability to register a route.

 PackageAppImport("Reflect"    "Strings")varMappingMap[string]reflect. Type = Make(Map[string]reflect. Type)funcRouter (patternstring, t reflect. Type) {mapping[strings. ToLower (pattern)] = t}funcRouter (Patternstring, App IApp) {REFV: = reflect. ValueOf (APP) Reft: = reflect. Indirect (REFV). Type () router (pattern, reft)}funcAutorouter (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, and his key is a string type, and we suspect that it is certain part of the URL that we entered in the browser, and then we pass it to get to the concrete to execute a struct. What about his value? reflect.Type , we see AutoRouter the implementation of the code is clear. In the AutoRouter first we used reflect.ValueOf to get to the structure we registered, and Value then we acquired it Type , and finally we put this pair of string,type to map. But the code here is just to explain how to register in, and do not explain why to save Type Ah, here secretly tell you, in fact, for each visit, we find that the corresponding is Controller not also must not be directly called the structure of the method, but by reflection to create a new instance to invoke. The specific code we'll talk about later.

So far, our route has been successfully registered, although we have some doubts about the preservation. Let's start with the Type RunOn function to see how it is based on the routing registry to find the corresponding Controller and its methods.

First look at RunOn the code.

funcstring) {    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 Server , Handler we are through a newHandler function to return, this newHandler did what?

func newHandler() *handler {    h := &handler{}    funcinterface{} {        return &Context{}    }    return h}

First constructs a handler , then gives the handler in the sync.Pool assignment, this thing is why, we later will say in detail, below we come to feel relieved to see handler how this structure design.

typestruct {    p sync.Pool}

Very simple, for the p above said, in the following we will say in detail, for handler we believe it must 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.

Then we use it sync.Pool , first we take one from the inside, Context and after this method is executed Context , put this in, what is the purpose of this? In fact, our site is not a single-line, so here is ServeHTTP not only for a user, and in our Controlleralso must be saved ResponseWriter and Request other 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, after the use of the time to go in, before each use of the information to refresh, This avoids the error of not having to ask for the information to be rewritten. For sync.Pool A brief explanation here, is there a field assignment that we have given him? The New logic in this is that when we take it from this, pool if it doesn't, it will be used to New create a new one, So here we can guarantee the Context only premise, but also to ensure that every time we pool get from the always get.

Continue to look at the code, and then we are going through the findControllerInfo URL to parse out the name we want to execute, controller method go down, we create a new object by reflection controller , and through to MethodByName get to execute the method. Specific code:

refV := reflect.New(controllerT)method := refV.MethodByName(methodName)

This explains why the above is saved reflect.Type . Finally, we'll Context set this up and Controller 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, Then the call is ready. However, one of the other things that we haven't said is that findControllerInfo the implementation is relatively simple, that is, the URL to find controller and the name of the method we want to execute. Take a look at the code:

func(H *handler) Findcontrollerinfo (R *http. Request) (string,string{path: = R.url. PathifStrings. Hassuffix (Path,"/") {Path = strings. Trimsuffix (Path,"/")} PathInfo: = Strings. Split (Path,"/") Controllername: = Defcontrollerif Len(PathInfo) >1{controllername = PathInfo[1]} methodName: = Defmethodif Len(PathInfo) >2{MethodName = strings. Title (Strings. ToLower (PathInfo[2]))    }returnControllername, MethodName}

Here first we get the URL 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 (    "index"    defMethod     "Index")

To the current location, our URL route has basically been formed, but there are a few points we have not shot, such as the above often seen App and Context . First, let's take a look at this Context , what's this? In fact, Context we have a simple encapsulation of the request information.

package appimport (    "net/http")typeinterface {    Config(w http.ResponseWriter, r *http.Request)}typestruct {    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 ResponseWriter and Request , each time we request the Config method will be called to the new ResponseWriter and Request saved in.

And what about apps? Design is more flexible, in addition to several in handler -use methods, basically are "on-the-spot play."

typeinterface {    Init(ctx *Context)    W() http.ResponseWriter    R() *http.Request    Display(tpls ...string)    DisplayWithFuncs(funcs template.FuncMap, tpls ...string)}

The method in this interface should be guessed, the Init method we have used in the above ServeHTTP , and W R the method is purely for ResponseWriter the convenience of access and, the Request following two Display methods here is not much to say, is to encapsulate the template loading mechanism of go native. Let's see App how this interface is implemented.

typeAppstruct{CTX *context DataMap[string]Interface{}}func(A *app) Init (ctx *context) {a.ctx = CTX A.data = Make(Map[string]Interface{})}func(A *app) W () http. Responsewriter {returnA.CTX.W}func(A *app) R () *http. Request {returnA.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.

varStaticMap[string]string= Make(Map[string]string)funcServestatic (w http. Responsewriter, R *http. Request)BOOL{ forprefix, Static: =RangeStatic {ifStrings. 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 with a single run.

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.