Go语言中http和mysql的实现代码

http 编程

Go 原生支持http:

import "net/http"

Go 的http服务性能和nginx比较接近:

就是说用Go写的Web程序上线,程序前面不需要再部署nginx的Web服务器,这里省掉的是Web服务器。如果服务器上部署了多个Web应用,还是需要反向代理的,一般这也是nginx或apache。

几行代码就可以实现一个web服务:

package main
import (
 "fmt"
 "net/http"
)
func Hello(w http.ResponseWriter, r *http.Request) {
 fmt.Println(*r)
 fmt.Fprintf(w, "Hello World")
}
func main() {
 http.HandleFunc("/", Hello)
 err := http.ListenAndServe("0.0.0.0:8000", nil)
 if err != nil {
  fmt.Println("http Listen failed")
 }
}

http client

http 常见的请求方法:

  • Get请求
  • Post请求
  • Put请求
  • Delete请求
  • Head请求

Get 请求

使用Get请求网站的示例:

package main

import (
 "fmt"
 "io/ioutil"
 "net/http"
)

func main() {
 res, err := http.Get("http://edu.51cto.com")
 if err != nil {
  fmt.Println("http get ERRPR:", err)
  return
 }
 data, err := ioutil.ReadAll(res.Body)
 if err != nil {
  fmt.Println("get data ERROR:", err)
  return
 }
 fmt.Println(string(data))
}

Head请求

Head请求只返回响应头。如果只想要获取一些状态信息的话,可以用Head请求。这样避免返回响应体,响应体的数据是比较多的,适合做监控。Head请求的示例:

package main
import (
 "fmt"
 "net/http"
)
var urls = []string{
 "http://×××w.baidu.com",
 "http://×××w.google.com",
 "http://×××w.sina.com.cn",
 "http://×××w.163.com",
}
func main() {
 for _, v := range urls {
  resp, err := http.Head(v)
  if err != nil {
   fmt.Println("Head request ERROR:", err)
   continue
  }
  fmt.Println(resp.Status)
 }
}

http 常见状态码

http.StatusContinue = 100

http.StatusOK = 200

http.StatusFound = 302 跳转

http.StatusBadRequest = 400 非法请求

http.StatusUnanthorized = 401 没有权限

http.StatusForbidden = 403 禁止访问

http.Status.NotFound = 404 页面不存在

http.StatusInternalServerError = 500 内部错误

处理form表单

package main

import (
 "fmt"
 "io"
 "net/http"
)

const form = `
<html>
<body>
<form action="#" method="post" name="bar">
 <input type="text" name="in" />
 <input type="text" name="in" />
 <input type="submit" value="Submit" />
</form>
</body>
</html>`

func FormServer(w http.ResponseWriter, request *http.Request) {
 w.Header().Set("content-Type", "text/html")
 switch request.Method {
 case "GET":
  io.WriteString(w, form)
 case "POST":
  request.ParseForm()
  io.WriteString(w, request.Form["in"][0]) // 注意上面的2个input的name是一样的
  io.WriteString(w, request.Form["in"][1]) // 所以这是一个数组
  io.WriteString(w, "</br>")
  io.WriteString(w, request.FormValue("in")) // 一般去一个值,就用这个方法
 }
}

func main() {
 http.HandleFunc("/form", FormServer)
 if err := http.ListenAndServe(":8000", nil); err != nil {
  fmt.Println("监听端口ERROR:", err)
 }
}

panic 处理

如果处理函数里有panic,会导致整个程序崩溃,所以要 defer revoer() 来处理 panic。在处理函数开头defer一个匿名函数:

func FormServer(w http.ResponseWriter, request *http.Request) {
 // 增加一个defer来处理panic
 defer func() {
  if x := recover(); x != nil {
   log.Println(request.RemoteAddr, "捕获到异常:", x)
  }
 }()
 // 原本的处理函数的内容
 w.Header().Set("content-Type", "text/html")
 switch request.Method {
 case "GET":
  io.WriteString(w, form)
 case "POST":
  request.ParseForm()
  io.WriteString(w, request.FormValue("in")) // 一般去一个值,就用这个方法
 }
 // 搞个panic出来
 zero := 0
 tmp := 1 / zero
 io.WriteString(w, string(tmp))
}

