开放的编程资料库

当前位置:我爱分享网 > Go教程 > 正文

如何在Golang中创建简单的HTTP服务器

GoHTTP服务器教程展示了如何在Golang中创建简单的HTTP服务器。

$ go version
go version go1.18.1 linux/amd64

我们使用Go版本1.18。

HTTP

超文本传输​​协议(HTTP是分布式、协作、超媒体信息系统的应用协议。HTTP协议是万维网数据通信的基础。

去http

在Go中,我们使用http包来创建GET和POST请求。该包提供了HTTP客户端和服务器实现。

Gohttp类型

客户端向服务器发送请求以接收资源。

type Request struct

Request表示由服务器接收、由客户端发送的HTTP请求。

type Response struct

Response表示来自HTTP请求的响应。

type ResponseWriter interface

HTTP处理程序使用ResponseWriter接口来构造HTTP响应。

type Handler interface {
    ServeHTTP(ResponseWriter, *Request)
}

Handler响应HTTP请求。ServeHTTP将回复标头和数据写入ResponseWriter,然后返回。

type HandlerFunc func(ResponseWriter, *Request)

HandlerFunc类型是一个适配器,它允许将普通函数用作HTTP处理程序。

去HTTP服务器句柄

Handle函数为给定的URL注册一个处理程序。处理程序的目标是创建对客户端请求的回复。

package main

import (
    "fmt"
    "net/http"
)

type CounterHandler struct {
    counter int
}

func (ct *CounterHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    fmt.Println(ct.counter)
    ct.counter++
    fmt.Fprintln(w, "Counter:", ct.counter)
}

func main() {

    th := &CounterHandler{counter: 0}
    http.Handle("/count", th)
    http.ListenAndServe(":8080", nil)
}

每次访问URL时,计数器都会递增并返回值。

type CounterHandler struct {
    counter int
}

func (ct *CounterHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    fmt.Println(ct.counter)
    ct.counter++
    fmt.Fprintln(w, "Counter:", ct.counter)
}

CounterHandler包含counter变量和ServeHTTP函数的实现。该函数递增计数器并将消息写入http.ResponseWriter

th := &CounterHandler{counter: 0}
http.Handle("/count", th)

CounterHandler创建并注册到Handle

$ curl localhost:8080/count
Counter: 1
$ curl localhost:8080/count
Counter: 2
$ curl localhost:8080/count
Counter: 3
$ curl localhost:8080/count
Counter: 4

去HTTP服务器HandleFunc

使用HandleFunc函数,我们为给定的URL模式注册一个处理函数。HandleFunc函数是创建处理程序的便捷方式。

package main

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

func main() {

    http.HandleFunc("/", HelloHandler)

    log.Println("Listening...")
    log.Fatal(http.ListenAndServe(":8080", nil))
}

func HelloHandler(w http.ResponseWriter, _ *http.Request) {

    fmt.Fprintf(w, "Hello, there\n")
}

该示例创建了一个侦听端口8080的简单HTTP服务器。在发送请求后,服务器会以“Hello,there”消息进行响应。

http.HandleFunc("/", HelloHandler)

我们使用HandleFunc/模式映射到HelloHandler

log.Fatal(http.ListenAndServe(":8080", nil))

ListenAndServe侦听TCP网络地址,然后处理传入连接的请求。

func HelloHandler(w http.ResponseWriter, _ *http.Request) {
    fmt.Fprintf(w, "Hello, there\n")
}

处理程序响应HTTP请求。它有两个参数:响应编写器和请求对象。

$ go run main.go

我们启动服务器。

$ curl localhost:8080/
Hello, there

使用curl工具,我们生成一个请求。

GoHTTP状态码

HTTP响应状态代码指示特定HTTP请求是否已成功完成。

响应分为五类:

  • 信息响应(100-199)
  • 成功响应(200-299)
  • 重定向(300-399)
  • 客户端错误(400-499)
  • 服务器错误(500-599)

响应的状态码是用WriteHeader函数写入的。

package main

import (
    "log"
    "net/http"
)

func main() {

    http.HandleFunc("/status", func(w http.ResponseWriter, _ *http.Request) {
        w.WriteHeader(http.StatusOK)
    })

    log.Println("Listening...")
    log.Fatal(http.ListenAndServe(":8080", nil))
}

我们为/status路径发送http.StatusOK

$ curl -I localhost:8080/status
HTTP/1.1 200 OK
Date: Sat, 23 Apr 2022 12:59:52 GMT

GoHTTPServer未找到处理程序

如果找不到服务器资源,将向客户端返回404错误代码。

package main

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

func main() {

    http.HandleFunc("/about", func(w http.ResponseWriter, _ *http.Request) {
        fmt.Fprintln(w, "about page")
    })

    http.HandleFunc("/news", func(w http.ResponseWriter, _ *http.Request) {
        fmt.Fprintln(w, "news page")
    })

    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {

        if r.URL.Path != "/" {
            w.WriteHeader(404)
            w.Write([]byte("404 - not found\n"))
            return
        }

        fmt.Fprintln(w, "home page")
    })

    log.Println("Listening...")
    log.Fatal(http.ListenAndServe(":8080", nil))
}

