Rust:axum与client之间的请求与响应的流式转发

有时候我们的后端API会对其他地方发送http请求来获取数据。如果请求体过大(如上传文件)或响应体过大(如下载文件),就需要使用流式传输以节约后端服务器内存(否则后端服务器可能会先把整个请求下载到自己的内存里面然后再发送)。

转发reqwest client的响应

可以使用流对reqwest的响应进行转发。官方例子:axum/examples/reqwest-response/src/main.rs at main · tokio-rs/axum · GitHub

转发请求到client

这里的原理是使用hyper库的Body trait进行转发。Body trait是hyper给出的面向流式传输的东西,使得基于hyper的http库的请求和响应都是可以随意转发的。然而,axum依赖hyper v1.2,但reqwest依赖hyper v0.14(提供的是Body struct而不是Body trait),不兼容,因此不得不找较新的hyper client。

参考了axum反向代理的例子reverse-proxy,使用较底层的hyper客户端hyper_util::client::legacy::Client<HttpConnector, Body>来发起请求。可以随意调整uri和headers。

client的返回值类型是Response<Incoming>,不能直接读取。将其into_response直接返回给前端的话,是可以被前端读出来的。若想在后端直接读取的话,GPT4给出的0.14版本的hyper的解决方案是:

use hyper::Client;
use hyper::client::HttpConnector;
use axum::body::Body;
use serde_json::from_slice;
use futures::TryStreamExt; // 提供了 stream 的扩展方法,如 and_then
use hyper::{Response, StatusCode};

async fn fetch_ipfs_add_response() -> Result<IpfsAddResponse, Box<dyn std::error::Error>> {
    let client = Client::new();
    let uri = "http://localhost:5001/api/v0/add".parse()?;
    let response = client.get(uri).await?;

    match response.status() {
        StatusCode::OK => {
            // 将响应体聚合成一个完整的bytes
            let bytes = hyper::body::to_bytes(response.into_body()).await?;
            // 将bytes解析为IpfsAddResponse
            let ipfs_response = from_slice::<IpfsAddResponse>(&bytes)?;
            Ok(ipfs_response)
        },
        _ => Err(Box::new(std::io::Error::new(std::io::ErrorKind::Other, "Failed to fetch"))))
    }
}

但在hyper的1.x版本里,有些东西被改掉了,个人摸索出来的解决方案是:

// read body  
let res = raw_hyper_client  
	.request(req)  
	.await.map_err(|_| http::StatusCode::BAD_REQUEST)?;  
let body = res.into_body().collect();  
let body = body.await.unwrap();  
let body = body.to_bytes();  
let body: dtos::IpfsAddFileResponse = serde_json::from_slice(body.as_ref()).unwrap();  
info!("Add file succeed. {:?}", body);  
  • 13
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值