Golang中错误处理的实现方法与最佳实践

Golang中错误处理的实现方法与最佳实践 我有一个循环,向切片中的网站发送GET请求,这些网站属于结构体类型。

我将分享与遇到的问题相关的最后两个函数。

第一个函数是检查器,接收"[]accountinfo"类型的输入:

func checker(info []accountinfo) {
	var request string
	var statusCode int

	for i := range info {
		request = fmt.Sprintf("https://%s/", info[i].domain)
		statusCode = getRequest(request)

		if statusCode == 200 {
            fmt.Println("that's ok")
		} else {
			fmt.Println(info[i].domain, "not found or content is not valid")
		}

	}
}

第二个函数发送请求并返回状态码:

func getRequest(url string) int {

	http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true}

	resp, err := http.Get(url)
	if err != nil {
		log.Println(err)
	}

	defer resp.Body.Close()

	return resp.StatusCode
}

我遇到了这个错误:

2019/09/05 17:52:18 Get https://something.com/: dial tcp <ip>:443: connect: connection refused
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x40 pc=0x124eebf]

goroutine 1 [running]:
main.getRequest(0xc0000ae0e0, 0x1a, 0x0)
	/Users/ozan/monitoring-development/v2/main.go:147 +0xff
main.checker(0xc000584000, 0x8d, 0x100)
	/Users/ozan/monitoring-development/v2/main.go:110 +0xd3
main.main()
	/Users/ozan/monitoring-development/v2/main.go:67 +0x4dd
exit status 2

Stackoverflow上的人提醒我,由于响应实际上没有完成,所以无法返回状态码,他们的方式让人不太愉快。 我希望这里的人不要像Stackoverflow上的人那样。我真的不明白作为一个新手为什么要被"大师们"攻击。

在这种情况下,我有哪些选择?我想创建一个自定义响应,比如如果连接失败就返回"自定义响应", 然后在第一个函数中,如果返回=="自定义响应"就跳过那个网站。 但我不知道如何实现,或者这是否是一个合适的方法……

如果你能用"愉快的方式"给我一些建议,我会非常高兴。


更多关于Golang中错误处理的实现方法与最佳实践的实战教程也可以访问 https://www.itying.com/category-94-b0.html

3 回复

非常感谢,这真的很有帮助。

更多关于Golang中错误处理的实现方法与最佳实践的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


检查你的代码,你除了写入日志外没有进行空值处理,而且代码继续执行(defer resp.Body.Close() return resp.StatusCode)

我虽然不是Go语言的"大师",但认为你可以通过返回错误来更符合Go语言的风格。我的意思是:

func getRequest(url string) (int, error) {
	http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true}

	if resp, err := http.Get(url);
	if err != nil {
		return -1, err  // 使用特定的状态码
	}

	defer resp.Body.Close()
	return resp.StatusCode, nil
}

在检查函数中:

func checker(info []accountinfo) {
	var request string
	var statusCode int
	var err error
	
	for i := range info {
		request = fmt.Sprintf("https://%s/", info[i].domain)
		statusCode, err = getRequest(request)

		if err != nil {
		  fmt.Println(err)
		} else {  
			if statusCode == 200 {
				fmt.Println("正常")
			} else {
				fmt.Println(info[i].domain, "未找到或内容无效")
			}
		}
	}
}

希望对你有帮助

在Go语言中,错误处理是一个核心特性,你的代码确实存在几个关键问题。主要问题是在getRequest函数中,当http.Get返回错误时,你仍然尝试访问resp.StatusCode,而此时resp可能为nil,导致空指针解引用。

以下是修复后的代码实现:

func checker(info []accountinfo) {
    for i := range info {
        request := fmt.Sprintf("https://%s/", info[i].domain)
        statusCode := getRequest(request)

        if statusCode == 200 {
            fmt.Println("that's ok")
        } else if statusCode == -1 {
            fmt.Println(info[i].domain, "connection failed")
        } else {
            fmt.Println(info[i].domain, "not found or content is not valid, status:", statusCode)
        }
    }
}

func getRequest(url string) int {
    http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true}

    resp, err := http.Get(url)
    if err != nil {
        log.Printf("Request failed for %s: %v", url, err)
        return -1 // 自定义错误码表示连接失败
    }
    defer resp.Body.Close()

    return resp.StatusCode
}

更符合Go惯用法的版本是返回错误:

func checker(info []accountinfo) {
    for i := range info {
        request := fmt.Sprintf("https://%s/", info[i].domain)
        statusCode, err := getRequest(request)

        if err != nil {
            fmt.Printf("%s: %v\n", info[i].domain, err)
            continue
        }

        if statusCode == 200 {
            fmt.Println("that's ok")
        } else {
            fmt.Printf("%s: status code %d\n", info[i].domain, statusCode)
        }
    }
}

func getRequest(url string) (int, error) {
    http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true}

    resp, err := http.Get(url)
    if err != nil {
        return 0, fmt.Errorf("connection failed: %w", err)
    }
    defer resp.Body.Close()

    return resp.StatusCode, nil
}

对于生产环境,建议添加超时控制:

func getRequest(url string) (int, error) {
    client := &http.Client{
        Timeout: 10 * time.Second,
        Transport: &http.Transport{
            TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
        },
    }

    resp, err := client.Get(url)
    if err != nil {
        return 0, fmt.Errorf("request failed: %w", err)
    }
    defer resp.Body.Close()

    return resp.StatusCode, nil
}

这些修改解决了空指针异常问题,提供了清晰的错误处理路径,并保持了代码的可读性。

回到顶部