У меня есть следующий код:
#[tokio::main]
async fn main() {
let app = Router::new()
.route("/", get(handler))
.route("/custom/*key", get(proxy).post(proxy));
// ...
}
pub async fn proxy(mut req: Request<Body>) -> Result<Response<Body>, StatusCode> {
let path_query = req
.uri()
.path_and_query()
.map_or_else(|| req.uri().path(), |v| v.as_str());
let uri = format!("http://localhost:9000/{}", path_query);
*req.uri_mut() = Uri::try_from(uri).unwrap();
let http_client = reqwest::Client::new();
let res = http_client.execute(req).await.unwrap()
if res.status().is_server_error() {
return Err(StatusCode::INTERNAL_SERVER_ERROR);
}
Ok(res)
}
Но очевидно, что это не работает:
error[E0308]: mismatched types
|
35 | let res = http_client.execute(req).await.unwrap();
| ------- ^^^ expected `Request`, found `Request<Incoming>`
| |
| arguments to this method are incorrect
|
= note: `Request<Incoming>` and `reqwest::Request` have similar names, but are actually distinct types
note: `Request<Incoming>` is defined in crate `http`
--> C:\Users\Fred\.cargo\registry\src\index.crates.io-6f17d22bba15001f\http-1.1.0\src\request.rs:158:1
|
158 | pub struct Request<T> {
| ^^^^^^^^^^^^^^^^^^^^^
note: `reqwest::Request` is defined in crate `reqwest`
--> C:\Users\Fred\.cargo\registry\src\index.crates.io-6f17d22bba15001f\reqwest-0.12.4\src\async_impl\request.rs:22:1
|
22 | pub struct Request {
| ^^^^^^^^^^^^^^^^^^
note: method defined here
--> C:\Users\Fred\.cargo\registry\src\index.crates.io-6f17d22bba15001f\reqwest-0.12.4\src\async_impl\client.rs:1965:12
|
1965 | pub fn execute(
| ^^^^^^^
и:
error[E0308]: mismatched types
|
43 | Ok(res)
| -- ^^^ expected `Response<Incoming>`, found `Response`
| |
| arguments to this enum variant are incorrect
|
= note: `reqwest::Response` and `Response<Incoming>` have similar names, but are actually distinct types
note: `reqwest::Response` is defined in crate `reqwest`
--> C:\Users\Fred\.cargo\registry\src\index.crates.io-6f17d22bba15001f\reqwest-0.12.4\src\async_impl\response.rs:29:1
Я использую axum 0.7.5 и reqwest 0.12 (оба на http 1).
Можно ли заставить его работать?

Как говорится в ошибке, запрос Запрос не то же самое, что аксум Запрос , который является псевдонимом Request из ящика http. Однако они пытаются выразить одну и ту же информацию, поэтому нам просто нужно преобразовать одно в другое.
Существует реализация TryFrom для создания запроса Request из http Request, однако есть загвоздка в том, что axum Body не конвертируется в запрос Body. Но на http .map() есть Request функция, которая может трансформировать тип телосложения самостоятельно.
Мы можем избежать буферизации всего тела запроса, сопоставляя его с потоками байтов следующим образом:
let req = req.map(|body| reqwest::Body::wrap_stream(body.into_data_stream()));
Однако снова есть загвоздка: в версиях, о которых вы спрашиваете, .into_data_stream() из тела axum не реализует Sync то, что требует Body::wrap_stream reqwest. К счастью, есть обходной путь: предыдущие версии reqwest не требовали Sync, но могли сохранить свою Body реализацию Sync, используя SyncStream из ящика sync-wrapper (прочитайте их документацию, если хотите узнать, как это работает ).
Таким образом, полное преобразование запроса выглядит так:
use sync_wrapper::SyncStream;
let req = req.map(|body| reqwest::Body::wrap_stream(SyncStream::new(body.into_data_stream())));
let req = reqwest::Request::try_from(req).unwrap();
let res = http_client.execute(req).await.unwrap();
Вызов try_from теоретически может завершиться неудачно из-за повторного анализа URL-адреса из http Uri в запрос Url (псевдоним из ящика URL-адресов).
Преобразование из reqwest Response обратно в axum/http Response не так уж и плохо, поскольку reqwest Body реализует признак http Body, а axum Body может быть создан с помощью чего-либо, реализующего признак Body. И ни одно из этих преобразований не может потерпеть неудачу. Вот как это выглядит:
let res = axum::http::Response::from(res);
let res = res.map(|body| Body::new(body));
Разве использование sync_wrapper::SyncStream не является своего рода «хаком»?
Как использовать его без необходимости Sync?
@FredHors Потоковая передача должна быть предпочтительнее даже для небольших полезных нагрузок. Вам нужно будет буферизовать тело только в том случае, если вы захотите его осмотреть. Кроме того, есть много способов получить его и отправить как Vec<u8> или Bytes, что в принципе универсально.
@FredHors Я связал ящик, чтобы вы могли оценить его сами. Вам нужно Sync создать тело запроса, и SyncStream — это совершенно безопасный способ сделать это, поскольку он требует эксклюзивных ссылок и, таким образом, позволяет избежать случаев, когда тип !Sync является проблемой.
Большое спасибо. Что делать, если я хочу буферизовать все тело запроса (они очень маленькие)?