实战:模拟http状态码

169 阅读6分钟

引言 HTTP状态码是客户端(浏览器)和服务器之间进行通信的基础。当浏览器请求网页时,服务器会返回一个状态码,告诉浏览器请求的网页是否成功、失败或有其他问题。在本文中,将讨论HTTP状态码及使用Nginx做代理模拟常见状态码。


本文分为两部分

  1. HTTP状态码基础介绍
  2. HTTP状态码模拟实战

HTTP状态码基础介绍

什么是HTTP状态码?

HTTP状态码是指由Web服务器返回给Web浏览器的3位数字代码。它们指示Web服务器在处理请求时发生了什么。它们帮助我们确定请求是否成功,并在请求失败时提供有关错误的详细信息。HTTP状态码由五个类别组成:信息性状态码(1xx)、成功状态码(2xx)、重定向状态码(3xx)、客户端错误状态码(4xx)和服务器错误状态码(5xx)。

信息性状态码

信息性状态码表示请求已经被接收,继续处理。例如,100状态码表示服务器已经接收了请求头,并且客户端应该继续发送请求主体。

成功状态码

成功状态码表示请求已成功被服务器接收、理解、并接受。例如,200状态码表示请求成功,而204状态码表示请求成功,但没有响应主体来返回。201状态码表示服务器成功创建了一个新的资源,并将其URI作为响应返回。

重定向状态码

重定向状态码表示客户端必须采取进一步的措施才能完成请求。例如,301状态码表示请求的资源已永久移动到新的URL地址,并且搜索引擎应该把旧的URL地址从它们的索引中删除。302状态码表示请求的资源已经暂时移动到新的URL地址。

客户端错误状态码

4xx 客户端错误状态码通常由客户端发起的请求有误造成的,常见的状态码有:

  • 400 Bad Request:客户端发送的请求有误,服务器无法处理;
  • 401 Unauthorized:客户端没有经过身份验证或没有正确的授权信息;
  • 403 Forbidden:客户端已经经过身份验证,但没有访问所请求的资源的权限;
  • 404 Not Found:客户端请求的资源不存在;
  • 405 Method Not Allowed:客户端使用的 HTTP 方法不被允许;
  • 408 Request Timeout:客户端在规定的时间内未向服务器发送请求。

服务器错误状态码

  • 500 Internal Server Error:服务器在处理请求时发生了未知错误;
  • 501 Not Implemented:服务器无法处理客户端的请求,因为请求的功能还没有被实现;
  • 502 Bad Gateway:服务器作为网关或代理,从上游服务器收到了无效的响应;
  • 503 Service Unavailable:服务器暂时无法处理客户端的请求,一般是由于服务器正在进行维护或过载;
  • 504 Gateway Timeout:服务器作为网关或代理,没有在规定的时间内收到上游服务器的响应。

HTTP状态码模拟实战

首先安装Nginx作为网关,代理后端服务器服务,转发我们的请求至相应服务器端口处理。我们主要模拟以下几种常见的状态码 404,500,502,503,504

安装Nginx(Mac版)

命令 brew info nginx 查看本机nginx安装情况

1.png 可以看到nginx未安装,缺少依赖包

安装两个依赖包

命令 brew install openssl@1.1 2.png

命令 brew install pcre2 3.png

使用brew info nginx命令看到依赖包安装完成 4.png

依赖包安装完成后,安装Nginx 5.png

下图中红框框起来的部分需要注意 6.png

我们来构建几个返回状态码的服务请求

package main

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

// 状态码直接返回部分逻辑
func Service200(w http.ResponseWriter, r *http.Request) {
	r.ParseForm() //解析参数
	fmt.Println("Form:", r.Form)
	fmt.Println("path:", r.URL.Path)
	fmt.Println("scheme", r.URL.Scheme)
	fmt.Println(r.Form["url_long"])
	for key, val := range r.Form {
		fmt.Println("key:", key)
		fmt.Println("val:", val)
	}
	fmt.Fprintf(w, "hello,world!")
}

func Service500(w http.ResponseWriter, r *http.Request) {
	r.ParseForm() //解析参数
	for key, val := range r.Form {
		fmt.Println("key:", key)
		fmt.Println("val:", val)
	}
	w.WriteHeader(http.StatusInternalServerError)
	w.Write([]byte("internal server error"))
}

func Service502(w http.ResponseWriter, r *http.Request) {
	r.ParseForm() //解析参数
	for key, val := range r.Form {
		fmt.Println("key:", key)
		fmt.Println("val:", val)
	}
	w.WriteHeader(http.StatusBadGateway)
	w.Write([]byte("bad gateway"))
}

