#[get("/")]
async fn index(req: HttpRequest) -> impl Responder {
HttpResponse::build(StatusCode::ACCEPTED)
.cookie(cookie::Cookie::new("sample", "value"))
.body("hello")
}
I have this route handler, a simple example to return some cookie. However, no cookie shows up in the response. What am I doing wrong?
Related
Background
I need to create a couple of endpoints for an API service project. These API can accept encrypted and un-encrypted one (for development). Both parameters then are passed into a same function. For example:
/api/movie/get with encrypted parameter (for production)
/dev/movie/get with un-encrypted parameter (for development)
Current implementation in actix_web
I use actix_web and routes (not macro) to provide path routing. Modified from the sample code below
use actix_web::{web, App, HttpServer, Responder};
async fn index() -> impl Responder {
"Hello world!"
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(|| {
App::new().service(
// prefixes all resources and routes attached to it...
web::scope("/app")
// ...so this handles requests for `GET /app/index.html`
.route("/index.html", web::get().to(index)),
)
})
.bind(("127.0.0.1", 8080))?
.run()
.await
}
For each API endpoint, I had to create 2 functions, such as:
/// for encrypted one
/// PATH: /api/movie/get
pub async fn handle_get_movie(
app_data: Data<AppState>,
Json(payload): Json<EncryptedPayload>,
) -> Result<impl Responder, Error> {
// decrypt the content
// this is the only difference between un-encrypted and encrypted endpoint
let params = payload
.unload::<GetMovieInf>()
.map_err(|_| MainError::Malformatted)?;
// other codes here....
Ok(Json(ReplyInf {ok: true}))
}
/// for un-encrypted one
/// PATH: /dev/movie/get
pub async fn handle_get_movie(
app_data: Data<AppState>,
Json(payload): Json<UnencryptedPayload>,
) -> Result<impl Responder, Error> {
// other codes here. These codes are exactly identical with the above function...
Ok(Json(ReplyInf {ok: true}))
}
Questions
Since both functions are similar, does it possible to combine them both into a single function ? a function "overload" maybe ?
The problem is this line Json(payload): Json<UnencryptedPayload> in the parameter function. I tried to use generics like Json<T>. this doesn't work.
I can use the environment variable to control which should be active (EncryptedPayload or UnencryptedPayload). I can use one path for each endpoint (eg: /api/movie/get) and don't have to write the same functionality twice.
I'm trying to send http GET request with Rust using reqwest crate. Following code works:
extern crate reqwest;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let resp = reqwest::blocking::get("https://en.wikipedia.org/wiki/Rust_(programming_language)")?
.text()?;
println!("{:#?}", resp);
Ok(())
}
but when I change the URL to https://www.mongolbank.mn/
response body html shows the following error and not the content I want
...Description: </b>An application error occurred on the server. The current custom error settings for this application prevent the details of the application error from being viewed remotely (for security reasons). It could, however, be viewed by browsers running on the local server machine...
What's happening?
How can I fix it?
Use tokio runtime and user agent to bypass the error. User agent you can go and grab it from your browser using browser's debugging toolkit
use reqwest::{self, header};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>>{
let mut headers = header::HeaderMap::new();
headers.insert(header::USER_AGENT,
header::HeaderValue::from_static("Mozilla/5.0...."));
let client = reqwest::Client::builder()
.default_headers(headers)
.build()?;
let res = client.get("https://www.mongolbank.mn").send().await?;
println!("{:#?}", res);
Ok(())
}
I'm trying to build out a simple backend with Rust, but I keep running into a CORS error.
My react frontend says:
Access to XMLHttpRequest at 'http://127.0.0.1:3002/auth' from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
Here's my backend code:
// Route Config //
pub fn routes(cfg: &mut web::ServiceConfig) {
cfg.route("/auth", web::get().to(auth_handler));
}
// Handler Config //
async fn auth_handler() -> impl Responder {
HttpResponse::Ok().json("Works")
}
#[actix_rt::main]
async fn main() -> io::Result<()> {
let app = move || App::new().configure(routes);
let _cors = Cors::permissive();
HttpServer::new(app).bind("127.0.0.1:3002")?.run().await
}
To my understanding, the CORS::permissive() function should allow all cross site interactions to work. Did I misunderstand the docs, or did I implement it wrong?
You need to actually use the Cors middleware in your App via .wrap():
let app = move || App::new().wrap(Cors::permissive()).configure(routes);
// ^^^^^^^^^^^^^^^^^^^^^^^^^
More info on configuring it here.
Another option is to use the actix_cors crate which has been created to specifically handle this use case.
So the main function would look something like this:
use actix_cors::Cors;
use actix_web::{http::header, App, HttpServer};
#[actix_web::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(move || {
App::new()
.wrap(
Cors::default()
.allowed_origin("*")
.allowed_methods(vec!["GET", "POST"])
.allowed_headers(vec![header::AUTHORIZATION, header::ACCEPT])
.allowed_header(header::CONTENT_TYPE),
)
.wrap(Logger::default())
.service(user::info)
})
.bind(("127.0.0.1", 3002))?
.run()
.await
}
I'm trying to write an authentication middleware for my Actix application. When validating the request in the middleware, I make a call to a database to retrieve the necessary user data to validate the incoming request. Once the request has been authorised, I want to be able to pass this user data to the handler as this will allow me to avoid having the query for the same data twice.
I can't find a solution for this. The best suggestion I could find so far was to "set a request extension". There doesn't seem to be any examples for this and there is also too little documentation around this to work out what to do here.
You can pass data from middleware (service) to handler via extensions. First of all you have to insert extension (in service).
For ServiceRequest struct is implemented HttpMessage witch hase extensions_mut() function. It must be mutable becouse you will be inserting new extension. It might look something like this:
req.extensions_mut().insert(user);
Then you have to implement FromRequest trait for your data structure.
impl FromRequest for User {
type Error = actix_web::Error;
type Future = futures::future::Ready<Result<Self, Self::Error>>;
type Config = ();
fn from_request(req: &HttpRequest, payload: &mut Payload) -> Self::Future {
match req.extensions().get::<User>() {
Some(user) => return ok(user.clone()),
None => return err(actix_web::error::ErrorBadRequest("ups..."))
};
}
}
Then you're ready to use it in handler.
pub async fn get_user_handler(user: User) {}
I am using iron. Most of time like 99.* % all is good. But sometimes I get error like Error was: ErrorImpl { code: EofWhileParsingString/List/Object, line: 1, column: 8186 } or InvalidUnicodeCodePoint. I am printing request in log and when i try that request every thing goes well. I also have server written in Golang receiving same request and they never have parsing or json to MyStruct conversion problem.Please note Code would not compile as it is, missing imports, error::from and structure definition. Can not provide reproducible request logs as it only happens when serving lots on concurrent request but if single request is taken it works fine.
I have tried serde_json::from_reader, bodyparser crate and all have same issue.
extern crate serde;
extern crate serde_json;
extern crate iron;
use self::iron;
use self::iron::prelude::*;
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct MyStruct {
}
struct ResponseTime;
impl typemap::Key for ResponseTime {
type Value = u64;
}
#[derive(Debug)]
struct RequestBody;
impl typemap::Key for RequestBody {
type Value = RefCell<Vec<u8>>;
}
impl BeforeMiddleware for ResponseTime {
fn before(&self, req: &mut Request) -> IronResult<()> {
req.extensions.insert::<RequestBody>(RefCell::new(Vec::new()));
req.extensions.insert::<ResponseTime>(precise_time_ns());
Ok(())
}
}
impl AfterMiddleware for ResponseTime {
fn after(&self, req: &mut Request, res: Response) -> IronResult<Response> {
Ok(res)
}
fn catch(&self, req : &mut Request, err : IronError) -> IronResult<Response> {
let ref byte_req = *req.extensions.get::<RequestBody>()
.unwrap()
.borrow();
//just to make sure uft8 is not causing some issue.
let payload = unsafe {
str::from_utf8_unchecked(&byte_req)
};
//but when i send request body all comes good
error!("Error {} for Body {}", err, payload);
Err(err)
}
}
fn iron_handler(req : &mut Request) -> Result<Response, CustomError>{
let mut buffer = req.extensions.get::<server::RequestBody>()
.unwrap()
.borrow_mut();
req.body.read_to_end(&mut buffer)?;
// not seeing InvalidUnicodeCodePoint after this.
let payload = String::from_utf8_lossy(&buffer);
//some request throw error
let my_struct_obj : MyStruct = serde_json::from_str(&payload)?;
Ok(Response::with((iron::status::Ok, "Final Response")))
}
Need help to figure out how to identify problem. Intent of posting here is to see if someone had same issue or can see obvious problem with this. Appreciate everyone'e time do not expect to build and run with examples as can not provide them because of privacy.