Go中遇到http code 206和302的获取数据的解决方案

简介: 文章提供了解决Go语言中处理HTTP状态码206(部分内容)和302(重定向)的方案,包括如何获取部分数据和真实请求地址的方法,以便程序员能快速完成工作,享受七夕时光。

http code 206含义是什么

一句话,http code 206标识请求已成功被服务器接收、理解、并接受,服务端只成功处理或返回了部分数据(Partial Content)。

为什么会出现http code 206

第一种情况

客户端请求头发送了部分请求的标识,且服务端支持Range数据。
客户端表明自己只需要目标URL上的部分资源的时候,可以带header里面指定Range字段来表明要获取哪一部分数据,如下面的例子:
req
上例中请求Header Range指明了要获取0-数据,即从0开始的全部数据。

Range头域可以请求实体的一个或者多个子范围,Range的值为0表示第一个字节,也就是Range计算字节数是从0开始的:
表示头500个字节:Range: bytes=0-499
表示第二个500字节:Range: bytes=500-999
表示最后500个字节:Range: bytes=-500
表示500字节以后的范围:Range: bytes=500-
第一个和最后一个字节:Range: bytes=0-0,-1
也可以同时指定几个范围:Range: bytes=500-600,601-999

上例中响应Header Content-Range指明了返回了2027665byte中的0-1048575的部分,也就是说还有后面的一部分没有返回回来,需要客户端进行第二次请求,并将Range写成1048576-2027665即可最终获取到全部数据。由此可以看出206 code可以很好的实现断点续传。

The Content-Range response HTTP header indicates where in a full body message a partial message belongs.

第二种情况

此种情况在爬取别人家的网站时更容易遇到,他们的服务端并不一定支持206 code而是当发现你是外部的非法调用时只给你在业务逻辑上返回了部分数据(比如完成应该返回10条数据,但是只给你返回5条),并且将响应码故意设置成了206。例如下面的例子:

➜  ~ curl -I https://dappradar.com/v2/api/dapps?params=UkdGd2NGSmhaR0Z5Y0dGblpUMHhKbk5uY205MWNEMXRZWGdtWTNWeWNtVnVZM2s5VlZORUptWmxZWFIxY21Wa1BURW1jbUZ1WjJVOWJXOXVkR2dtYzI5eWREMTFjMlZ5Sm05eVpHVnlQV1JsYzJNbWJHbHRhWFE5TWpZPQ==
HTTP/2 206
date: Thu, 04 Aug 2022 09:36:28 GMT
content-type: application/json
cache-control: private, must-revalidate
cache-control: no-cache
pragma: no-cache
expires: -1
x-cache-status: EXPIRED
x-cache-status: EXPIRED
x-frame-options: SAMEORIGIN
cf-cache-status: DYNAMIC
expect-ct: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
server: cloudflare
cf-ray: 73564f0f483a3c34-HKG

或者IDE调试
206
可见上面的命令虽然返回206 code,当时并没有出现Content-Range字段。主要是因为这个请求是非法的请求,服务端给强制将本来是200的code设置为了206

如何解决http code 206只返回部分内容的问题

第一种情况的解决

从上面的原理已经看的比较明白了,就是直接按照byte的位置逐次发送请求即可,此时服务端也回返回对应byte返回的数据。这的注意的是每次不要超过服务端最多支持返回的bytes.

第二种情况的解决

这种情况解决方案就是,将自己的请求修改成合法的请求后再次调用就直接返回全部数据,且返回code变成了200. 例如针对上面的爬虫问题,经过尝试发现是因为在请求的Header缺少了cookie字段,添加上之后就可以正确的返回全部数据了。
在这里插入图片描述
上面的请求加上cookie后就正确的返回了全部数据,并且变成了code 200。

http client调用返回了code 302之该如何获取到真实的请求地址

http code 302含义是什么

