mirror of
				https://gitlab.com/comunic/comunicapiv3
				synced 2025-11-04 01:24:04 +00:00 
			
		
		
		
	Start to read POST request arguments
This commit is contained in:
		
							
								
								
									
										3
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										3
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							@@ -503,7 +503,10 @@ version = "0.1.0"
 | 
				
			|||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 "actix-rt",
 | 
					 "actix-rt",
 | 
				
			||||||
 "actix-web",
 | 
					 "actix-web",
 | 
				
			||||||
 | 
					 "encoding_rs",
 | 
				
			||||||
 | 
					 "futures",
 | 
				
			||||||
 "mysql",
 | 
					 "mysql",
 | 
				
			||||||
 | 
					 "percent-encoding",
 | 
				
			||||||
 "serde",
 | 
					 "serde",
 | 
				
			||||||
 "yaml-rust",
 | 
					 "yaml-rust",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -12,3 +12,6 @@ mysql = "18.2.0"
 | 
				
			|||||||
actix-web = "2.0.0"
 | 
					actix-web = "2.0.0"
 | 
				
			||||||
actix-rt = "1.1.1"
 | 
					actix-rt = "1.1.1"
 | 
				
			||||||
serde = "1.0.110"
 | 
					serde = "1.0.110"
 | 
				
			||||||
 | 
					futures = "0.3.5"
 | 
				
			||||||
 | 
					encoding_rs = "0.8.23"
 | 
				
			||||||
 | 
					percent-encoding = "2.1.0"
 | 
				
			||||||
@@ -1,17 +1,119 @@
 | 
				
			|||||||
use actix_web::{App, HttpResponse, HttpServer, web, http};
 | 
					use actix_web::{App, HttpResponse, HttpServer, web, http, FromRequest, HttpRequest, HttpMessage};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use crate::controllers::routes::{get_routes, Route};
 | 
					use crate::controllers::routes::{get_routes, Route, RequestResult};
 | 
				
			||||||
use crate::data::config::Config;
 | 
					use crate::data::config::Config;
 | 
				
			||||||
use crate::data::http_error::HttpError;
 | 
					use crate::data::http_error::HttpError;
 | 
				
			||||||
use crate::controllers::routes::Method::{GET, POST};
 | 
					use crate::controllers::routes::Method::{GET, POST};
 | 
				
			||||||
use crate::data::http_request_handler::HttpRequestHandler;
 | 
					use crate::data::http_request_handler::{HttpRequestHandler, RequestValue};
 | 
				
			||||||
 | 
					use actix_web::dev::{PayloadStream, Payload, Decompress};
 | 
				
			||||||
 | 
					use actix_web::web::{BytesMut};
 | 
				
			||||||
 | 
					use futures::future::{ok, LocalBoxFuture, err};
 | 
				
			||||||
 | 
					use futures::{FutureExt, StreamExt, TryFutureExt};
 | 
				
			||||||
 | 
					use actix_web::error::{PayloadError, ErrorBadRequest};
 | 
				
			||||||
 | 
					use std::error::Error;
 | 
				
			||||||
 | 
					use encoding_rs::UTF_8;
 | 
				
			||||||
 | 
					use std::collections::HashMap;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Main server functions
 | 
					/// Main server functions
 | 
				
			||||||
///
 | 
					///
 | 
				
			||||||
