mirror of
https://gitlab.com/comunic/comunicapiv3
synced 2024-11-22 13:29:21 +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 = [
|
||||
"actix-rt",
|
||||
"actix-web",
|
||||
"encoding_rs",
|
||||
"futures",
|
||||
"mysql",
|
||||
"percent-encoding",
|
||||
"serde",
|
||||
"yaml-rust",
|
||||
]
|
||||
|
@ -12,3 +12,6 @@ mysql = "18.2.0"
|
||||
actix-web = "2.0.0"
|
||||
actix-rt = "1.1.1"
|
||||
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::http_error::HttpError;
|
||||
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
|
||||
///
|
||||
/// @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
|
||||
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();
|
||||
|
||||
// We search the appropriate route for the request
|
||||
@ -40,9 +142,9 @@ async fn process_request(req: web::HttpRequest) -> HttpResponse {
|
||||
let route = route.unwrap();
|
||||
|
||||
// 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
|
||||
Err(e) => {
|
||||
@ -57,10 +159,6 @@ async fn process_request(req: web::HttpRequest) -> HttpResponse {
|
||||
request.success("Success").unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
// I use this to be quiet with IntelliJ
|
||||
#[allow(unreachable_patterns)]
|
||||
_ => {println!("Unexpected case (server.rs)!")}
|
||||
}
|
||||
|
||||
request.response()
|
||||
|
@ -6,6 +6,10 @@ use std::error;
|
||||
///
|
||||
/// @author Pierre Hubert
|
||||
|
||||
|
||||
/// Simple result type
|
||||
pub type ResultExecError<E> = Result<E, ExecError>;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ExecError(pub String);
|
||||
|
||||
@ -17,6 +21,10 @@ impl ExecError {
|
||||
pub fn boxed_new(msg: &str) -> Box<ExecError> {
|
||||
Box::new(ExecError(msg.to_string()))
|
||||
}
|
||||
|
||||
pub fn boxed_string(msg: String) -> Box<ExecError> {
|
||||
Box::new(ExecError(msg))
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for ExecError {
|
||||
|
@ -4,11 +4,27 @@ use crate::data::http_error::HttpError;
|
||||
use std::convert::TryFrom;
|
||||
use std::error::Error;
|
||||
use serde::Serialize;
|
||||
use crate::data::error::{ResultExecError, ExecError};
|
||||
use std::collections::HashMap;
|
||||
|
||||
/// Http request handler
|
||||
///
|
||||
/// @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)]
|
||||
struct SuccessMessage {
|
||||
success: String
|
||||
@ -16,16 +32,17 @@ struct SuccessMessage {
|
||||
|
||||
pub struct HttpRequestHandler {
|
||||
request: web::HttpRequest,
|
||||
response: Option<web::HttpResponse>
|
||||
body: HashMap<String, RequestValue>,
|
||||
response: Option<web::HttpResponse>,
|
||||
}
|
||||
|
||||
impl HttpRequestHandler {
|
||||
|
||||
/// Construct a new request handler
|
||||
pub fn new(req: HttpRequest) -> HttpRequestHandler {
|
||||
pub fn new(req: HttpRequest, body: HashMap<String, RequestValue>) -> HttpRequestHandler {
|
||||
HttpRequestHandler {
|
||||
request: req,
|
||||
response: None
|
||||
body,
|
||||
response: None,
|
||||
}
|
||||
}
|
||||
|
||||
@ -53,4 +70,34 @@ impl HttpRequestHandler {
|
||||
HttpError::internal_error("Internal server error.")));
|
||||
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::helpers::database;
|
||||
use comunic_server::controllers::server;
|
||||
use comunic_server::helpers::database::QueryInfo;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct User {
|
||||
id : i64,
|
||||
name: String,
|
||||
}
|
||||
|
||||
#[actix_rt::main]
|
||||
async fn main() -> std::io::Result<()> {
|
||||
@ -18,16 +12,6 @@ async fn main() -> std::io::Result<()> {
|
||||
// Connect to the 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
|
||||
server::start_server(conf()).await
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user