tomaka / rouille

Web framework in Rust
Apache License 2.0
1.12k stars 106 forks source link

Flash message cookie example #255

Closed mycognosist closed 2 years ago

mycognosist commented 2 years ago

Thanks to @tomaka for this great project and to @bradfier for the maintenance efforts.

I wonder if you might be able to point me to an example implementation of flash message cookies in rouille?

I'm hoping to find an implementation similar to Rocket which allows the setting of a flash cookie in the Response and retrieval of a flash cookie in the Request.

To be clear, I'm not requesting that this feature be added to rouille, just hoping to find an existing implementation before jumping into implementing my own.

Thanks for your time!

mycognosist commented 2 years ago

Here's what I came up with (sharing here in case it's helpful for others):

use rouille::{input, Request, Response};

/// Flash message trait for `Request`.
pub trait FlashRequest {
    /// Retrieve the flash message cookie values from a `Request`.
    fn retrieve_flash(&self) -> (Option<&str>, Option<&str>);
}

impl FlashRequest for Request {
    fn retrieve_flash(&self) -> (Option<&str>, Option<&str>) {
        // check for flash cookies
        let flash_name = input::cookies(&self)
            .find(|&(n, _)| n == "flash_name")
            // return the value of the cookie (key is already known)
            .map(|key_val| key_val.1);
        let flash_msg = input::cookies(&self)
            .find(|&(n, _)| n == "flash_msg")
            .map(|key_val| key_val.1);

        (flash_name, flash_msg)
    }
}

/// Flash message trait for `Response`.
pub trait FlashResponse {
    /// Add flash message cookies to a `Response`.
    fn add_flash(self, flash_name: String, flash_msg: String) -> Response;
    /// Reset flash message cookie values for a `Response`.
    fn reset_flash(self) -> Response;
}

impl FlashResponse for Response {
    fn add_flash(self, flash_name: String, flash_msg: String) -> Response {
        // set the flash cookie headers
        self.with_additional_header("Set-Cookie", format!("{}; Max-Age=1", flash_name))
            .with_additional_header("Set-Cookie", format!("{}; Max-Age=1", flash_msg))
    }

    fn reset_flash(self) -> Response {
        // set blank cookies to clear the flash msg from the previous request
        self.with_additional_header("Set-Cookie", "flash_name=; Max-Age=0; Expires=Wed, 3 Jan 1990 12:00:00 UTC")
            .with_additional_header("Set-Cookie", "flash_msg=; Max-Age=0; Expires=Wed, 3 Jan 1990 12:00:00 UTC")
    }
}

Can then be used as follows:

// define a flash message
let (flash_name, flash_msg) = ("flash_name=success".to_string(), "flash_msg=New password has been saved".to_string());

// add the flash message to the response
Response::redirect_303("/auth/change").add_flash(flash_name, flash_msg)

// or, reset the flash message for a response
Response::html(routes::auth::change::build_template(request)).reset_flash()

// retrieve the flash message from a request
let (flash_name, flash_msg) = request.retrieve_flash();