302状态码表示重定向,浏览器在拿到服务器返回的这个状态码后会自动跳转到一个新的URL地址,这个地址可以从响应的Location首部中获取(用户看到的效果就是他输入的地址A瞬间变成了另一个地址B。

http code 302与301区别

301是永久重定向,而302是临时重定向。301适合做永久重定向; 302适合做临时的跳转。
301的定义:301 Moved Permanently 被请求的资源已永久移动到新位置,并且将来任何对此资源的引用都应该使用本响应返回的若干个URI之一。如果可能,拥有链接编辑功能的客户端应当自动把请求的地址修改为从服务器反馈回来的地址。除非额外指定,否则这个响应也是可缓存的。
302的定义:302 Found 请求的资源现在临时从不同的URI响应请求。由于这样的重定向是临时的,客户端应当继续向原有地址发送以后的请求。只有在Cache-Control或Expires中进行了指定的情况下,这个响应才是可缓存的。

服务器端跳转就是指地址栏内容不变(客户端浏览器的地址栏不会显示目标地址的URL),客户端请求到达以后,服务器发现当前资源给不出回应,在服务器内部请求另一个资源的跳转。所以跳转与否客户端不知道,属于一次请求。
客户端跳转是指地址栏内容发生改变,客户端再根据服务器端给的响应中的URL再向服务器发送请求,所以是两次请求。客户端请求到达服务端,服务端返回一个 “去访问其他链接” 的回应。

如何获取http code 302真实的跳转地址

302重定向(redirect),那么针对这种方式,go语言默认自动执行redirect的,所以没办法使用get请求获取真实地址对于第一条描述的,在302重定向的时候,真实地址在response的location中。
go语言中,默认是支持10层redirect,所以,除非跳出,否则会redirect 到第10层才退出,然而也是可以自定义的

方法一是通过GET来获取真正的Url
func ParseRealUrl(originUrl string) string {

    client := &http.Client{
        Timeout:       15 * time.Second,
        CheckRedirect: checkRedirect,
    }
    req, err1 := http.NewRequest("GET", originUrl, nil)
    req.Header.Set("user-agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.64 Safari/537.36")
    if err1 != nil {
        log.Sugar().Errorf("NewRequest err:%#v", err1.Error())
        return ""
    }
    resp, err2 := client.Do(req)
    //注意⚠️这里不能err2 != nil而直接返回,这里就是checkRedirect通过返回错误进行的拦截
    if err2 != nil {
        log.Sugar().Errorf("Do err:%#v", err2.Error())
    }
    respUrl, err3 := resp.Location()
    if err3 != nil {
        log.Sugar().Errorf("Location err:%#v", err3.Error())
        return ""
    }
    return respUrl.String()
}

func checkRedirect(req *http.Request, via []*http.Request) error {
    if len(via) >= 1 {
        return fmt.Errorf("checkRedirect len(via) >= 1")
    }
    return nil
}

创建client的时候,指定CheckRedirect为自己重写的myCheckRedirect方法,指定“len(via) >= 1”,即第一次redirect就停止,就可以获取到真实的URL了。

方法二是通过HEAD来获取真正的Url

func ParseRealUrl(originUrl string) string {

    client := &http.Client{
        Timeout:       15 * time.Second,
        CheckRedirect: checkRedirect,
    }
    req, err1 := http.NewRequest("HEAD", originUrl, nil)
    req.Header.Set("user-agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.64 Safari/537.36")
    if err1 != nil {
        log.Sugar().Errorf("NewRequest err:%#v", err1.Error())
        return ""
    }
    resp, err2 := client.Do(req)
    if err2 != nil {
        log.Sugar().Infof("Do err:%#v", err2.Error())
    }
    return resp.Header.Get("Location")
}

func checkRedirect(req *http.Request, via []*http.Request) error {
    if len(via) >= 1 {
        return fmt.Errorf("checkRedirect len(via) >= 1")
    }
    return nil
}

浪漫🌹提醒:翻过技术这座山,你可以与TA浪起来了~

参考

HTTP 206 获取文件部分内容和范围请求
如何处理http返回类型为206的数据
断点续传服务端处理(http206)
HTTP状态码206
面试连环炮系列(十四): HTTP状态码302的跳转逻辑
go自定义http请求,捕获302重定向

相关文章
|
3月前
|
JSON 安全 前端开发
类型安全的 Go HTTP 请求
类型安全的 Go HTTP 请求
|
3天前
|
消息中间件 测试技术
通过轻量消息队列(原MNS)主题HTTP订阅+ARMS实现自定义数据多渠道告警
轻量消息队列(原MNS)以其简单队列模型、轻量化协议及按量后付费模式,成为阿里云产品间消息传输首选。本文通过创建主题、订阅、配置告警集成等步骤,展示了该产品在实际应用中的部分功能,确保消息的可靠传输。
14 2
|
14天前
|
网络协议 安全 Go
Go语言进行网络编程可以通过**使用TCP/IP协议栈、并发模型、HTTP协议等**方式
【10月更文挑战第28天】Go语言进行网络编程可以通过**使用TCP/IP协议栈、并发模型、HTTP协议等**方式
43 13
|
2月前
|
存储 JSON Go
在Gin框架中优雅地处理HTTP请求体中的JSON数据
在Gin框架中优雅地处理HTTP请求体中的JSON数据
|
2月前
|
JSON JavaScript 前端开发
Haskell中的数据交换:通过http-conduit发送JSON请求
Haskell中的数据交换:通过http-conduit发送JSON请求
|
3月前
|
存储 算法 Java
Go 通过 Map/Filter/ForEach 等流式 API 高效处理数据
Go 通过 Map/Filter/ForEach 等流式 API 高效处理数据
|
2月前
|
存储 JSON API
Python编程:解析HTTP请求返回的JSON数据
使用Python处理HTTP请求和解析JSON数据既直接又高效。`requests`库的简洁性和强大功能使得发送请求、接收和解析响应变得异常简单。以上步骤和示例提供了一个基础的框架,可以根据你的具体需求进行调整和扩展。通过合适的异常处理,你的代码将更加健壮和可靠,为用户提供更加流畅的体验。
159 0
|
3月前
|
存储 负载均衡 算法
[go 面试] 一致性哈希:数据分片与负载均衡的黄金法则
[go 面试] 一致性哈希:数据分片与负载均衡的黄金法则
|
3月前
|
消息中间件 Kafka Go
从Go channel中批量读取数据
从Go channel中批量读取数据
|
3月前
|
数据采集 网络协议 测试技术
使用Go Validator在Go应用中有效验证数据
使用Go Validator在Go应用中有效验证数据