优化统一处理

按照上面的做法,要在每个处理函数的开头都加上panic的处理。由于每个处理函数的panic处理方法都一样,所以可以写一个自定义的处理函数:

// 自定义的panic处理的函数
func logPanics(handle http.HandlerFunc) http.HandlerFunc {
 return func(writer http.ResponseWriter, request *http.Request) {
  defer func() {
   if x := recover(); x != nil {
    log.Println(request.RemoteAddr, "捕获到异常:", x)
   }
  }()
  // 上面先处理panic,再接着下面调用业务逻辑
  handle(writer, request)
 }
}

func main() {
 // http.HandleFunc("/form", FormServer) // 修改调用处理函数的方法
 http.HandleFunc("/form", logPanics(FormServer)) // 把处理函数传给自己写的封装了panic处理的函数里
 if err := http.ListenAndServe(":8000", nil); err != nil {
  fmt.Println("监听端口ERROR:", err)
 }
}

原本直接调用处理函数。现在调用自定义的函数,把处理函数传进去。在自定义的函数里先加载defer,然后再调用执行原本的处理函数。逻辑很简单,就是把处理函数作为参数传给自定义的函数,在自定义的函数里再调用处理函数。在自定义的函数里写上defer,这样就相当于所有的处理函数都有defer了。

模板

使用模板需要用到 "text/template" 包。然后调用模板的t.Execute()方法输出。

替换

先准备一个简单的模板:

<p>Hello {{.Name}}</p>
<p>Age: {{.Age}}</p>

然后在Go里使用模板:

package main

import (
 "fmt"
 "os"
 "text/template"
)

type Person struct {
 Name string
 Age int
}

func main() {
 t, err := template.ParseFiles("index.html")
 if err != nil {
  fmt.Println("模板解析异常:", err)
  return
 }
 p := Person{"Bob", 32}
 if err := t.Execute(os.Stdout, p); err != nil {
  fmt.Println("模板加载数据异常:", err)
 }
}

/* 执行结果
PS H:\Go\src\go_dev\day10\http\use_template> go run main.go
<p>Hello Bob</p>
<p>Age: 32</p>
PS H:\Go\src\go_dev\day10\http\use_template>
*/

如果直接用 {{.}} 不加字段名的话,就是输出结构体打印的效果。

输出到浏览器里

要输出到浏览器里,只需要在 t.Execute(os.Stdout, p) 里,把原本输出到终端换成输出到处理函数的 w http.ResponseWriter 类型,就好了。

html模板的内容不变,下面是go的代码:

package main

import (
 "fmt"
 "net/http"
 "text/template"
)

func Hello(w http.ResponseWriter, r *http.Request) {
 fmt.Fprintf(w, "Hello World")
}

type Person struct {
 Name string
 Age int
}

func Index(w http.ResponseWriter, r *http.Request) {
 p := Person{"Cara", 18}
 t, err := template.ParseFiles("index.html")
 if err != nil {
  fmt.Println("加载模板ERROR:", err)
  return
 }
 t.Execute(w, p)
}

func main() {
 http.HandleFunc("/", Hello)
 http.HandleFunc("/index", Index)
 err := http.ListenAndServe("0.0.0.0:8000", nil)
 if err != nil {
  fmt.Println("http Listen failed")
 }
}

判断

用法示例:

<body>
{{if gt .Age 18}}
<p>已成年</p>
{{else}}
<p>未成年</p>
{{end}}
</body>

更多判断逻辑:

not 非

{{if not .condition}}

{{end}}

and 与

{{if and .condition1 .condition2}}

{{end}}

or 或

{{if or .condition1 .condition2}}

{{end}}

eq 等于

{{if eq .var1 .var2}}

{{end}}

ne 不等于

{{if ne .var1 .var2}}

{{end}}

lt 小于

{{if lt .var1 .var2}}

{{end}}

le 小于等于

{{if le .var1 .var2}}

{{end}}

gt 大于

{{if gt .var1 .var2}}

