Golang小项目(1)


前言

本项目适合Golang初学者,通过简单的项目实践来加深对Golang的基本语法和Web开发的理解。

正文

项目结构

.
├── main.go
└── static
    ├── form.html
    └── index.html

项目流程图

定义三个路由:

  • /:首页,显示static/index.html页面
  • /hello:欢迎页面,显示 hello
  • /form:表单页面,显示static/form.html页面

项目初始化

package main

// 导入必要的包
import (
    "fmt"
    "log"
    "net/http"
)

func main(){

}

在static目录下创建index.html

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

在static目录下创建form.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Form Page</title>
</head>
<body>
    <div>
        <form action="/form" method="POST">
            <label>Name</label><input name="name" type="text" value=""/>
            <label>Address</label><input name="address" type="text" value=""/>

            <input type="submit" value="Submit"/>
        </form>
    </div>
</body>
</html>

编写程序主入口

func main() {
    // 创建一个 HTTP 文件服务器,用于提供静态文件
    fileServer := http.FileServer(http.Dir("./static"))
    // 将根路径 / 绑定到 fileServer 处理器。这意味着所有根路径的请求都由 fileServer 处理,提供 ./static 目录中的文件。
    http.Handle("/", fileServer)
    // 将 /form 路径绑定到 formHandler 函数。这意味着对 /form 的请求将由 formHandler 处理。
	http.HandleFunc("/form", formHandler)
    // 将 /hello 路径绑定到 helloHandler 函数。这意味着对 /hello 的请求将由 helloHandler 处理。
    http.HandleFunc("/hello", helloHandler)

    // 打印一条消息,表示服务器正在启动,监听 8080 端口。
    fmt.Fprintf("Starting server at port 8080\n")
    // 启动一个 HTTP 服务器,监听 8080 端口。如果启动失败,err 将不为空。
    // nil 表示使用默认的 ServeMux 路由器。
    if err := http.ListenAndServe(":8080",nil); err != nil {
        // 如果服务器启动失败,记录错误并终止程序。
        log.Fatal(err)
    }
}

编写路由处理函数

// 表单处理函数
// w 为 http.ResponseWriter 的实例,用于向客户端返回响应。
// r 为 http.Request 的实例,包含了客户端的请求信息。
func formHandler(w http.ResponseWriter, r *http.Request){
	// 尝试解析表单数据。如果解析失败,将打印err信息。
    if err := r.ParseForm(); err != nil {
		fmt.Fprintf(w, "ParseForm() err: %v", err)
        // 结束函数执行
		return
	}
    // 如果表单解析成功,向响应写入成功信息。
	fmt.Fprintf(w, "POST request successful")
    // 获取form.html页面中表单中名为 name 和 address 的字段值。
	name := r.FormValue("name")
	address := r.FormValue("address")
    // 将 name 和 address 字段值写入响应 w。
	fmt.Fprintf(w, "Name = %s\n", name)
	fmt.Fprintf(w, "Address = %s\n", address)
}



/// 欢迎页面处理函数
func helloHandler(w http.ResponseWriter, r *http.Request){
    // 检查request请求的 URL 路径是否为 /hello,如果不是,返回 404 错误响应。
	if r.URL.Path != "/hello" {
		http.Error(w, "404 not found", http.StatusNotFound)
        // 结束函数执行
		return
	}
    // 检查请求的方法是否为 GET,如果不是,返回 405 错误(即请求方法不允许)。
	if r.Method != "GET" {
		http.Error(w, "method is not supported", http.StatusNotFound)
		return
	}
    //  如果路径和方法均匹配,向响应写入 "hello!"。
	fmt.Fprintf(w, "hello!")
}

运行项目

go build main.go
go run main.go

打开浏览器,访问 http://localhost:8080/ ,显示 Static Website 页面。
打开浏览器,访问 http://localhost:8080/hello ,显示 hello! 页面。
打开浏览器,访问 http://localhost:8080/form.html ,显示 Form Page 页面,并可以输入姓名和地址。

每日小技巧:指针传递

在 Go 语言中,* 用于声明和操作指针。具体到 r *http.Request 中的 *,它表示 r 是一个指向 http.Request 类型的指针。下面详细解释了在这种上下文中使用 * 的含义和作用:

  1. 声明指针

在函数参数中使用 * 来声明指针类型。这意味着参数 r 是一个 http.Request 的指针,而不是 http.Request 的值本身。

func handleRequest(r *http.Request) {
    // 这里的 r 是 *http.Request 类型
}
  1. 访问和操作 http.Request 对象
  • 通过指针访问字段: 使用指针 *http.Request 可以直接访问和修改 http.Request 对象的字段,因为指针指向了实际的 http.Request 对象,而不是其副本。
  • 修改请求: 如果函数需要对请求进行修改(例如修改请求头或解析请求体),使用指针可以直接在原始对象上进行操作。
func modifyHeader(r *http.Request) {
    r.Header.Set("X-Custom-Header", "Value") // 修改请求头
}
  1. 避免对象复制
  • 节省内存: 如果 http.Request 是一个大结构体,直接传递指针而不是整个结构体可以减少内存使用和复制开销。
  • 提高性能: 传递指针避免了将整个结构体复制到函数调用栈,从而提高了性能。
  1. 指针解引用
  • 获取实际值: 如果你有一个指向 http.Request 的指针,可以通过解引用(*r)来访问实际的 http.Request 对象。
func printRequestMethod(r *http.Request) {
fmt.Println(r.Method) // 访问请求方法
}
  1. 一致性和标准库
  • 与标准库一致: Go 的标准库中处理 HTTP 请求的函数普遍使用 *http.Request,这为开发者提供了一种一致的编程模式,并确保了对 http.Request 的高效处理。