I'm attempting to send a request off with Hyper, then deserialize it via JSON through Serde, but I cannot seem to wrap my head around futures and I'm receiving type mismatch errors stating expected (), found struct [put some odd struct here]. Nor can I wrap my head around the incredibly long and confusing error messages they spit out on each change. Here's my code:
extern crate futures;
extern crate hyper;
extern crate serde;
extern crate serde_json;
use futures::{
Future,
Stream,
future
};
use hyper::{
Body,
Client,
Response,
StatusCode,
Uri,
client::HttpConnector,
};
use serde::{ Deserialize };
use std::error::{ Error };
enum JsonError
{
RequestError(hyper::Error),
ResponseError(StatusCode),
DeserializeError(serde_json::Error),
}
fn get_json
<'t, T, F>
(client: &Client<HttpConnector>, uri: Uri)
-> impl Future<Item = T, Error = JsonError>
where
T : Deserialize<'t>
{
let f = client
.get(uri)
.map(|response|
{
let (parts, body) = response.into_parts();
if parts.status.is_success()
{
body
.fold(
vec![],
|mut accum, chunk|
{
accum.extend_from_slice(&*chunk);
Ok(accum)
}
)
.map(|v|
{
serde_json::from_slice::<T>(&v)
.map_err(|err| JsonError::DeserializeError(err))
})
}
future::err(JsonError::ResponseError(parts.status))
})
.map_err(|err| JsonError::RequestError(err));
return f;
}
I am completely lost and I think any advice could help at this point.
You have multiple errors stemming from logic issues when chaining futures. I have fixed its implementation, and it is available on the playground but I would strongly recommend you walk with me through what I changed.
But first, a recurring trend in your code:
#1: Future::map()'s return type
Future::map() allows you to change a future result of type T into type R, and it does so with the assumption that the transformation cannot fail (i.e. Fn(T) -> R). You've used map() multiple times in your code while returning either another Future, or a Result. Both are incorrect.
For Future chaining, and_then() allows you to perform the mapping fn(T) -> IntoFuture<Item = R> with error types remaining the same
For Result, convert them via future::result() into an already-executed future so you can also and_then()
#2: Errors
Errors do not convert themselves, especially not if you do not define their conversion methods. For this purpose, I've implemented From<hyper::Error> for your error type:
impl From<hyper::Error> for JsonError {
fn from(s: hyper::Error) -> JsonError {
JsonError::RequestError(s)
}
}
This allows you to use into() wherever the typechecker notices that it is possible to do so.
Do note, however, that because of hyper's own response type, err_into() gets the type checker confused, hence the explicit map(|r| r.into()).
#3: Deserialize and lifetimes
Deserialize<'t> is not the exact trait you were looking for. This trait implies that the entire object needs to live for lifetime 't. In a non-futures world, this would probably pass, but not in this case where the return object needs to be owned (hence the lifetime error you are getting).
for<'t> Deserialize<'t> is a completely different beast, and tells the compiler that this trait will have a lifetime 't, but will then be an owned object, or in other words, that the slice used to create the object will need to live for lifetime 't, not the entire returned object. Just what we need!
An additional nitpick: you really ought to separate the response parsing from the HTTP extraction in this function. As it stands right now, if I make a request over HTTPS, I will not be able to use your get_json() function, as my connector for hyper would then be TlsConnector<HttpConnector>. Problematic ;-)
The code:
use futures::{future, Future, Stream};
use hyper::{client::HttpConnector, Client, StatusCode, Uri};
use serde::Deserialize;
enum JsonError {
RequestError(hyper::Error),
ResponseError(StatusCode),
DeserializeError(serde_json::Error),
}
impl From<hyper::Error> for JsonError {
fn from(s: hyper::Error) -> JsonError {
JsonError::RequestError(s)
}
}
fn get_json<T, F>(
client: &Client<HttpConnector>,
uri: Uri,
) -> impl Future<Item = T, Error = JsonError>
where
T: for<'t> Deserialize<'t> + 'static,
{
client.get(uri).map_err(|e| e.into()).and_then(
|response| -> Box<dyn Future<Item = T, Error = JsonError>> {
let (parts, body) = response.into_parts();
match parts.status.is_success() {
true => Box::new(body
.map_err(|e| e.into())
.fold(
vec![],
|mut accum, chunk| -> Box<dyn Future<Item = Vec<u8>, Error = JsonError>>
{
accum.extend_from_slice(&*chunk);
Box::new(future::ok(accum))
}
)
.and_then(|v|
{
future::result(serde_json::from_slice::<T>(&v))
.map_err(|err| JsonError::DeserializeError(err))
})),
false => Box::new(future::err(JsonError::ResponseError(parts.status)))
}
},
)
}
Related
This question is written for Yew v0.19
Asynchronous foreign JavaScript functions can be used in Rust through Closures, as the function to pass-in:
#[wasm_bindgen]
extern "C" {
fn setInterval(closure: &Closure<dyn FnMut()>, time: u32) -> i32;
}
// ...
let cb = Closure::new(|| {
log("interval elapsed!");
});
let interval_id = setInterval(&cb, 1_000);
This is nice for a pedantic examples, but Closures have a critical requirement - the function applied needs to have a 'static lifetime. Likewise, with Yew applications, a perfect mechanism for spontaneous response is the Message enum, and having it update() the Model. However, the link() mechanism in Context (which issues messages) does not have a static lifetime.
In an ideal world, the value submitted to the closure could just be applied as a Yew component message:
struct Model {
thing: Option<JsValue>,
}
enum Msg {
GotThing(JsValue),
}
#[wasm_bindgen]
extern "C" {
fn createThing(closure: &Closure<dyn FnMut(JsValue) -> ());
}
impl Component for Model {
type Message = Msg;
type Properties = ();
fn create(_ctx: &Context<Self>) -> Self {
Model {
thing: None, // initial value
}
}
fn update(&mut self, _ctx: &Context<Self>, msg: Self::Message) -> bool {
match msg {
Msg::GotThing(x) => { // handle the message
self.thing = Some(x);
true
},
}
}
fn rendered(&mut self, ctx: &Context<Self>, first_render: bool) {
if first_render {
let cb: Box<dyn FnMut(JsValue) -> ()> = Box::new(|x| {
// try and issue the message
ctx.link().send_message(Msg::GotThing(x));
// ^ doesn't have a `'static` lifetime! Won't compile
});
createThing(Closure::wrap(&cb));
}
}
// fn view() ... omitted, not relevant
}
I'm wondering if there's a way to convert a Callback into a Closure, or if there's a better, more canonical way to do this, to please correct me.
Another idea I had would use some kind of queue defined statically (which wouldn't be safe as it's a mutable static variable), but then it could be used as an intermediary data type between the Closure passed to createThing, and messages could be dispatched within the component.
Maybe there's an external way to interact with a Yew component that I'm overlooking? I'm not sure how to resolve this issue. What would be the most correct way to achieve this goal?
I am creating a rust web application. I am new to this thing and I am trying to make API requests and pass a result form a request as Response to the web view. There are main.rs , route.rs and common.rs files. Basically main.rs file call relevant route and then route will call the function.
But, there is an error with a function in the route.rs file.
error[E0277]: the trait bound `fn() -> impl std::future::Future {<search::routes::get_token as actix_web::service::HttpServiceFactory>::register::get_token}: actix_web::handler::Factory<_, _, _>` is not satisfied
--> src\search\routes.rs:20:10
|
20 | async fn get_token() -> Result<String, reqwest::Error> {
| ^^^^^^^^^ the trait `actix_web::handler::Factory<_, _, _>` is not implemented for `fn() -> impl std::future::Future {<search::routes::get_token as actix_web::service::HttpServiceFactory>::register::get_token}`
How can I Fix this ??
route.rs
use crate::search::User;
use actix_web::{get, post, put, delete, web, HttpResponse, Responder};
use serde_json::json;
extern crate reqwest;
extern crate serde;
mod bfm;
mod common;
#[get("/token")]
async fn get_token() -> Result<String, reqwest::Error> {
let set_token = common::authenticate();
// let set_token = common::get_rust_posts();
return set_token.await;
//HttpResponse::Ok().json(set_token)
}
common.rs
extern crate reqwest;
use reqwest::header::HeaderMap;
use reqwest::header::AUTHORIZATION;
use reqwest::header::CONTENT_TYPE;
pub async fn authenticate() -> Result<String, reqwest::Error> {
fn construct_headers() -> HeaderMap {
let mut headers = HeaderMap::new();
headers.insert(AUTHORIZATION, "Basic bghythjuikyt==".parse().unwrap());
headers.insert(CONTENT_TYPE, "application/x-www-form-urlencoded".parse().unwrap());
assert!(headers.contains_key(AUTHORIZATION));
assert!(!headers.contains_key(CONTENT_TYPE));
headers
}
let client = reqwest::Client::new();
let reszr = client.post("https://api.test.com/auth/token")
.headers(construct_headers())
.body("grant_type=client_credentials")
.send()
.await?
.text()
.await;
return reszr;
}
First of all, please read: https://actix.rs/docs/errors/
--
Do you have implemented ResponseError for reqwest::Error?
You have to implement ResponseError trait because framework have to response with something in case when Result is Err.
Here is an example
impl ResponseError for Error {
fn error_response(&self) -> HttpResponse {
match self.kind_ref() {
ErrorKind::HttpResponse { msg, code, real } => {
println!("{}", real);
HttpResponse::build(code.to_owned()).json(json!({
"status": false,
"msg": msg
}))
}
_ => {
println!("{}", self);
HttpResponse::build(StatusCode::INTERNAL_SERVER_ERROR).finish()
}
}
}
}
Error in this case is my custom error that contains msg, code, real, but the logic should be similar in your case.
--
UPDATE: after #Masklinn comment about error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
Code above works only on custom Error you own.
What you can to do is:
map reqwest::Error to Error that already implements ResponseError. You can check list of implementations here: https://docs.rs/actix-web/3.0.0-alpha.1/actix_web/trait.ResponseError.html#implementors
create custom Error that wraps reqwest::Error (and other errors in your code) and implement ResponseError on it (this is basicaly same/similar as first)
I am learning to use Rust futures and I am finding it extremely confusing. I feel like I am being stupid but when would then, and_then and or_else be used? What return types are expected?
Please provide some examples of the different situations you would expect to see them.
TL;DR: then is used when you want to do something regardless of if the future was successful or not, and_then runs the closure only when the future succeeded, and or_else runs the closure only when the future failed.
and_then and or_else are direct analogs to the methods of the same name on Result .
Your first step should be to read the docs. The documentation contains the exact method signatures (which explain what types it expects and what the return types are), prose describing each method, and example usage as well.
I've extracted small snippets of the docs and emphasized the relevant parts.
Future::then:
This function can be used to ensure a computation runs regardless of
the conclusion of the future. The closure provided will be yielded a
Result once the future is complete.
The returned value of the closure must implement the IntoFuture trait
and can represent some more work to be done before the composed future
is finished.
Future::and_then:
This function can be used to chain two futures together and ensure
that the final future isn't resolved until both have finished. The
closure provided is yielded the successful result of this future and
returns another value which can be converted into a future.
Future::or_else
Return a future that passes along this future's value if it succeeds, and otherwise passes the error to the closure f and waits for the future it returns.
The return type for all three methods is any type that can be converted into another future.
then: no additional restrictions on the return type.
and_then requires that the error type of the returned future match the starting future's error type.
or_else requires that the success type of the returned future match the starting future's success type.
use futures::{future, Future}; // 0.1.25
struct Error;
fn download_from_server(server: u8) -> impl Future<Item = Vec<u8>, Error = Error> {
/* ... */
}
fn upload_to_server(data: Vec<u8>) -> impl Future<Item = usize, Error = Error> {
/* ... */
}
// Uses `or_else` to do work on failure
fn download() -> impl Future<Item = Vec<u8>, Error = Error> {
download_from_server(0)
.or_else(|_| download_from_server(1))
.or_else(|_| download_from_server(2))
}
// Uses `and_then` to do work on success
fn reupload() -> impl Future<Item = usize, Error = Error> {
download().and_then(|data| upload_to_server(data))
}
// Uses `then` to always do work
fn do_things() -> impl Future<Item = (), Error = ()> {
reupload().then(|r| {
match r {
Ok(size) => println!("Uploaded {} bytes", size),
Err(_) => println!("Got an error"),
};
Ok(())
})
}
Some cases are simplified by the async / await syntax stabilized in Rust 1.39:
// Equivalent to `or_else`
async fn download() -> Result<Vec<u8>, Error> {
match download_from_server(0).await {
Ok(v) => Ok(v),
Err(_) => match download_from_server(1).await {
Ok(v) => Ok(v),
Err(_) => download_from_server(2).await,
},
}
}
// Equivalent to `and_then`
async fn reupload() -> Result<usize, Error> {
let data = download().await?;
upload_to_server(data).await
}
// Equivalent to `then`
async fn do_things() -> Result<(), ()> {
match reupload().await {
Ok(size) => println!("Uploaded {} bytes", size),
Err(_) => println!("Got an error"),
}
Ok(())
}
The code below is the beginnings of a small library I'm writing to talk to a web API. Users of the library will instantiate a client MyClient and access the web API through it. Here, I'm trying to get an access token from the API before making requests to it.
In get_new_access() I'm able to make the request and receive the JSON response. I then try to use serde to turn the response into an Access struct, and this is where the problems start.
I've created a library specific error enum MyError which can represent the JSON deserializing and reqwest errors that could occur within get_new_access(). However, when I go to compile I get the trait serde::Deserialize<'_> is not implemented for MyError. My understanding is that this is happening because in the case that I get one of the aforementioned errors, serde does not know how to deserialize it into an Access struct. Of course, I don't want it to do that at all, so my question is what should I do?
I've looked at various serde deserialize examples, but all of them seem to assume that they are running in a main function that can only return a serde error. If I put #[derive(Deserialize)] above MyError's declaration, then I get the same error, but it shifts to reqwest::Error and serde_json::Error instead.
use std::error;
use std::fmt;
extern crate chrono;
extern crate reqwest;
#[macro_use]
extern crate serde_derive;
extern crate serde;
extern crate serde_json;
use chrono::prelude::*;
use reqwest::Client;
pub struct MyClient {
access: Access,
token_expires: DateTime<Utc>,
}
#[derive(Deserialize, Debug)]
struct Access {
access_token: String,
expires_in: i64,
token_type: String,
}
fn main() {
let sc: MyClient = MyClient::new();
println!("{:?}", &sc.access);
}
impl MyClient {
pub fn new() -> MyClient {
let a: Access = MyClient::get_new_access().expect("Couldn't get Access");
let e: DateTime<Utc> = chrono::Utc::now(); //TODO
MyClient {
access: a,
token_expires: e,
}
}
fn get_new_access() -> Result<Access, MyError> {
let params = ["test"];
let client = Client::new();
let json = client
.post(&[""].concat())
.form(¶ms)
.send()?
.text()
.expect("Couldn't get JSON Response");
println!("{}", &json);
serde_json::from_str(&json)?
//let a = Access {access_token: "Test".to_string(), expires_in: 3600, token_type: "Test".to_string() };
//serde_json::from_str(&json)?
}
}
#[derive(Debug)]
pub enum MyError {
WebRequestError(reqwest::Error),
ParseError(serde_json::Error),
}
impl fmt::Display for MyError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "eRROR")
}
}
impl error::Error for MyError {
fn description(&self) -> &str {
"API internal error"
}
fn cause(&self) -> Option<&error::Error> {
// Generic error, underlying cause isn't tracked.
None
}
}
impl From<serde_json::Error> for MyError {
fn from(e: serde_json::Error) -> Self {
MyError::ParseError(e)
}
}
impl From<reqwest::Error> for MyError {
fn from(e: reqwest::Error) -> Self {
MyError::WebRequestError(e)
}
}
Playground link here.
Your first problem is that your fn get_new_access() -> Result<Access, MyError> expects a Result. But in here:
//...
serde_json::from_str(&json)?
}
because of using ?(try macro), you are trying to return Result's unwrapped value which is a subtype of serde::Deserialize<'_>. The compiler warns you about this Deserialize is not a Result. What you should do is just return the result without unwrapping it:
//...
serde_json::from_str(&json)
}
Or
//...
let access = serde_json::from_str(&json)?; // gets access or propagates error
Ok(access) //if no error return access in a Result
}
Then you will have a second problem because your function expects MyError in the Result while you are returning serde_json::Error with this call serde_json::from_str(&json). Luckily Result has the function map_err which maps the actual error type to your custom error type.
This code will solve your problem:
//...
serde_json::from_str(&json).map_err(MyError::ParseError)
}
For the request in the comment :
For example, if I change the web request line to let json = client.post("").form(¶ms).send().map_err(MyError::WebRequestError)?.text()?;,
is that better practice at all?
Yes but since text() returns a Result you need to map it's error as MyError too. Since both send and text has same error type(reqwest::Error) you can combine the results with and_then :
let json = client
.post(&[""].concat())
.form(¶ms)
.send()
.and_then(Response::text) //use reqwest::Response;
.map_err(MyError::WebRequestError)?;
I am trying to create a REST server using hyper. For robust error handling, I would prefer to have the service return a future with a custom error type that wraps hyper, Diesel, and other errors. Unfortunately, hyper::Response seems to hard-code a stream with error type hyper::error::Error, which conflicts with the error type I've defined for my service. I see a couple possible solutions:
Make my service return my custom error type by modifying hyper::Response, which seems hard.
Wrap non-hyper errors in a hyper::error::Error. This seems hacky.
Something else. It seems like I'm missing the "right" way to do this.
The following code shows what I think I want to do:
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
This code results in a compiler error which I believe is because the bind function requires that the response type have a body that is a stream with error type 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`
Because the ultimate goal of the server is to return a response to the user, I found an acceptable solution to be to create a finalize function that converts errors encountered while processing a request into correctly formed responses and treats those errors as non-errors from hyper's perspective. I will need to flesh this idea out some (e.g. By passing along hyper errors as errors), but I believe the basic idea is sound.
The following code modifies the code in the question to do this:
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
You can implement the std::convert::From trait for your Error type. E.g. for the hyper::Error case:
impl From<hyper::Error> for Error {
fn from(error: hyper::Error) -> Self {
Error::Request(error)
}
}