{{end}}

ge 大于等于

{{if ge .var1 .var2}}

{{end}}

with 封装

with语句就是创建一个封闭的作用域,在其范围内,{{.}}代表with的变量,而与外面的{{.}}无关,只与with的参数有关:

<body>
{{with .Name}}
<p>{{.}}</p>
{{end}}
</body>

上面这样包在 {{with .Var}} 里,with 里的 {{.}} 代表的就是 Var 这个变量。

with 可以封装常数:

{{ with "world"}}
 Now the dot is set to {{ . }}
{{ end }}

循环(遍历)

golang的template支持range循环来遍历map、slice内的内容,在range循环内,还可以使用$设置循环变量,我们可以通过 $i $v 来访问遍历的值。语法为:

{{range $i, $v := .slice}}
 <li>key: {{ $key }}, value: {{ $value }}</li>
{{end}}

这是另外一种遍历方式,这种方式无法访问到index或者key的值,需要通过点来访问对应的value:

{{range .slice}}
{{.field}}
{{end}}

在循环内,点是代表遍历的值。原本使用点来访问的变量,那么在循环内部就要用 $. 来访问。下面的例子表示循环内和循环外 ArticleConten 这个变量访问的方式:

{{.ArticleContent}}
{{range .slice}}
{{$.ArticleContent}}
{{end}}

定义变量

模板的参数可以是go中的基本数据类型,如字串,数字,布尔值,数组切片或者一个结构体。在模板中设置变量可以使用 $variable := value。我们在range迭代的过程使用了设置变量的方式。

{{$article := "hello"}}
{{$name := .Name}}

mysql 使用

这里只简单讲了数据的增删改查,所以测试代码前,需要先把数据库准备好。

先创建一个数据库,指定了编码,这样应该可以支持中文:

CREATE DATABASE 库名 CHARSET "utf8";

然后建2张表:

CREATE TABLE person (
  user_id int primary key auto_increment,
  username varchar(260),
  gender varchar(260),
  email varchar(260)
);

CREATE TABLE place (
  country varchar(200),
  city varchar(200),
  telcode int
);

导入数据库驱动

sql 包提供了通用的SQL(或类SQL)数据库接口。

sql 包必须与数据库驱动结合使用。

驱动包需要安装:

go get -u github.com/go-sql-driver/mysql

使用前,先要导入mysql的包:

import (
  "database/sql"
  _ "github.com/go-sql-driver/mysql"
)

上面导入了2个包。第一个是sql包,就是我们调用操作数据库用的。

第二个是驱动包,这里前面加了占位符,所以这个包只是引入,但是不使用它。并且如果要操作别的数据库的话,只需要修改驱动包就行了。

连接数据库

构建连接, 格式是:”用户名:密码@tcp(IP:端口)/数据库?charset=utf8” :

package main

import (
  "fmt"
  "time"
  "database/sql"
  _ "github.com/go-sql-driver/mysql"
)

var DB *sql.DB

func init() {
  database, err := sql.Open("mysql", "admin:admin123@tcp(192.168.3.103:3306)/Golang_week10")
  if err != nil {
    fmt.Println("连接数据库失败:", err)
    return
  }
  DB = database
}

func main() {
  fmt.Println(DB)
  DB.SetMaxIdleConns(16) //设置闲置连接数
  DB.SetMaxOpenConns(100) //设置最大连接数
  DB.SetConnMaxLifetime(100*time.Second) //最大连接周期,超过时间的连接就close
  fmt.Println(DB)
}

插入数据

下面是插入数据,并且再获取id的示例:

// 数据库连接的init函数就省略了
func insert() {
  r, err := DB.Exec("insert into person(username,gender,email) values(?,?,?)", "Barry", "Male", "Barry@go.net")
  if err != nil {
    fmt.Println("插入数据ERROR:", err)
    return
  }
  fmt.Println(r)
  id, err := r.LastInsertId()
  if err != nil {
    fmt.Println("获取id ERROR:", err)
    return
  }
  fmt.Println(id)
}

func main() {
  insert()
}