在示例中,我们有三个端点。对于这三个端点之外的任何其他端点,我们将返回404错误消息。

$ curl localhost:8080/about
about page
$ curl localhost:8080/
home page
$ curl localhost:8080/contact
404 - not found

去HTTP服务器获取标头

HTTP标头让客户端和服务器通过HTTP请求或响应传递附加信息。HTTP标头是一个名称/值对,由冒号字符分隔。

User-Agent请求标头是一个字符串,可让服务器和网络对等方识别请求用户代理的应用程序、操作系统、供应商和/或版本。

package main

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

func main() {

    http.HandleFunc("/ua", func(w http.ResponseWriter, r *http.Request) {

        ua := r.Header.Get("User-Agent")

        fmt.Fprintf(w, "User agent: %s\n", ua)
    })

    log.Println("Listening...")
    log.Fatal(http.ListenAndServe(":8080", nil))
}

Header字段中,我们获取User-Agent标头并将其返回给调用者。

package main

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

func main() {

    resp, err := http.Get("http://localhost:8080/ua")

    if err != nil {
        log.Fatal(err)
    }

    defer resp.Body.Close()

    body, err := ioutil.ReadAll(resp.Body)

    if err != nil {
        log.Fatal(err)
    }

    fmt.Println(string(body))
}

该示例在Go中创建了一个简单的客户端,它生成一个GET请求到/ua路径。

GoURL路径参数

我们可以在URL中向服务器发送数据。

package main

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

func main() {

    http.HandleFunc("/", HelloServer)
    fmt.Println("Server started at port 8080")

    log.Fatal(http.ListenAndServe(":8080", nil))
}

func HelloServer(w http.ResponseWriter, r *http.Request) {

    fmt.Fprintf(w, "Hello, %s!\n", r.URL.Path[1:])
}

在代码示例中,我们使用r.URL.Path[1:]获取URL路径值,并使用数据构建消息。

$ curl localhost:8080/John
Hello, John!

我们在URL路径中发送名称;服务器回复问候语。

Go查询参数

查询字符串是统一资源定位符(URL)的一部分,它为指定的参数赋值。

通用URL具有以下形式:

