Build a tutorial of web application using Go language

  • 2020-05-30 20:23:23
  • OfStack

1.Abstract

In the process of learning web development, I encountered many difficulties, so I wrote a review article. As a roadmap, it introduces the learning process of golang development and the Example code starting from index, the development element of web.

In the description, we usually use the code to describe the usage method without too much explanation. Finally, you can implement your own requirements with the convenient copy code.

Objects of this paper:

Someone with 1 level of experience in web development People who can use ajax flexibly (at least know how to separate before and after) I have a general understanding of golang web development. I have skimmed at least some books developed by golang web

What you'll gain from reading this article:

1 tips developed by golang web golang web developed some practical API

This article has written the explanation in code comments for the purpose of explaining it in as much detail as possible.
2.golang web development check list

Skipped parts: basic process control, OOP and other basic grammar knowledge.

3. The router

The router is the external soul of the entire site, and URL can be very disgusting if the routing is not done well. So this part is designed to be the first thing to say.

There are two types of routing. One is manual routing. For the purpose of scheduling fixed functions through tul, the other one is resource acquisition.

Automatic routing, mainly using OOP COMMAND mode to achieve. All functions use post, unified 1 entrance, convenient permission management, security management, cross-domain management. But let the framework do the heavy lifting. There is no reference for beginners here.

3.1 manual routing

package main
    import (
      "log"
      "net/http"
      )
      func main() {
        RouterBinding() // Routing binding function
        err := http.ListenAndServe(":9090", nil) // Set the listening port
        if err != nil {
          log.Fatal("ListenAndServe: ", err)
        }
      }

Bind the route before httpserver runs
3.2 bindings for manual routing
3.2.1 static files

 http.Handle("/pages/", http.StripPrefix("/pages/", http.FileServer(http.Dir("./pages")))) 

3.2.2 fixed functions and resource acquisition

They're all one

http.HandleFunc("/images/", fileUpload.DownloadPictureAction) 

4. Page loading
4.1 pure static page (HTML)

Just give it to the route. You automatically access that folder. However, the production environment must be cdn, if you have more servers. You can use nginx reverse proxy.

The main benefits of separation before and after, can on CDN is more communication times. But it's all back to ok through optimization and all that stuff.

4.2 loading of template pages

commonPage, err := template.ParseFiles("pages/common/head.gtpl", // To load a template 
    "pages/common/navbar.gtpl", "pages/common/tail.gtpl")
    if err != nil {
      panic(err.Error())
    }
    navArgs := map[string]string{"Home": "home", "User": "yupengfei"}// Complex parameters start being pushed in
    knowledgePage, err := template.ParseFiles("pages/knowledge/knowledge.gtpl")
    knowledgeArgs := map[string]interface{}{"Head": "This is a test title",
    "Author": "kun.wang", "PublishDatetime": "2014-09-14",
    "Content": template.HTML("<p style=\"text-indent: 2em\"> Why use semantics? </p>")}// Not bad, just doing string analysis will affect the engineering efficiency
    commonPage.ExecuteTemplate(w, "header", nil)// render start
    commonPage.ExecuteTemplate(w, "navbar", navArgs)
    knowledgePage.ExecuteTemplate(w, "knowledge", knowledgeArgs)
    commonPage.ExecuteTemplate(w, "tail", nil)

Provide only critical code.

Everything else is fine, except for the fact that the page is rendered on a server that is a little too expensive.
It is difficult to error an array of strings as input parameters
Conclusion: although the communication times are reduced, there is no way to get on CDN egg ache, in addition, mapping egg ache of template.

5. Presentation layer scripts

Presentation layer scripting is difficult and not easy to learn. But once it's done, there's a significant increase in code reuse.

In general, the development efficiency of JS is very high and the flexibility is high. Moreover, cpu is a client with good performance, many free resources and many learners, so it is easy to recruit.

5.1 require.js
5.1.1 load