上面的 values(?,?,?) 里的问号,是占位符,具体的值可以写在后面的参数里。当然如果不用占位符,直接就传1个字符串作为参数也是可以的。

查询

查询单个字段:

func query() {
  row := DB.QueryRow("select username from person where user_id=?", 1)
  var name string // 创建变量用于存放查询到的数据
  if err := row.Scan(&name); err != nil {
    fmt.Println("Scan Failed:", err)
    return
  }
  fmt.Println(name)
}

func main() {
  query()
}

也可以一次查询多个字段或所有字段,查询之前按照表的类型创建结构体,用查询到的数据为结构体赋值:

type Person struct {
  ID int `db:"user_id"`
  Username sql.NullString `db:"username"`
  Gender sql.NullString `db:"gender"`
  Email sql.NullString `db:"email"`
}

func query() {
  row := DB.QueryRow("select * from person where user_id=?", 6)
  var person = new(Person)
  // row.scan中的字段必须是按照数据库存入字段的顺序,否则报错
  if err := row.Scan(&person.ID, &person.Username, &person.Gender, &person.Email); err != nil {
    fmt.Println("Scan Failed:", err)
    return
  }
  fmt.Println(person)
}

func main() {
  query()
}

数据模型,就是上面定义的结构体。这里的类型可以是Go的标准数据类型。但是如果数据库的字段允许为空,并且该字段的值也为空,那么查询后该字段会返回nil。如果是string类型,则无法接收nil,但sql.NullString则可以接收nil值。

另外,结构体里的tag标签在这里没有意义。不过上面的tag标注了该字段在数据库里对应的字段名,可能在别处会有用。

查询多行

func query() {
  rows, err := DB.Query("select * from person where user_id > ?", 1)
  defer func() {
    if rows != nil {
      rows.Close()
    }
  }()
  if err != nil {
    fmt.Println("Query 查询 ERROR:", err)
    return
  }
  var person = new(Person)
  for rows.Next() {
    if err = rows.Scan(&person.ID, &person.Username, &person.Gender, &person.Email); err != nil {
      fmt.Println("Scan Failed:", err)
      return
    }
    fmt.Println(person)
  }
}

func main() {
  query()
}

查询用起来还是不太方法,不过还可以选择其他第三方库,那里会有一些很好的扩展。后面会举例。

其他操作

由于基本都是用SQL的命令进行操作,所以其他操作就不一个一个举例了

update 更新数据

result, err := DB.Exec("UPDATE person set email=? where username=?", "Cara", Cara@catco.org)

delete 删除数据

result,err := DB.Exec("DELETE FROM person where id=?",1)

注意:更新数据不返回LastInsertID,所以result.LastInsertID一直为0。删除数据可以拿到LastInsertID,用法和插入数据里一样。

第三方库 sqlx

sqlx是一个go语言包,在内置database/sql包之上增加了很多扩展,简化数据库操作代码的书写。

由于database/sql接口是sqlx的子集,所有database/sql的用法,在sqlx中一样可以用。不过sqlx还有更多扩展,用起来更方便。

安装:

go get github.com/jmoiron/sqlx

查询 Select() 方法

Select是一个非常省时的扩展。它们把query和非常灵活的scan语法结合起来。Select用来获取结果切片:

// 这里的tag标签就有意义了,下面的Select()方法应该就是根据tag标签对号入座的
type Person struct {
  ID int `db:"user_id"`
  Username sql.NullString `db:"username"`
  Gender sql.NullString `db:"gender"`
  Email sql.NullString `db:"email"`
}

func select() {
  var persons []Person // 这里创建的是存放结构体的切片
  if err := DB.Select(&person, "select * from person where userid > ?", 1); err != nil {
    fmt.Println("Select ERROR:", err)
    return
  }
  fmt.Println(person)
}

Select可以提高编码效率,还有更多扩展。sqlx 号称 golang 数据库开发神器,这里就提一下,等到真正用的时候再去深入学习了。

(0)