/// @author Pierre Hubert
 | 
					/// @author Pierre Hubert
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Custom request value
 | 
				
			||||||
 | 
					struct CustomRequest {
 | 
				
			||||||
 | 
					    req: web::HttpRequest,
 | 
				
			||||||
 | 
					    body: HashMap<String, RequestValue>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Process in our way incoming requests
 | 
				
			||||||
 | 
					impl FromRequest for CustomRequest {
 | 
				
			||||||
 | 
					    type Error = actix_web::Error;
 | 
				
			||||||
 | 
					    type Future = LocalBoxFuture<'static, Result<CustomRequest, actix_web::Error>>;
 | 
				
			||||||
 | 
					    type Config = ();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn from_request(req: &HttpRequest, payload: &mut Payload<PayloadStream>) -> Self::Future {
 | 
				
			||||||
 | 
					        let req = req.clone();
 | 
				
			||||||
 | 
					        let payload = payload.take();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        async move {
 | 
				
			||||||
 | 
					            let mut body_args = HashMap::new();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Process "application/x-www-form-urlencoded" requests
 | 
				
			||||||
 | 
					            if req.content_type().eq("application/x-www-form-urlencoded") {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // Maximum size of the request
 | 
				
			||||||
 | 
					                let limit = 16384;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // Ready body
 | 
				
			||||||
 | 
					                let mut body = BytesMut::with_capacity(8192);
 | 
				
			||||||
 | 
					                let mut stream = Decompress::from_headers(payload, req.headers());
 | 
				
			||||||
 | 
					                while let Some(item) = stream.next().await {
 | 
				
			||||||
 | 
					                    let chunk = item?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    if body.len() + chunk.len() > limit {
 | 
				
			||||||
 | 
					                        return Err(actix_web::error::ErrorBadRequest("Overflow - too long"));
 | 
				
			||||||
 | 
					                    } else {
 | 
				
			||||||
 | 
					                        body.extend_from_slice(&chunk);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                let body = body.freeze();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // Decode body as a string
 | 
				
			||||||
 | 
					                let encoding = req.encoding()?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                let body_str = if encoding == UTF_8 {
 | 
				
			||||||
 | 
					                    String::from_utf8_lossy(body.as_ref()).to_string()
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    encoding
 | 
				
			||||||
 | 
					                        .decode_without_bom_handling_and_without_replacement(&body)
 | 
				
			||||||
 | 
					                        .map(|s| s.into_owned())
 | 
				
			||||||
 | 
					                        .ok_or_else(|| ErrorBadRequest("Can not decode body"))?
 | 
				
			||||||
 | 
					                };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // Parse body arguments (following the pattern key1=value1&key2=value2)
 | 
				
			||||||
 | 
					                if body_str.len() > 0 {
 | 
				
			||||||
 | 
					                    for v in body_str.split("&") {
 | 
				
			||||||
 | 
					                        if v.len() == 0 {
 | 
				
			||||||
 | 
					                            continue;
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        let args: Vec<&str> = v.split("=").collect();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        if args.len() != 2 {
 | 
				
			||||||
 | 
					                            return Err(actix_web::error::ErrorBadRequest(format!("{} is invalid!", args[0])));
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        // Add the value to the body
 | 
				
			||||||
 | 
					                        body_args.insert(
 | 
				
			||||||
 | 
					                            args[0].to_string(),
 | 
				
			||||||
 | 
					                            RequestValue::string(args[1].to_string())
 | 
				
			||||||
 | 
					                        );
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            Ok(CustomRequest {
 | 
				
			||||||
 | 
					                req: req.clone(),
 | 
				
			||||||
 | 
					                body: body_args,
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					        }.boxed_local()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Process a "simple request" aka not a WebSocket request
 | 
				
			||||||
 | 
					fn process_simple_route(route: &Route, req: &mut HttpRequestHandler) -> RequestResult {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Validate client token
 | 
				
			||||||
 | 
					    req.check_client_token()?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    (route.func)(req)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Process an incoming request
 | 
					/// Process an incoming request
 | 
				
			||||||
async fn process_request(req: web::HttpRequest) -> HttpResponse {
 | 
					async fn process_request(custom_req: CustomRequest) -> HttpResponse {
 | 
				
			||||||
 | 
					    let req = &custom_req.req;
 | 
				
			||||||
    let routes = get_routes();
 | 
					    let routes = get_routes();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // We search the appropriate route for the request
 | 
					    // We search the appropriate route for the request
 | 
				
			||||||
@@ -40,9 +142,9 @@ async fn process_request(req: web::HttpRequest) -> HttpResponse {
 | 
				
			|||||||
    let route = route.unwrap();
 | 
					    let route = route.unwrap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Execute the request
 | 
					    // Execute the request
 | 
				
			||||||
    let mut request = HttpRequestHandler::new(req);
 | 
					    let mut request = HttpRequestHandler::new(custom_req.req, custom_req.body);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    match (route.func)(&mut request) {
 | 
					    match process_simple_route(route, &mut request) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Set default error response if required
 | 
					        // Set default error response if required
 | 
				
			||||||
        Err(e) => {
 | 
					        Err(e) => {
 | 
				
			||||||
@@ -57,10 +159,6 @@ async fn process_request(req: web::HttpRequest) -> HttpResponse {
 | 
				
			|||||||
                request.success("Success").unwrap()
 | 
					                request.success("Success").unwrap()
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					 | 
				
			||||||
        // I use this to be quiet with IntelliJ
 | 
					 | 
				
			||||||
        #[allow(unreachable_patterns)]
 | 
					 | 
				
			||||||
        _ => {println!("Unexpected case (server.rs)!")}
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    request.response()
 | 
					    request.response()
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,6 +6,10 @@ use std::error;
 | 
				
			|||||||
///
 | 
					///
 | 
				
			||||||
/// @author Pierre Hubert
 | 
					/// @author Pierre Hubert
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Simple result type
 | 
				
			||||||
 | 
					pub type ResultExecError<E> = Result<E, ExecError>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Debug, Clone)]
 | 
					#[derive(Debug, Clone)]
 | 
				
			||||||
pub struct ExecError(pub String);
 | 
					pub struct ExecError(pub String);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -17,6 +21,10 @@ impl ExecError {
 | 
				
			|||||||
    pub fn boxed_new(msg: &str) -> Box<ExecError> {
 | 
					    pub fn boxed_new(msg: &str) -> Box<ExecError> {
 | 
				
			||||||
        Box::new(ExecError(msg.to_string()))
 | 
					        Box::new(ExecError(msg.to_string()))
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn boxed_string(msg: String) -> Box<ExecError> {
 | 
				
			||||||
 | 
					        Box::new(ExecError(msg))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl fmt::Display for ExecError {
 | 
					impl fmt::Display for ExecError {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,11 +4,27 @@ use crate::data::http_error::HttpError;
 | 
				
			|||||||
use std::convert::TryFrom;
 | 
					use std::convert::TryFrom;
 | 
				
			||||||
use std::error::Error;
 | 
					use std::error::Error;
 | 
				
			||||||
use serde::Serialize;
 | 
					use serde::Serialize;
 | 
				
			||||||
 | 
					use crate::data::error::{ResultExecError, ExecError};
 | 
				
			||||||
 | 
					use std::collections::HashMap;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Http request handler
 | 
					/// Http request handler
 | 
				
			||||||
///
 | 
					///
 | 
				
			||||||
/// @author Pierre Hubert
 | 
					/// @author Pierre Hubert
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Single request body  value
 | 
				
			||||||
 | 
					pub struct RequestValue {
 | 
				
			||||||
 | 
					    pub string: Option<String>
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl RequestValue {
 | 
				
			||||||
 | 
					    /// Build a string value
 | 
				
			||||||
 | 
					    pub fn string(s: String) -> RequestValue {
 | 
				
			||||||
 | 
					        RequestValue {
 | 
				
			||||||
 | 
					            string: Some(s)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Serialize)]
 | 
					#[derive(Serialize)]
 | 
				
			||||||
struct SuccessMessage {
 | 
					struct SuccessMessage {
 | 
				
			||||||
    success: String
 | 
					    success: String
 | 
				
			||||||
@@ -16,16 +32,17 @@ struct SuccessMessage {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
pub struct HttpRequestHandler {
 | 
					pub struct HttpRequestHandler {
 | 
				
			||||||
    request: web::HttpRequest,
 | 
					    request: web::HttpRequest,
 | 
				
			||||||
    response: Option<web::HttpResponse>
 | 
					    body: HashMap<String, RequestValue>,
 | 
				
			||||||
 | 
					    response: Option<web::HttpResponse>,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl HttpRequestHandler {
 | 
					impl HttpRequestHandler {
 | 
				
			||||||
 | 
					 | 
				
			||||||
    /// Construct a new request handler
 | 
					    /// Construct a new request handler
 | 
				
			||||||
    pub fn new(req: HttpRequest) -> HttpRequestHandler {
 | 
					    pub fn new(req: HttpRequest, body: HashMap<String, RequestValue>) -> HttpRequestHandler {
 | 
				
			||||||
        HttpRequestHandler {
 | 
					        HttpRequestHandler {
 | 
				
			||||||
            request: req,
 | 
					            request: req,
 | 
				
			||||||
            response: None
 | 
					            body,
 | 
				
			||||||
 | 
					            response: None,
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -53,4 +70,34 @@ impl HttpRequestHandler {
 | 
				
			|||||||
            HttpError::internal_error("Internal server error.")));
 | 
					            HttpError::internal_error("Internal server error.")));
 | 
				
			||||||
        Err(Box::try_from(actix_web::error::ErrorInternalServerError(error)).unwrap())
 | 
					        Err(Box::try_from(actix_web::error::ErrorInternalServerError(error)).unwrap())
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Check login tokens
 | 
				
			||||||
 | 
					    pub fn check_client_token(&mut self) -> ResultExecError<()> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        println!("me = {}", self.post_string("me")?);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Ok(())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Check if a POST parameter was present in the request or not
 | 
				
			||||||
 | 
					    pub fn has_post_parameter(&self, name: &str) -> bool {
 | 
				
			||||||
 | 
					        self.body.contains_key(name)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Get a post parameter
 | 
				
			||||||
 | 
					    pub fn post_parameter(&self, name: &str) -> Result<&RequestValue, ExecError> {
 | 
				
			||||||
 | 
					        self.body.get(name)
 | 
				
			||||||
 | 
					            .ok_or(ExecError(format!("POST parameter {} not found in request!", name)))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Get a post string
 | 
				
			||||||
 | 
					    pub fn post_string(&self, name: &str) -> Result<String, ExecError> {
 | 
				
			||||||
 | 
					        let param = self.post_parameter(name)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        match ¶m.string {
 | 
				
			||||||
 | 
					            None => Err(ExecError(format!("{} is not a string!", name))),
 | 
				
			||||||
 | 
					            Some(s) => Ok(s.to_string())
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
							
								
								
									
										18
									
								
								src/main.rs
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								src/main.rs
									
									
									
									
									
								
							@@ -1,13 +1,7 @@
 | 
				
			|||||||
 | 
					use comunic_server::controllers::server;
 | 
				
			||||||
use comunic_server::data::config::{conf, Config};
 | 
					use comunic_server::data::config::{conf, Config};
 | 
				
			||||||
use comunic_server::helpers::database;
 | 
					use comunic_server::helpers::database;
 | 
				
			||||||
use comunic_server::controllers::server;
 | 
					 | 
				
			||||||
use comunic_server::helpers::database::QueryInfo;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Debug)]
 | 
					 | 
				
			||||||
struct User {
 | 
					 | 
				
			||||||
    id : i64,
 | 
					 | 
				
			||||||
    name: String,
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[actix_rt::main]
 | 
					#[actix_rt::main]
 | 
				
			||||||
async fn main() -> std::io::Result<()> {
 | 
					async fn main() -> std::io::Result<()> {
 | 
				
			||||||
@@ -18,16 +12,6 @@ async fn main() -> std::io::Result<()> {
 | 
				
			|||||||
    // Connect to the database
 | 
					    // Connect to the database
 | 
				
			||||||
    database::connect(&conf().database).expect("Could not connect to database!");
 | 
					    database::connect(&conf().database).expect("Could not connect to database!");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let mut query = QueryInfo::new("user");
 | 
					 | 
				
			||||||
    query.cond("age", "190");
 | 
					 | 
				
			||||||
    //query.cond("id", "1");
 | 
					 | 
				
			||||||
    let res = database::query_row(query, |res| Ok(User {
 | 
					 | 
				
			||||||
        id: res.get_int64("id")?,
 | 
					 | 
				
			||||||
        name: res.get_str("name")?,
 | 
					 | 
				
			||||||
    })).unwrap();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    println!("{:#?}", res);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Start the server
 | 
					    // Start the server
 | 
				
			||||||
    server::start_server(conf()).await
 | 
					    server::start_server(conf()).await
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user