自学内容网 自学内容网

当okhttp网络库遇到不规范的http状态码

如题,最近工作遇到的问题,我们的 Android 应用网络请求埋点报表,收集到了奇怪的网络请求异常;通过日志收集与分析,确定到是服务器返回了不规范的状态码所导致。
在这里插入图片描述
如上是根据线上的业务场景,本地写个简单的MockServer 以及一个简单的 Java 应用(使用 okhttp),重刻出的现场后 client 端的异常堆栈。我们可以看到 okhttp 对于非法的不规范的响应码,直接就抛出ProtocolException,中断http 响应报文的解析,通过onFailure回调通知上层调用者
在这里插入图片描述

示例代码

server

from flask import Flask, jsonify, make_response

app = Flask(__name__)

@app.route('/')
def not_found():
    response = {
        'error': 'Not found'
    }
    return make_response(jsonify(response), 36)  # HTTP 30

if __name__ == '__main__':
    app.run(port=8080)

client

package com.luo;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.Call;
import okhttp3.Callback;

import java.io.IOException;

public class OkHttpGetRequestExample {

    public static void main(String[] args) {
        OkHttpClient client = new OkHttpClient();

        Request request = new Request.Builder()
                .url("http://127.0.0.1:8080/")
                .build();

        // 异步请求
        client.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                e.printStackTrace();
                System.out.println("请求失败:" + e.getMessage());
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                if (response.isSuccessful()) {
                    System.out.println("请求成功:" + response.body().string());
                } else {
                    System.out.println("请求失败:" + response.code());
                }
            }
        });
    }
}

其它

如果上面那个 Server 的代码使用 python 自带的网络组件,唯一的区别是返回的响应行,只有响应码/状态码 36 ,没有状态消息
在这里插入图片描述

from http.server import BaseHTTPRequestHandler, HTTPServer
import json

class SimpleHTTPRequestHandler(BaseHTTPRequestHandler):

    def do_GET(self):
        # 创建一个字典,包含要返回的 JSON 数据
        data = {
            'status': 'fail',
            'message': 'This is a JSON response',
            'code': 36
        }
        # 将字典转换为 JSON 字符串
        json_data = json.dumps(data)
        # 设置响应状态码为 200
        self.send_response(36)
        # 设置响应头
        self.send_header('Content-type', 'application/json')
        self.end_headers()
        # 设置响应内容
        self.wfile.write(json_data.encode('utf-8'))

# 设置服务器的端口号
server_address = ('', 8080)
httpd = HTTPServer(server_address, SimpleHTTPRequestHandler)
print("Starting httpd on port", server_address[1])
# 开始监听
httpd.serve_forever()

如果你用 Node js 来写上面的 Mockserver,当 server 收到请求,服务就crash 了,因为代码中设置了个非法的响应码
在这里插入图片描述
对应 client 那边侧是EOFException的出现
在这里插入图片描述

上面的 Mockserver 的代码如下:

const http = require('http');

const server = http.createServer((req, res) => {
    // 检查请求是否为GET方法
    if (req.method === 'GET') {
        // 设置响应头部为JSON
        res.setHeader('Content-Type', 'application/json');
        // 设置HTTP状态码为36
        res.statusCode = 36;
        // 发送响应数据
        res.end(JSON.stringify({
            status: 0,
            message: 'Not Found'
        }));
    } else {
        // 如果不是GET请求,返回404
        res.statusCode = 404;
        res.end(JSON.stringify({
            status: -1,
            message: 'Not Found'
        }));
    }
});

const PORT = 8080; // 你可以选择任何未被占用的端口
server.listen(PORT, () => {
    console.log(`Server is running on http://localhost:${PORT}`);
});

http 协议规范

让我们一起来复习 http协议规范中的 http 响应头这块的内容

状态行

HTTP响应行,也称为状态行,是HTTP响应报文的第一行。它包含了以下三个部分:

HTTP协议版本:指定了用于通信的HTTP协议的版本,如HTTP/1.1或HTTP/2。这告诉客户端服务器支持的HTTP版本。

状态码:一个三位数字,表示请求的结果。状态码分为五类:

1xx:指示信息,表示请求已接收,继续处理。
2xx:成功,表示请求已被成功接收、理解、并接受。
3xx:重定向,表示需要后续操作以完成请求。
4xx:客户端错误,表示请求包含语法错误或无法完成请求。
5xx:服务器错误,表示服务器在处理请求的过程中发生了错误。

状态消息:状态码的简短描述,通常是状态码的文字解释,如"OK"、"Not Found"、"Internal Server Error"等。状态消息是可选的,但通常包含在响应中以提高可读性。

状态码

HTTP状态码总是三位数字,这是HTTP协议规范的一部分。如果服务器返回的状态码不是三位数字,那么这个响应是不符合HTTP协议标准的,客户端(如浏览器或其他HTTP客户端)可能会无法正确解析和理解该响应。

HTTP状态码的第一位数字定义了响应的类别:

1xx(信息性状态码):表示接收的请求正在处理。
2xx(成功状态码):表示请求正常处理完毕。
3xx(重定向状态码):需要后续操作才能完成这一请求。
4xx(客户端错误状态码):表示请求包含语法错误或无法完成。
5xx(服务器错误状态码):服务器在处理请求的过程中发生了错误。
如果遇到非标准的响应,客户端可能会采取以下措施:

显示错误:对于无法识别的响应,客户端可能会向用户显示错误信息。
忽略响应:在某些情况下,客户端可能会忽略该响应并尝试重新发起请求。
记录日志:客户端可能会记录日志信息,以供后续分析问题。
如果你是服务器端开发者,应确保返回的HTTP状态码符合标准。如果你是客户端开发者,应确保你的客户端能够妥善处理非标准响应。

值得一提的是,虽然在理论上状态码应该是三位数字,但实际上,几乎所有的HTTP客户端和服务器都对非标准的响应码有一定的容忍度。例如,如果服务器返回了200 OK后跟了额外的非标准文字,很多客户端可能会忽略这些额外的文字。但是,遵循标准总是最佳实践。

调试技巧

上面 okhttp 抛出异常的情况下,我们可以在readResponseHeaders方法设置一个断点,然后通过headersReader.readLine() 读出整个报文
在这里插入图片描述
在这里插入图片描述

直接用浏览器访问 http://localhost:8080/ 可以在网络调试窗口看到对应的报文(响应头)
在这里插入图片描述


原文地址:https://blog.csdn.net/SCHOLAR_II/article/details/142534723

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!