创建HTTP服务器

package main

import (
   "fmt"
   "net/http"
   "path"
   "path/filepath"
   "strings"
   "time"
)

func main() {
   // 演示地址:http://localhost:8080/demo.html?name=张三&gender=男&age=18
   http.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) {
      // 屏蔽“/favicon.ico”的请求
      if request.URL.Path == "/favicon.ico" {
         return
      }

      // 设置响应头
      writer.Header().Set("Content-Type", "text/html; charset=utf-8") // 默认为"text/plain; charset=utf-8"
      // writer.Header().Set("Content-Type", "application/json; charset=utf-8") // 响应内容为JSON时使用
      writer.Header().Set("Server", "MaNongServer")
      writer.Header().Set("X-AspNetMvc-Version", "4.0")    // 自定义响应头
      writer.Header().Set("X-AspNet-Version", "4.0.30319") // 自定义响应头
      writer.Header().Set("X-Powered-By", "ASP.NET")       // 自定义响应头

      // 简单返回404错误页
      // 重要提醒:writer.WriteHeader()会覆盖http.NotFound()的状态码,若状态码不符合预期可检查是否为两者先后顺序导致
      // http.NotFound(writer, request)
      // return // http.NotFound()不会终止程序往下执行,必须使用return手动终止

      // 设置HTTP状态码(非必须,若不设置则默认为200)
      writer.WriteHeader(http.StatusOK) // const StatusOK int = 200
      // writer.WriteHeader(http.StatusNotFound) // const StatusNotFound int = 404

      // 获取请求头部分信息
      fmt.Printf("Accept-Encoding ===> %+v \n", request.Header.Get("Accept-Encoding"))
      fmt.Printf("Referer ===> %+v \n", request.Header.Get("Referer"))
      fmt.Printf("User-Agent ===> %+v \n", request.Header.Get("User-Agent"))
      // Accept-Encoding ===> gzip, deflate, br
      // Referer ===>
      // User-Agent ===> Mozilla/5.0 (Windows NT 10.0; Win64; x64) ……(此处省略内容若干)……

      // 请求方法
      fmt.Printf("request.Method ===> %+v \n", request.Method)
      // request.Method ===> GET

      // 请求URI
      fmt.Printf("request.RequestURI ===> %+v \n", request.RequestURI)
      // request.RequestURI ===> /demo.html?name=%E5%BC%A0%E4%B8%89&gender=%E7%94%B7&age=18

      // 请求路径
      fmt.Printf("request.URL.Path ===> %+v \n", request.URL.Path)
      // request.URL.Path ===> /demo.html

      // 请求参数(map)
      fmt.Printf("request.URL.Query() ===> %+v \n", request.URL.Query())
      // request.URL.Query() ===> map[age:[18] gender:[男] name:[张三]]

      // 请求参数(裸)
      fmt.Printf("request.URL.RawQuery ===> %+v \n", request.URL.RawQuery)
      // request.URL.RawQuery ===> name=%E5%BC%A0%E4%B8%89&gender=%E7%94%B7&age=18

      // 解析请求路径
      dir, file := filepath.Split(request.URL.Path)
      ext := path.Ext(file)
      filename := strings.TrimSuffix(file, ext)
      fmt.Printf("dir ===> %+v \n", dir)           // dir ===> /             目录
      fmt.Printf("file ===> %+v \n", file)         // file ===> demo.html    文件名(含扩展名)
      fmt.Printf("ext ===> %+v \n", ext)           // ext ===> .html         扩展名
      fmt.Printf("filename ===> %+v \n", filename) // filename ===> demo     文件名(无扩展名)

      // 获取GET参数
      name := request.URL.Query().Get("name")
      gender := request.URL.Query().Get("gender")
      age := request.URL.Query().Get("age")
      fmt.Printf("俺叫%+v%+v),今年%+v岁。\n", name, gender, age) // 俺叫张三(男),今年18岁。

      // 设置要输出的内容,writer.Write()可多次调用
      _, _ = writer.Write([]byte("PHP是世界上最好の语言"))
      _, _ = writer.Write([]byte("~~~"))
      // 页面输出结果:PHP是世界上最好の语言~~~

      // 重要提醒:即使网页已加载完毕,协程也不会终止,且不依赖客户端,就算关掉浏览器协程任务依然会执行
      go func(writer http.ResponseWriter) {
         fmt.Println("协程任务执行开始")

         // 由于协程不会阻塞网页加载且不依赖客户端,故这里可执行非紧急任务(如记录本次请求日志)或长耗时任务
         max := 5
         for i := 1; i <= max; i++ {
            time.Sleep(time.Second * 1)
            fmt.Printf("协程任务执行中(%d/%d)……\n", i, max)
         }

         // 重要提醒:不能在协程里调用writer.Write(),因为协程是异步的,可能服务器已经返回响应内容了协程还没启动
         _, err := writer.Write([]byte("从入门到放弃"))
         if err != nil {
            fmt.Println("在协程里调用writer.Write()出错:" + err.Error())
            // 在协程里调用writer.Write()出错:http: wrote more than the declared Content-Length
         }

         fmt.Println("协程任务执行完毕")
      }(writer)
   })

   // 启动HTTP服务器
   err := http.ListenAndServe(":8080", nil)
   if err != nil {
      panic("启动HTTP服务器失败:" + err.Error())
   }
}

// ========== 总结 ========== //
// 1、响应HTTP请求有顺序要求,建议按照(设置响应头)→(设置HTTP状态码)→(设置要输出的内容)顺序依次执行。

Copyright © 2024 码农人生. All Rights Reserved