Создание гипер-сервиса с пользовательским типом ошибки

Я пытаюсь создать 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`

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)
    }
}
Другие вопросы по тегам