# Next Web
Build web applications with joy and simplicity.
For Example:
```rust
use std::{
collections::HashSet,
net::SocketAddr,
sync::{atomic::AtomicU32, Arc},
};
use next_web::{
application::Application,
async_trait,
context::properties::ApplicationProperties,
extract::{find_singleton::FindSingleton, ConnectInfo},
macros::{bind::*, idempotency},
traits::store::idempotency_store::IdempotencyStore,
util::local_date_time::LocalDateTime,
ApplicationContext,
};
use next_web::{response::Html, store::memory_idempotency_store::MemoryIdempotencyStore};
use tokio::sync::Mutex;
use tracing::info;
/// Test application
#[derive(Default, Clone)]
pub struct TestApplication;
#[async_trait]
impl Application for TestApplication {
type ErrorSolve = ();
async fn init_middleware(
&self,
_ctx: &mut ApplicationContext,
_properties: &ApplicationProperties,
) {
}
async fn on_ready(&self, ctx: &mut ApplicationContext) {
ctx.insert_singleton_with_name(Arc::new(AtomicU32::new(0)), "requestCount");
#[rustfmt::skip]
ctx.insert_singleton_with_name(Arc::new(Mutex::new(HashSet::<SocketAddr>::new())),"requestIps");
ctx.insert_singleton_with_name(ApplicationStore::default(), "applicationStoreTwo");
ctx.insert_singleton_with_name(
Arc::new(MemoryIdempotencyStore::new()) as Arc<dyn IdempotencyStore<Value = ()>>,
"memoryIdempotencyStore",
);
}
}
#[request_mapping(method = "GET", path = "/timestamp")]
pub async fn req_timestamp() -> impl IntoResponse {
LocalDateTime::now()
}
#[post_mapping(path = "/record")]
pub async fn req_record(
ConnectInfo(addr): ConnectInfo<SocketAddr>,
FindSingleton(store): FindSingleton<ApplicationStore>,
) -> impl IntoResponse {
store.add(addr).await;
"Ok"
}
#[any_mapping(
path = "/recordTwo",
headers = ["ContentType", "Authorization"],
consume = "application/json",
produce = "application/json"
)]
pub async fn req_record_two(
// Search for singleton using variable names
#[find] FindSingleton(application_store_two): FindSingleton<ApplicationStore>,
ConnectInfo(addr): ConnectInfo<SocketAddr>,
) -> &'static str {
application_store_two.add(addr).await;
"Ok"
}
#[allow(unused)]
struct TestUserRoutes;
#[request_mapping(path = "/user")]
impl TestUserRoutes {
// Request -> /user/login
#[get_mapping(path = "/login")]
async fn req_login() -> impl IntoResponse {
Html("<h1>Login Page</h1>")
}
// Request -> /user/logout
#[idempotency(
name = "memoryIdempotencyStore",
key = "Idempotency-Key",
cache_key_prefix = "test_key",
ttl = 10
)]
#[request_mapping(method = "POST", path = "/logout")]
async fn req_logout() -> impl IntoResponse {
Html("<h1>Logout Page</h1>")
}
}
#[singleton(name = "applicationStore")]
#[derive(Clone)]
pub struct ApplicationStore {
pub request_count: Arc<AtomicU32>,
pub request_ips: Arc<Mutex<HashSet<SocketAddr>>>,
}
impl ApplicationStore {
async fn add(&self, addr: SocketAddr) {
self.request_count
.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
match self.request_ips.lock().await.insert(addr) {
true => info!("Store add new ip: {}", addr.to_string()),
false => info!("Ip already exists in store: {}", addr.to_string()),
}
info!(
"Current request count: {}",
self.request_count
.load(std::sync::atomic::Ordering::Relaxed)
)
}
}
impl Default for ApplicationStore {
fn default() -> Self {
Self {
request_count: Arc::new(AtomicU32::new(u32::MAX / 2)),
request_ips: Default::default(),
}
}
}
#[tokio::main]
async fn main() {
TestApplication::run().await;
}
```