<script data-main="/reqmod/login_main" language="JavaScript" defer async="true" src="js/r.js"></script> 

1 loading script entry for the entire page (only 1 js per page is preferred)

benefits

js is lazy loading. There will be no dead page
Maximize the use of the cache. HTTP (304).
Only 1 js per page
dom event binding, no need to write js binding on html control

The bad

It's hard to learn
Website updates always have a cache of browsers that have not been updated. Error (so in some cases the customer knows how many times to refresh, which has become a habit for the user)

Parameter interpretation

'data-main' business logic entry, load the current string.js this file
` language ` don't explain
'defer async' literally
'src' r. js means' require. js '. Code is everywhere.

5.1.2 page Business

Load dependent file

require.baseUrl = "/"
    require.config({
      baseUrl: require.baseUrl,
      paths: {
        "jquery": "js/jquery-1.10.2.min",
        "domready" : "reqmod/domReady",
        "pm" : "reqmod/pmodal",
        "cookie":"reqmod/cookie",
        "user":"reqmod/user",
        "bootstrap": "reqmod/bootstrap.min",
        "nav":"reqmod/nav"
      },
      shim: {
        'bootstrap': {
          deps: ['jquery']
        }
      }
    });
    // directly copy All done.

Execute page business

The most that dom does in execution is to bind to events. Load the various js library direct references.

The code is beautiful, the development efficiency, the execution efficiency is very good.

require(['nav','domready', 'jquery', 'user','pm'], function (nav,doc, $, user,pm){
      // The first of this function 1 a ` An array of ` The parameter is the selected dependent module. 1. The absolute path of the site. 2. Select when using load dependent modules export The content of the
      // The order of the array should follow function The order 1 So, if you have two module dependencies for example jquery Plugins, write the end without variables, direct use `$`
      doc(function () { // domReady
        pm.load();// Loading various plug-ins HTML Templates and all that ok
        $('#btn_login')[0].onclick = function(){user.login();}//button event
      });
    });

Page MODEL

define(['jquery','reqmod/cookie','user','bootstrap'],function ($,cookie,user){
        //define The parameter content of the function require is 1 Kind of.
        // The dependent module here is in the module that calls this module path Configuration. Otherwise will die very miserably, when the error will not say what is missing what is wrong.
      var nav_load = function () { // Notice how the function is defined copy just
        $.get('/nav.html', function(result){
          var newNode = document.createElement("div");
          newNode.innerHTML = result;
          $('body')[0].insertBefore(newNode,$('body')[0].firstChild);
          //document.body.innerHTML = result + document.body.innerHTML;
          $('#btn_login')[0].onclick = function(){user.login();}
          $('#btn_reg')[0].onclick = function(){window.location='/register.html'}
          $.post('/login_check',{},function(data){
            if(data==0){
              Form_login.style.display=""
            }
            else{
              form_userInfo.style.display=""
            }
          })
        });
      }
      return {// This is similar to microrouting. Very flexible, very convenient
        load :nav_load
      };
    });

5.2 JQuery

JQ functions as long as require.js is basically the same after it has been referenced.

If necessary, you can go to w3school to learn 1.

6. The business layer

Post analysis

func XXXAction(w http.ResponseWriter, r *http.Request) {
      r.parseForm() // You have to have this to get the parameter
      r.Form["Email"] // To obtain Email Parameters ( String )
      // Write the next business.
    }

Resource entry function resource require analysis (url analysis fixed writing method)

func Foo(w http.ResponseWriter, r *http.Request) {
      queryFile := strings.Split(r.URL.Path, "/")
      queryResource := queryFile[len(queryFile)-1] // Parse the file
    }
    // Once the string segmentation is complete, it is ok to get the resource as required.

Enter object directly