scheme:[//[user:password@]host[:port]][/]path[?query][#fragment]

查询参数以?字符开头。多个查询参数用&字符分隔。

https://example.com/path/page?name=John&occupation=teacher

这是一个带有两个查询参数的URL示例。

package main

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

func main() {

    http.HandleFunc("/", handler)

    log.Println("Listening...")
    log.Fatal(http.ListenAndServe(":8080", nil))
}

func handler(w http.ResponseWriter, r *http.Request) {

    keys, ok := r.URL.Query()["name"]

    name := "guest"

    if ok {

        name = keys[0]
    }

    fmt.Fprintf(w, "Hello %s!\n", name)
}

在代码示例中,我们接受一个name参数。我们使用r.URL.Query()["name"]获取参数。

$ curl localhost:8080/?name=Peter
Hello Peter!

我们将名称作为查询参数发送;服务器响应消息。

Go文件服务器

使用http.FileServer,我们将文件发送到客户端。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>About page</title>
</head>
<body>
    <p>
        About page
    </p>
</body>
</html>

这是about.html页面。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Home page</title>
</head>
<body>
    <p>
        Home page
    </p>

</body>
</html>

这是主页。

package main

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

func main() {

    fileServer := http.FileServer(http.Dir("./public"))
    http.Handle("/", fileServer)

    http.HandleFunc("/hello", func(w http.ResponseWriter, r *http.Request) {
        io.WriteString(w, "Hello there!\n")
    })

    log.Println("Listening...")
    log.Fatal(http.ListenAndServe(":8080", nil))
}

在代码示例中,我们有一个文件服务器和一个简单的hello处理程序。

fileServer := http.FileServer(http.Dir("./public"))
http.Handle("/", fileServer)

文件服务器已注册到Handle;它提供来自public目录的文件。

$ curl localhost:8080
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Home page</title>
</head>
<body>
    <p>
        Home page
    </p>

</body>
</html>
$ curl localhost:8080/about.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>About page</title>
</head>
<body>
    <p>
        About page
    </p>
</body>
</html>

我们请求主页和关于页面。

去处理GET/POST请求

在下面的示例中,服务器处理来自客户端的GET和POST请求。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Form</title>
</head>
<body>

    <form method="POST" action="/">
        <div>
            <label>Name:</label><input name="name" type="text">
        </div>
        <div>
            <label>Occupation:</label><input name="occupation" type="text">
        </div>
        <button type="submit" value="submit">Submit</button>
    </form>

</body>
</html>

HTML页面呈现一个简单的表单。

package main

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

func process(w http.ResponseWriter, r *http.Request) {

    if r.URL.Path != "/" {
        http.Error(w, "404 not found.", http.StatusNotFound)
        return
    }

    switch r.Method {
    case "GET":

        http.ServeFile(w, r, "form.html")
    case "POST":

        if err := r.ParseForm(); err != nil {
            fmt.Fprintf(w, "ParseForm() err: %v", err)
            return
        }

        name := r.FormValue("name")
        occupation := r.FormValue("occupation")

        fmt.Fprintf(w, "%s is a %s\n", name, occupation)

    default:
        fmt.Fprintf(w, "Sorry, only GET and POST methods are supported.")
    }
}

func main() {

    http.HandleFunc("/", process)

    log.Println("Listening...")
    log.Fatal(http.ListenAndServe(":8080", nil))
}

对于GET请求,我们发送带有表单的网页。对于POST请求,我们处理来自表单的数据。

case "GET":

    http.ServeFile(w, r, "form.html")

如果请求是GET请求,我们将form.html发送给客户端。

case "POST":

    if err := r.ParseForm(); err != nil {
        fmt.Fprintf(w, "ParseForm() err: %v", err)
        return
    }

如果是POST请求,我们调用ParseForm函数;它解析来自URL的原始查询并更新r.Form

name := r.FormValue("name")
occupation := r.FormValue("occupation")

fmt.Fprintf(w, "%s is a %s\n", name, occupation)

我们使用FormValue获取表单值并构建一条消息。

转到HTTP服务图像

在下面的示例中,我们提供一张图片。

package main

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

func main() {

    handler := http.HandlerFunc(handleRequest)

    http.Handle("/image", handler)

    fmt.Println("Server started at port 8080")
    http.ListenAndServe(":8080", nil)
}

func handleRequest(w http.ResponseWriter, r *http.Request) {

    buf, err := ioutil.ReadFile("sid.png")

    if err != nil {

        log.Fatal(err)
    }

    w.Header().Set("Content-Type", "image/png")
    w.Write(buf)
}

在代码示例中,我们创建了一个向客户端发送图像的简单Web服务器。该图像位于当前工作目录中。

handler := http.HandlerFunc(handleRequest)

http.Handle("/image", handler)

我们将处理程序映射到/image路径。

func handleRequest(w http.ResponseWriter, r *http.Request) {
...

处理函数接受两个参数:http.ResponseWriterhttp.Request

buf, err := ioutil.ReadFile("sid.png")

我们将图像读入缓冲区。

w.Header().Set("Content-Type", "image/png")

我们设置标题。Content-Type内容类型用于PNGimage。

w.Write(buf)

图像数据通过Write写入响应体。

GoHTTP服务器模板

Go有一个内置的模板包,用于生成动态HTML内容。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Users</title>
</head>

<body>
    <table>
        <thead>
            <tr>
                <th>Name</th>
                <th>Occupation</th>
            </tr>
        </thead>
        <tbody>
            {{ range .Users}}
            <tr>
                <td>{{.Name}}</td>
                <td>{{.Occupation}}</td>
            </tr>
            {{ end }}
        </tbody>
    </table>
</body>
</html>

输出是一个HTML文件。数据被插入到HTML表格中。

package main

import (
    "html/template"
    "log"
    "net/http"
)

type User struct {
    Name       string
    Occupation string
}

type Data struct {
    Users []User
}

func main() {

    tmp := template.Must(template.ParseFiles("layout.html"))

    http.HandleFunc("/users", func(w http.ResponseWriter, _ *http.Request) {

        data := Data{
            Users: []User{
                {Name: "John Doe", Occupation: "gardener"},
                {Name: "Roger Roe", Occupation: "driver"},
                {Name: "Peter Smith", Occupation: "teacher"},
            },
        }
        tmp.Execute(w, data)
    })

    log.Println("Listening...")
    http.ListenAndServe(":8080", nil)
}

Web服务器返回一个HTML页面,其中包含/usersURL路径的用户表。

在本教程中,我们使用Go创建了简单的HTTP服务器。

列出所有Go教程。

未经允许不得转载:我爱分享网 » 如何在Golang中创建简单的HTTP服务器

感觉很棒!可以赞赏支持我哟~

赞(0) 打赏