Как вернуть ошибку вызывающей стороне из обработчика с клиентом
Я создал сервер с actix_web, который будет соединяться через GET с другим сервисом, используя клиент actix, и возвращать тело в случае успеха или в случае ошибки. Я смог вернуть тело, но понятия не имею, как вернуть ошибку.
Я перепробовал все, что пришло мне в голову, но ничего не понял...
fn echo_client(client: web::Data<Client>) -> impl Future<Item = HttpResponse, Error = Error> {
client.get("127.0.0.1:9596/echo/javier") // <- Create request builder
.header("User-Agent", "Actix-web")
//.finish().unwrap()
.send() // <- Send http request
.map_err(|_| ())
//.map_err(Error::from)
.and_then(|response| {
response.body().and_then( |body| {
println!("{:?}", body);
Ok(HttpResponse::Ok().body(body))
}).map_err(|error| {
Err(error.error_response())
})
})
}
Этот обработчик я сделал.
0 ответов
Есть три вещи, которые могут потерпеть неудачу:
- Ошибка соединения.
- Не 200-статусный код.
- Резкая остановка в потоке тела.
Чтобы справиться с 1, не map_err
в ()
:
.map_err(|err| match err {
SendRequestError::Connect(error) => {
ErrorBadGateway(format!("Unable to connect to httpbin: {}", error))
}
error => ErrorInternalServerError(error),
})
SendRequestError
перечисляет ошибки, которые могут возникнуть при выполнении клиентских запросов.
Для обработки 2 убедитесь, что вы используете код состояния из ответа клиента:
.and_then(|response| Ok(HttpResponse::build(response.status()).streaming(response))))
Я думаю, что actix-web обрабатывает 3.
Завершите пример, обрабатывая заголовки тоже:
use actix_web::client::{Client, SendRequestError};
use actix_web::error::{ErrorBadGateway, ErrorInternalServerError};
use actix_web::{web, App, Error, HttpResponse, HttpServer};
use futures::future::Future;
fn main() {
HttpServer::new(|| App::new().data(Client::new()).route("/", web::to(handler)))
.bind("127.0.0.1:8000")
.expect("Cannot bind to port 8000")
.run()
.expect("Unable to run server");
}
fn handler(client: web::Data<Client>) -> Box<Future<Item = HttpResponse, Error = Error>> {
Box::new(
client
.get("https://httpbin.org/get")
.no_decompress()
.send()
.map_err(|err| match err {
SendRequestError::Connect(error) => {
ErrorBadGateway(format!("Unable to connect to httpbin: {}", error))
}
error => ErrorInternalServerError(error),
})
.and_then(|response| {
let mut result = HttpResponse::build(response.status());
let headers = response
.headers()
.iter()
.filter(|(h, _)| *h != "connection" && *h != "content-length");
for (header_name, header_value) in headers {
result.header(header_name.clone(), header_value.clone());
}
Ok(result.streaming(response))
}),
)
}
Что на самом деле не удалось:
$ curl -v localhost:8000
* Rebuilt URL to: localhost:8000/
* Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 8000 (#0)
> GET / HTTP/1.1
> Host: localhost:8000
> User-Agent: curl/7.54.0
> Accept: */*
>
< HTTP/1.1 502 Bad Gateway
< content-length: 50
< content-type: text/plain
< date: Sun, 07 Jul 2019 21:01:39 GMT
<
* Connection #0 to host localhost left intact
Unable to connect to httpbin: SSL is not supported
Добавьте ssl как функцию в Cargo.toml, чтобы исправить ошибку соединения:
actix-web = { version = "1.0", features=["ssl"] }
Затем попробуйте запрос еще раз:
$ curl -v localhost:8000
* Rebuilt URL to: localhost:8000/
* Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 8000 (#0)
> GET / HTTP/1.1
> Host: localhost:8000
> User-Agent: curl/7.54.0
> Accept: */*
>
< HTTP/1.1 200 OK
< transfer-encoding: chunked
< x-frame-options: DENY
< date: Sun, 07 Jul 2019 21:07:18 GMT
< content-type: application/json
< access-control-allow-origin: *
< access-control-allow-credentials: true
< server: nginx
< x-content-type-options: nosniff
< x-xss-protection: 1; mode=block
< referrer-policy: no-referrer-when-downgrade
<
{
"args": {},
"headers": {
"Date": "Sun, 07 Jul 2019 21:07:18 GMT",
"Host": "httpbin.org"
},
"origin": "212.251.175.90, 212.251.175.90",
"url": "https://httpbin.org/get"
}