ntex

Add Nyoxis threat detection to an ntex application using a service middleware transform and reqwest for async HTTP calls.

Prerequisites

  • Rust 1.75 or later
  • An ntex application (0.7+)
  • A Nyoxis workspace API key — get one here

Add dependencies

toml
# Cargo.toml [dependencies] ntex = { version = "0.7", features = ["tokio"] } reqwest = { version = "0.12", features = ["json"] } serde = { version = "1", features = ["derive"] } serde_json = "1" tokio = { version = "1", features = ["full"] }

Middleware

Create src/middleware/nyoxis.rs:

rust
use std::{future::Future, pin::Pin, rc::Rc}; use ntex::{ service::{Middleware, Service, ServiceCtx}, web::{HttpRequest, HttpResponse, WebRequest, WebResponse}, }; use reqwest::Client; use serde_json::{json, Value}; /// Nyoxis WAF middleware — calls /v0/predict for every request. pub struct NyoxisWAF { api_key: Rc<String>, block_on_high: bool, } impl NyoxisWAF { pub fn new(api_key: impl Into<String>) -> Self { Self { api_key: Rc::new(api_key.into()), block_on_high: false, } } pub fn block_on_high(mut self) -> Self { self.block_on_high = true; self } } impl<S> Middleware<S> for NyoxisWAF { type Service = NyoxisWAFService<S>; fn create(&self, service: S) -> Self::Service { NyoxisWAFService { service, api_key: self.api_key.clone(), block_on_high: self.block_on_high, client: Client::new(), } } } pub struct NyoxisWAFService<S> { service: S, api_key: Rc<String>, block_on_high: bool, client: Client, } impl<S, E> Service<WebRequest<E>> for NyoxisWAFService<S> where S: Service<WebRequest<E>, Response = WebResponse>, E: ntex::web::ErrorRenderer, { type Response = WebResponse; type Error = S::Error; type Future<'f> = Pin<Box<dyn Future<Output = Result<WebResponse, S::Error>> + 'f>> where Self: 'f; ntex::forward_poll_ready!(service); ntex::forward_poll_shutdown!(service); fn call<'a>(&'a self, req: WebRequest<E>, ctx: ServiceCtx<'a, Self>) -> Self::Future<'a> { Box::pin(async move { let ip = req .connection_info() .realip_remote_addr() .map(str::to_string); let payload = json!({ "method": req.method().as_str(), "path": req.path(), "query": req.query_string().is_empty() .then(|| Option::<String>::None) .unwrap_or_else(|| Some(req.query_string().to_string())), "ip_addr": ip, }); let url = format!( "https://api.nyoxis.com/v0/predict?api_key={}", self.api_key ); match self .client .post(&url) .json(&payload) .timeout(std::time::Duration::from_secs(3)) .send() .await { Ok(resp) => { if let Ok(verdict) = resp.json::<Value>().await { if self.block_on_high { let risk = verdict .pointer("/prediction/risk") .and_then(Value::as_str) .unwrap_or("none"); if risk == "high" { let http_res = HttpResponse::Forbidden() .json(&json!({ "error": "Forbidden" })); return Ok(req.into_response(http_res)); } } // Store verdict in request extensions for handlers req.extensions_mut().insert(verdict); } } Err(e) => { // Fail open — log and continue eprintln!("[nyoxis] prediction error: {e}"); } } ctx.call(&self.service, req).await }) } }

Register the middleware

rust
// src/main.rs mod middleware; use middleware::nyoxis::NyoxisWAF; use ntex::web; use std::env; #[ntex::main] async fn main() -> std::io::Result<()> { let api_key = env::var("NYOXIS_API_KEY").expect("NYOXIS_API_KEY not set"); web::HttpServer::new(move || { web::App::new() .wrap(NyoxisWAF::new(api_key.clone()).block_on_high()) .route("/", web::get().to(index)) }) .bind("0.0.0.0:8080")? .run() .await } async fn index(req: web::HttpRequest) -> web::HttpResponse { // Read verdict from request extensions let risk = req .extensions() .get::<serde_json::Value>() .and_then(|v| v.pointer("/prediction/risk")) .and_then(serde_json::Value::as_str) .unwrap_or("none") .to_string(); web::HttpResponse::Ok().json(&serde_json::json!({ "ok": true, "risk": risk })) }

Next steps

  • API Reference — complete field descriptions and status codes.
  • Overview — how the classifier and redaction pipeline work.

Cookie preferences

Nyoxis uses essential cookies for authentication and session security. We only enable Analytics after you consent. See our Cookie Policy for details.