我正在尝试使用 hyper 创建 REST 服务器。对于稳健的错误处理,我希望服务返回一个带有自定义错误类型的未来,该类型包含 hyper、Diesel 和其他错误。不幸的是,hyper::Response似乎对错误类型的流进行了硬编码hyper::error::Error,这与我为我的服务定义的错误类型相冲突。我看到了几个可能的解决方案:
通过修改 使我的服务返回我的自定义错误类型hyper::Response,这似乎很难。
将非超级错误包装在hyper::error::Error. 这看起来很hacky。
别的东西。似乎我错过了执行此操作的“正确”方法。
以下代码显示了我认为我想做的事情:
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
Run Code Online (Sandbox Code Playgroud)
此代码导致编译器错误,我认为这是因为该bind函数要求响应类型的主体是具有错误类型的流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
Run Code Online (Sandbox Code Playgroud)
因为服务器的最终目标是向用户返回响应,所以我找到了一个可接受的解决方案是创建一个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
Run Code Online (Sandbox Code Playgroud)