func Service503(w http.ResponseWriter, r *http.Request) {
	r.ParseForm() //解析参数
	for key, val := range r.Form {
		fmt.Println("key:", key)
		fmt.Println("val:", val)
	}
	w.WriteHeader(http.StatusServiceUnavailable)
	w.Write([]byte("service unavailable"))
}

func Service504(w http.ResponseWriter, r *http.Request) {
	r.ParseForm() //解析参数
	for key, val := range r.Form {
		fmt.Println("key:", key)
		fmt.Println("val:", val)
	}
	w.WriteHeader(http.StatusGatewayTimeout)
	w.Write([]byte("gateway time out"))
}

func Logic500(w http.ResponseWriter, r *http.Request) {
	r.ParseForm() //解析参数
	time.Sleep(20 * time.Second)
	if err := OneFunc(); err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		w.Write([]byte("internal server error"))
		return
	}
	w.Write([]byte("ok 200"))
}
func OneFunc() error {
	// 具体业务逻辑
	// 产生一个error
	return errors.New("one error occurred")
}
func Logic502(w http.ResponseWriter, r *http.Request) {
	r.ParseForm() //解析参数

}
func Logic503(w http.ResponseWriter, r *http.Request) {
	r.ParseForm() //解析参数
	time.Sleep(10 * time.Second)
	w.WriteHeader(http.StatusServiceUnavailable)
	fmt.Fprint(w, "Service is temporarily unavailable")
}
func Logic504(w http.ResponseWriter, r *http.Request) {
	r.ParseForm() //解析参数
	timeout := time.After(15 * time.Second) //15s超时时间
	select {
	case <-r.Context().Done():
		return
	case <-timeout:
		w.WriteHeader(http.StatusGatewayTimeout)
		fmt.Fprint(w, "Gateway timeout")
	}
}

func main() {
	http.HandleFunc("/httpcode200", Service200)
	http.HandleFunc("/httpcode500", Service500)
	http.HandleFunc("/httpcode502", Service502)
	http.HandleFunc("/httpcode503", Service503)
	http.HandleFunc("/httpcode504", Service504)
	http.HandleFunc("/logic500", Logic500)
	http.HandleFunc("/logic502", Logic502)
	http.HandleFunc("/logic503", Logic503)
	http.HandleFunc("/logic504", Logic504)
	err := http.ListenAndServe(":9999", nil)
	if err != nil {
		log.Fatal("ListenAndServe : ", err)
	}
}

我们在本地的9999端口,启动后端服务。

服务启动完成之后,我们配置Nginx代理的配置文件(配置文件位置在6.png中) 7.png 命令 nginx -s reload 使nginx重新加载配置文件 命令 brew services start/restart/stop nginx 启动/重启/停止 nginx 服务(nginx服务启动在本地的8080端口)

我们先请求一下后端服务器的服务,观察是否正确运行 8.png

再通过nginx代理来请求我们后端的服务,可以看到nginx直接将后端服务器的返回信息透传过来嘞 9.png

如果我们想让nginx帮我们处理后端返回的状态码,并展示我们自定义的页面信息(如下) 10.png

我们需要找到nginx存放静态资源的根目录,如下图 11.png

在根目录下添加我们自定义的静态页面 12.png

13.png

14.png

网关配置超时等待时间 15.png

超时响应 16.png

请求一个不存在的资源,会返回404 17.png

总结

HTTP状态码是Web开发中的重要组成部分。它们告诉我们请求是否成功、失败或有其他问题。在本文中,介绍了HTTP状态码的五个类别,我们可以测试我们的应用程序在不同的HTTP状态码下的响应,以确保应用程序对各种情况做出适当的响应。

常见问题解答

  1. 什么是HTTP状态码?

HTTP状态码是指由Web服务器返回给Web浏览器的3位数字代码。它们指示Web服务器在处理请求时发生了什么。

  1. HTTP状态码的五个类别是什么?

HTTP状态码由五个类别组成:信息性状态码、成功状态码、重定向状态码、客户端错误状态码和服务器错误状态码。

  1. HTTP状态码模拟的目的是什么?

HTTP状态码模拟的目的是测试我们的应用程序对不同HTTP状态码的响应。

写在最后

感谢大家的阅读,晴天将继续努力,分享更多有趣且实用的主题,如有错误和纰漏,欢迎给予指正。 更多文章敬请关注作者个人公众号 晴天码字