go

【Go Web开发】HTTP服务器基础

2022-01-12  本文已影响0人  Go语言由浅入深

简单的HTTP服务器

现在项目的框架结构已经就绪,让我们将注意力集中在启动和运行HTTP服务器上。

首先,我们将服务器配置为只有一个路由:/v1/healthcheck。这条路由将返回有关API服务的一些基本信息,包括其当前版本号和操作环境(开发、测试、生产等)。

URL模式 Handler 操作
/v1/healthcheck healthcheckHandler 显示应用信息

如果你跟着本文操作,下面打开cmd/api/main.go文件用以下代码替换’hello world‘应用:

File:main.go

package main

import (
    "flag"
    "fmt"
    "log"
    "net/http"
    "os"
    "time"
)

// 声明一个包含应用程序版本号的字符串. 在本书的后面,我们将在构建时自动生成它
//但是现在我们只将版本号存储为一个硬编码的全局常量.
const version = "1.0.0"

// 定义一个配置结构体来保存应用程序的所有配置设置.
//目前,唯一的配置是服务器监听的端口和应用程序的环境名称 (开发, 预发, 生成等等)
//将从命令行参数中读取这些配制信息
type config struct {
    port int
    env  string
}

// 定义一个application结构体来保存HTTP处理程序的依赖项
//目前,这只包含配置实例的一个副本和日志对象,但随着项目深入会增加更多内容
type application struct {
    config config
    logger *log.Logger
}

func main() {
    // 声明一个配置结构体实例
    var cfg config

    // 从命令行参数中将port和env读取到配制结构体实例当中。
    //默认端口使用4000以及环境信息使用开发环境development
    flag.IntVar(&cfg.port, "port", 4000, "API server port")
    flag.StringVar(&cfg.env, "env", "development", "Environment (development|staging|production)")
    flag.Parse()

    // 初始化日志对象,将消息写入到标准输出。 以当前日期和时间为前缀
    logger := log.New(os.Stdout, "", log.Ldate|log.Ltime)

    // 声明应用程序结构的实例, 包含配制对象实例和日志对象。
    app := &application{
        config: cfg,
        logger: logger}

    // 申明一个新的servemux并添加/v1/healthcheck路由,将请求路由到即将实现的handler处理程序中去
    mux := http.NewServeMux()
    mux.HandleFunc("/v1/healthcheck", app.healthcheckHandler)

    // 使用一些合理的超时设置声明HTTP服务器
    //使用配制对象中的端口好以及创建的servemux作为handler
    srv := &http.Server{
        Addr:         fmt.Sprintf(":%d", cfg.port),
        Handler:      mux,
        IdleTimeout:  time.Minute,
        ReadTimeout:  10 * time.Second,
        WriteTimeout: 30 * time.Second,
    }

    // 启动HTTP服务
    logger.Printf("starting %s server on %s", cfg.env, srv.Addr)
    err := srv.ListenAndServe()
    logger.Fatal(err)
}

创建healthcheck处理程序

接下来我们需要创建用于响应HTTP请求的healthcheckHandler方法。现在,我们将保持这个处理程序中的逻辑非常简单,只让它返回一个包含三段信息的纯文本响应:

继续创建cmd/api/healthcheck.go文件:

$ touch cmd/api/healthcheck.go

添加如下代码到文件中:

File:cmd/api/healthcheck.go

package main

import (
    "fmt"
    "net/http"
)

//申明一个handler返回应用程序状态,操作环境和版本
func (app *application) healthcheck(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintln(w, "status: available")
    fmt.Fprintf(w, "environment %s\n", app.config.env)
    fmt.Fprintf(w, "version: %s\n", version)
}

这里需要指出的重要一点是,healthcheckHandler是作为application结构体上的一个方法实现的.

这是一种有效且惯用的方法,使我们的处理程序可以使用依赖项,而不需要借助全局变量或闭包——当我们在main()中初始化healthcheckHandler时,任何依赖项都可以简单地作为一个字段包含在应用程序结构中。

我们可以看到上面的代码中已经使用了这个模式,其中通过调用app.config.env从应用程序结构中检索操作环境名称。

演示

代码我们来试试接口,请确保你的所有更改都已保存,然后再次使用go run命令来执行cmd/api包中的代码。您应该会看到一条日志消息,确认HTTP服务器正在运行,类似如下:

 $ go run ./cmd/api
2021/11/15 19:42:50 starting development server on :4000

当服务器运行时,继续尝试在浏览器中访问localhost:4000/v1/healthcheck。你应该从healthcheckHandler得到如下响应:



或者,您可以使用curl工具从您的终端发出请求:

$ curl -i localhost:4000/v1/healthcheck
HTTP/1.1 200 OK
Date: Mon, 05 Apr 2021 17:46:14 GMT Content-Length: 58
Content-Type: text/plain; charset=utf-8
status: available environment: development version: 1.0.0

注意:上面命令中的-i标志表示curl显示HTTP响应头和响应体。

如果你想验证在命令行参数是否正常工作,通过指定port和env值然后在启动服务器。你应该可以看到日志信息,如下:

 $ go run ./cmd/api -port=3030 -env=production
2021/04/05 19:48:34 starting production server on :3030

附加说明

API版本

支持真实业务和用户的api经常需要随着时间的推移而更改其功能和API——有时是以一种向后不兼容的方式。因此,为了避免客户端出现问题和困惑,最好实现某种形式的API版本控制。

通常有两种方法来实现:

1、通过给所有url加上API版本的前缀,比如/v1/healthcheck或/v2/healthcheck。

2、通过在请求和响应中使用自定义的Accept和Content-Type头来传递API版本,比如Accept: application/vnd.greenlight-v1。

从HTTP语义的角度来看,使用请求头来传递API版本是一种“更纯粹”的方法。但从用户体验的角度来看,使用URL前缀可能更好。开发者一眼就能看出API的版本,也意味着可以使用常规的浏览器来访问API(如果放在请求头就更不方便)。

在整个应用中,我们将通过在所有URL路径前加上/v1/来对API进行版本化,就像我们在本章中对/v1/healthcheck接口所做的那样。

上一篇下一篇

猜你喜欢

热点阅读