2

我正在尝试使用 hyper 创建一个 REST 服务器。对于稳健的错误处理,我希望服务返回一个包含超、柴油和其他错误的自定义错误类型的未来。不幸的是,hyper::Response似乎用错误类型硬编码了一个流hyper::error::Error,这与我为我的服务定义的错误类型冲突。我看到了几个可能的解决方案:

  1. 通过修改让我的服务返回我的自定义错误类型hyper::Response,这似乎很难。

  2. 将非超错误包装在hyper::error::Error. 这似乎很骇人听闻。

  3. 别的东西。似乎我错过了执行此操作的“正确”方法。

以下代码显示了我想我想做的事情:

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`
4

2 回答 2

5

因为服务器的最终目标是向用户返回响应,所以我找到了一个可接受的解决方案,即创建一个finalize函数,将处理请求时遇到的错误转换为正确格式的响应,并从 hyper 的角度将这些错误视为非错误. 我需要将这个想法充实一些(例如,通过将超错误作为错误传递),但我相信基本想法是合理的。

以下代码修改了问题中的代码以执行此操作:

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
于 2018-03-18T03:47:38.940 回答
-3

您可以std::convert::From为您的Error类型实现特征。例如对于这种hyper::Error情况:

impl From<hyper::Error> for Error {
    fn from(error: hyper::Error) -> Self {
        Error::Request(error)
    }
}
于 2018-03-17T19:28:12.900 回答