相关推荐

  • Go语言使用MySql的方法

    本文实例讲述了Go语言中使用MySql的方法.分享给大家供大家参考.具体如下: 此代码需要先安装mysql的go语言驱动. 首先安装mysql的go语言驱动: 复制代码 代码如下: go get github.com/ziutek/mymysql/godrv 示例代码如下: 复制代码 代码如下: package users import (     "database/sql"     "fmt"     _ "github.com/ziutek/mymy

  • Go语言集成mysql驱动、调用数据库、查询数据操作示例

    本文实例讲述了Go语言集成mysql驱动.调用数据库.查询数据操作.分享给大家供大家参考,具体如下: 1.安装第三方mysql驱动包 go get -u github.com/go-sql-driver/mysql 2.连接数据库基本代码 复制代码 代码如下: package main import (         _"github.com/go-sql-driver/mysql"  // 注意前面的下划线_, 这种方式引入包只执行包的初始化函数         "dat

  • Go语言操作mysql数据库简单例子

    Go语言操作数据库非常的简单, 他也有一个类似JDBC的东西"database/sql" 实现类是"github.com/go-sql-driver/mysql" 使用过JDBC的人应该一看就懂 对日期的处理比较晦涩,没有JAVA流畅: 复制代码 代码如下: package main import (     "database/sql"     _ "github.com/go-sql-driver/mysql"     &

  • Golang中如何对MySQL进行操作详解

    前言 Golang官方并没有提供数据库驱动,但通过database/sql/driver包来提供了实现驱动的标准接口.可以在Github上找到很多开源的驱动. 其中go-sql-driver/mysql是一个比较推荐的驱动,其完全支持database/sql接口. 使用这个驱动, 在项目里import进: import ( "database/sql" _ "github.com/go-sql-driver/mysql" ) 在正式使用database/sql包之前

  • golang gorm 操作mysql及gorm基本用法

    golang 官方的那个操作mysql的有点麻烦所以就使用了gorm,下面就gorm的使用做下简单介绍 下载gorm: go get -u github.com/jinzhu/gorm 在项目中引入gorm: import ( "github.com/jinzhu/gorm" _ "github.com/jinzhu/gorm/dialects/mysql" ) 定义db连接信息 func DbConn(MyUser, Password, Host, Db stri

  • golang实现mysql数据库备份的操作方法

    背景 navicat是mysql可视化工具中最棒的,但是,在处理视图的导入导出方面,它是按照视图名称的字母顺序来处理的,若视图存在依赖,在导入过程中就会报错.前面已经用python写了一个,但在使用过程中,遇到xfffd编码,python的pymysql会直接崩溃.发现golang没有这个问题,正好用go重写,来熟悉golang. 一些关键点 map & json,在处理主键与外键信息时,需要用到json数据结构来存储中间结果,因为要灵活处理,在golang中只能用map[string]inte

  • 在golang中操作mysql数据库的实现代码

    前言 Golang 提供了database/sql包用于对SQL数据库的访问, 作为操作数据库的入口对象sql.DB, 主要为我们提供了两个重要的功能: •sql.DB 通过数据库驱动为我们提供管理底层数据库连接的打开和关闭操作. •sql.DB 为我们管理数据库连接池 需要注意的是,sql.DB表示操作数据库的抽象访问接口,而非一个数据库连接对象;它可以根据driver打开关闭数据库连接,管理连接池.正在使用的连接被标记为繁忙,用完后回到连接池等待下次使用.所以,如果你没有把连接释放回连接池,

  • Go语言中http和mysql的实现代码

    http 编程 Go 原生支持http: import "net/http" Go 的http服务性能和nginx比较接近: 就是说用Go写的Web程序上线,程序前面不需要再部署nginx的Web服务器,这里省掉的是Web服务器.如果服务器上部署了多个Web应用,还是需要反向代理的,一般这也是nginx或apache. 几行代码就可以实现一个web服务: package main import ( "fmt" "net/http" ) func

  • Go语言中strings和strconv包示例代码详解

    前缀和后缀 HasPrefix判断字符串s是否以prefix开头: strings.HaxPrefix(s string, prefix string) bool 示例: package main import ( "fmt" "strings" ) func main() { pre := "Thi" str1 := "This is a Go program!" fmt.Println(strings.HasPrefix(

  • c语言中if 语句的作用范围示例代码

    复制代码 代码如下: # include <stdio.h> int main(void) { if (1 > 2) printf("第一条表达式"); printf("第二条表达式"); } /* 输出结果 第二条表达式 */ 说明if语句的作用范围只有紧跟if的第一条表达式

  • C语言中strcmp的实现原型

    C语言中strcmp的实现原型 实现代码: int __cdecl strcmp ( const char * src, const char * dst ) { int ret = 0 ; while( ! (ret = *(unsigned char *)src - *(unsigned char *)dst) && *dst) ++src, ++dst; if ( ret < 0 ) ret = -1 ; else if ( ret > 0 ) ret = 1 ; ret

  • ASP中用select case代替其他语言中的switch case, default用case else

    asp中不能用switch语句,要用select case语句了 简单的介绍一下 选择报表的工作一样,如果语句.然而不同的是,他们可以检查多个值.当然,你有 多个相同的,如果.. else语句,但是这并不总是最好的方法. 选择语句允许一个程序来计算表达式,并试图匹配表达式的值案件标签.如果找到匹 配,程序执行相关的声明.对于SELECT语句的语法如下: select case expression case label_1 statements_1 case label_2 statements

  • C语言中printf()缓冲问题详解

    前言 缓冲区又称为缓存,它是内存空间的一部分.也就是说,在内存空间中预留了一定的存储空间,这些存储空间用来缓冲输入或输出的数据,这部分预留的空间就叫做缓冲区. 缓冲区根据其对应的是输入设备还是输出设备,分为输入缓冲区和输出缓冲区. 为什么要引入缓冲区 比如我们从磁盘里取信息,我们先把读出的数据放在缓冲区,计算机再直接从缓冲区中取数据,等缓冲区的数据取完后再去磁盘中读取,这样就可以减少磁盘的读写次数,再加上计算机对缓冲区的操作大大快于对磁盘的操作,故应用缓冲区可大大提高计算机的运行速度. 又比如,

  • C语言中的getchar和putchar的使用方法

    C语言中的getchar和putchar的使用方法 getchar是以行为单位进行存取的. 当用getchar进行输入时,如果输入的第一个字符为有效字符(即输入是文件结束符EOF,Windows下为组合键Ctrl+Z, Unix/Linux下为组合键Ctrl+D),那么只有当最后一个输入字符为换行符'\n'(也可以是文件结束符EOF,EOF将在后面讨论)时, getchar才会停止执行,整个程序将会往下执行.譬如下面程序段: while((c = getchar()) != EOF){ putc

  • 详解C语言中return与exit的区别

    详解C语言中return与exit的区别 1,exit用于在程序运行的过程中随时结束程序,exit的参数是返回给OS的.main函数结束时也会隐式地调用exit函数.exit函数运行时首先会执行由atexit()函数登记的函数,然后会做一些自身的清理工作,同时刷新所有输出流.关闭所有打开的流并且关闭通过标准I/O函数tmpfile()创建的临时文件.exit是结束一个进程,它将删除进程使用的内存空间,同时把错误信息返回父进程,而return是返回函数值并退出函数 2,return是语言级别的,它

  • 详解 Go 语言中 Map 类型和 Slice 类型的传递

    Map 类型 先看例子 m1: func main() { m := make(map[int]int) mdMap(m) fmt.Println(m) } func mdMap(m map[int]int) { m[1] = 100 m[2] = 200 } 结果是 map[2:200 1:100] 我们再修改如下 m2: func main() { var m map[int]int mdMap(m) fmt.Println(m) } func mdMap(m map[int]int) {

  • go语言中range用法

    本文实例讲述了go语言中range用法.分享给大家供大家参考.具体分析如下: range是go语言系统定义的一个函数. 函数的含义是在一个数组中遍历每一个值,返回该值的下标值和此处的实际值. 假如说a[0]=10,则遍历到a[0]的时候返回值为0,10两个值. 下面是一个例子:这个例子是求一个数组里面的平均值. 复制代码 代码如下: package main import (     "fmt" ) func main() {     sum := 0.0     var avg fl

随机推荐