mirror of
https://gitlab.com/comunic/comunicapiv3
synced 2024-11-26 07:19:22 +00:00
Start to read POST request arguments
This commit is contained in:
parent
fcc38afe42
commit
7c18f19674
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
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user