data, err := ioutil.ReadAll(r.Body) // Directly read form for  json  string 
     if err != nil {
      utility.SimpleFeedBack(w, 10, "failed to read body")
      pillarsLog.PillarsLogger.Print("failed to read body")
      return
     }
     k := 【 BUSINESS OBJECT 】
     err = json.Unmarshal(data, &k)
     if err != nil {
      utility.SimpleFeedBack(w, 13, "Pramaters failed!")
      pillarsLog.PillarsLogger.Print("Pramaters failed!")
      return
     }
    // Convenient and quick. When you access the parameters again, you simply call the struct parameters.
    // Pay attention to ajax You need to do it when you call a function 1 The adjustment codes are as follows:
    $.ajax([dist],JSON.stringify([data]),function(){},'json');// Pay attention to JSON

7. The persistence layer
7.1 Mysql

In fact, no matter what language Mysql driver comes from PRO*C, so after PRO*\C, everything is ok

Insert/Delete/Update

stmt, err := mysqlUtility.DBConn.Prepare("INSERT INTO credit (credit_code, user_code, credit_rank) VALUES (?, ?, ?)")
    if err != nil {
      pillarsLog.PillarsLogger.Print(err.Error())
      return false, err
    }
    defer stmt.Close()
    _, err = stmt.Exec(credit.CreditCode, credit.UserCode, credit.CreditRank)
    if err  != nil {
      return false, err
      } else {
        return true, err
      }
      // It's more convenient

Query

stmt, err := mysqlUtility.DBConn.Prepare(`SELECT commodity_code, commodity_name, description, picture,
      price, storage, count, status,
      insert_datetime, update_datetime FROM commodity WHERE commodity_code = ?`)
      if err != nil {
        return nil, err
      }
      defer stmt.Close()
      result, err := stmt.Query(commodityCode)
      if err != nil {
        return nil, err
      }
      defer result.Close()
      var commodity utility.Commodity
      if result.Next() {
        err = result.Scan(&(commodity.CommodityCode), &(commodity.CommodityName), &(commodity.Description),
        &(commodity.Picture), &(commodity.Price), &(commodity.Storage), &(commodity.Count), &(commodity.Status),
        &(commodity.InsertDatetime), &(commodity.UpdateDatetime))
        if err != nil {
          pillarsLog.PillarsLogger.Print(err.Error())
          return nil, err
        }
      }
      return &commodity, err

7.2 Mongodb

err :=  mongoUtility.PictureCollection.Find(bson.M{"picturecode":*pictureCode}).One(&picture) 

Only the simplest example is given here. See mgo's development documentation for details on ok. It's a little bit easier.
8. Unit test considerations

Test the command go test-v (no more arguments!!) If you do not have -v, you only show the results, not the debugging process. It is mainly used for debugging and development
xxx_test.go file format xxx_test0.go file format xxx_test.go file format xxx_test.go
Due to the principle of test first, one test at the time of development is only one of two functions.
This is equivalent to commenting out the other tests
The configuration files should be placed under the test directory. Don't forget.
Mentality, too many mistakes 1 to 1, to have a good mentality.

9.LOG

Note the indefectibility of Log in debugging.

If you don't know where api came from, just search doc.

package utility
    import "log"
    import "os"
    import "fmt"
    // Logger Model min variable.
    var Logger *log.Logger
    var outFile *os.File
    // init function if Logger if not inited will invoke this function
    func init() {
      if Logger == nil {
        propertyMap := ReadProperty("pic.properties")
        logFileName := propertyMap["LogFile"]
        fmt.Println("Initial and Open log file ", logFileName)
        var err error
        outFile, err = os.OpenFile(logFileName, os.O_CREATE|os.O_APPEND|os.O_RDWR, 0666)
        if err != nil {
          panic(err.Error())
        }
        Logger = log.New(outFile, "", log.Ldate|log.Ltime|log.Llongfile)
      }
    }
    // CloseLogFile function : close Logger invoke file.
    func CloseLogFile() {
      outFile.Close()
    }

Usage:

utility.Logger.Println("Log test") 


Related articles: