Создание гипер-сервиса с пользовательским типом ошибки
Я пытаюсь создать REST-сервер с использованием гипер. Для надежной обработки ошибок я бы предпочел, чтобы служба возвращала будущее с пользовательским типом ошибок, охватывающим гипер, дизельные и другие ошибки. К несчастью, hyper::Response
кажется жестко закодировать поток с типом ошибки hyper::error::Error
, который конфликтует с типом ошибки, которую я определил для моего сервиса. Я вижу пару возможных решений:
Сделайте так, чтобы мой сервис возвращал мой тип ошибки, изменив
hyper::Response
, что кажется сложным.Оберните не гипер-ошибки в
hyper::error::Error
, Это кажется хакерским.Что-то другое. Кажется, мне не хватает "правильного" способа сделать это.
Следующий код показывает, что я хочу сделать:
extern crate diesel;
extern crate futures;
extern crate hyper;
use futures::future::{ok, Future};
use hyper::StatusCode;
use hyper::server::{Request, Response, Service};
fn main() {
let address = "127.0.0.1:8080".parse().unwrap();
let server = hyper::server::Http::new()
.bind(&address, move || Ok(ApiService {}))
.unwrap();
server.run().unwrap();
}
pub struct ApiService;
impl Service for ApiService {
type Request = Request;
type Response = Response;
type Error = Error;
type Future = Box<Future<Item = Self::Response, Error = Self::Error>>;
fn call(&self, request: Request) -> Self::Future {
Box::new(ok(Response::new().with_status(StatusCode::Ok)))
}
}
#[derive(Debug)]
pub enum Error {
Request(hyper::Error),
DatabaseResult(diesel::result::Error),
DatabaseConnection(diesel::ConnectionError),
Other(String),
}
// omitted impl of Display, std::error::Error for brevity
Этот код приводит к ошибке компилятора, которая, как я полагаю, заключается в том, что bind
функция требует, чтобы тип ответа имел тело, являющееся потоком с типом ошибки hyper::error::Error
:
error[E0271]: type mismatch resolving `<ApiService as hyper::client::Service>::Error == hyper::Error`
--> src/main.rs:14:10
|
14 | .bind(&address, move || Ok(ApiService {}))
| ^^^^ expected enum `Error`, found enum `hyper::Error`
|
= note: expected type `Error`
found type `hyper::Error`
2 ответа
Поскольку конечная цель сервера - вернуть ответ пользователю, я нашел приемлемое решение - создать finalize
функция, которая преобразует ошибки, возникшие при обработке запроса, в правильно сформированные ответы и обрабатывает эти ошибки как не ошибки с точки зрения гипер. Мне нужно будет конкретизировать эту идею (например, путем передачи гипер-ошибок в качестве ошибок), но я считаю, что основная идея является обоснованной.
Следующий код изменяет код в вопросе, чтобы сделать это:
extern crate diesel;
extern crate futures;
extern crate hyper;
#[macro_use]
extern crate serde_derive;
use futures::future::{ok, Future};
use hyper::StatusCode;
use hyper::server::{Request, Response, Service};
fn main() {
let address = "127.0.0.1:8080".parse().unwrap();
let server = hyper::server::Http::new()
.bind(&address, move || Ok(ApiService {}))
.unwrap();
server.run().unwrap();
}
fn finalize(result: Result<Response, Error>) -> FutureResult<Response, hyper::Error> {
match result {
Ok(response) => ok(response),
Err(error) => {
let response_body =
json!({"status": 500, "description": error.description()}).to_string();
ok(Response::new()
.with_status(StatusCode::InternalServerError)
.with_header(ContentLength(response_body.len() as u64))
.with_body(response_body))
}
}
}
pub struct ApiService;
impl Service for ApiService {
type Request = Request;
type Response = Response;
type Error = hyper::Error;
type Future = Box<Future<Item = Self::Response, Error = Self::Error>>;
fn call(&self, request: Request) -> Self::Future {
let response = Ok(Response::new().with_status(StatusCode::Ok));
Box::new(finalize(response))
}
}
#[derive(Debug)]
pub enum Error {
Request(hyper::Error),
DatabaseResult(diesel::result::Error),
DatabaseConnection(diesel::ConnectionError),
Other(String),
}
// omitted impl of Display, std::error::Error for brevity
Вы можете реализовать std::convert::From
черта для вашего Error
тип. Например, для hyper::Error
дело:
impl From<hyper::Error> for Error {
fn from(error: hyper::Error) -> Self {
Error::Request(error)
}
}