Writing an Application
ntex provides various primitives to build web servers and applications with Rust. It provides routing, middleware, pre-processing of requests, post-processing of responses, etc.
All ntex servers are built around the App instance. It is used for registering routes for resources and middleware. It also stores application state shared across all handlers within the same scope.
An application's scope acts as a namespace for all routes, i.e. all routes for a specific application scope have the same url path prefix. The application prefix always contains a leading "/" slash. If a supplied prefix does not contain leading slash, it is automatically inserted. The prefix should consist of value path segments.
For an application with scope
/app, any request with the paths/app,/app/, or/app/testwould match; however, the path/applicationwould not match.
In this example, an application with the /app prefix and an index.html resource is created. This resource is available through the /app/index.html url.
For more information, check the URL Dispatch section.
State
An application has a single state type. The state type must implement the web::State trait, which also defines the application's error domain (usually web::DefaultError). Handlers registered with Route::to_with_state() receive a reference to the application state and the request state as their first two arguments, followed by any number of extractors. State is also passed to middleware and to error renderers.
Let's write a simple application and store the application name in the state:
Next, start the server with web::server_with_config(). The first closure is the state factory and the second one is the application factory:
Shared Mutable State
HttpServer accepts a state factory and an application factory rather than instances. The server calls the state factory once for each worker thread, so each worker owns its own state value. Per-worker state does not have to be Send or Sync, Rc<Cell<T>> works fine. If you want to share data between different threads, a shareable object should be used, e.g. Arc<Mutex<T>>.
In the following example, we will write an application with mutable, shared state. First, we define our state and create our handler:
and create the state for each worker:
Key takeaways:
- State initialized inside the state factory is local to the worker thread and may become de-synced if modified.
- To achieve globally shared state, it must be created outside of the state factory and moved/cloned in.
A complete example of process-wide and per-worker state is available in the state/process example. The state/web-state example shows how middleware, filters, handlers and error renderers use the application state, and how filters change the request state type to enforce handler requirements, such as an authenticated user, at compile time.
Using an Application Scope to Compose Applications
The web::scope() method allows setting a resource group prefix. This scope represents a resource prefix that will be prepended to all resource patterns added by the resource configuration. This can be used to help mount a set of routes at a different location than the original author intended while still maintaining the same resource names.
For example:
In the above example, the show_users route will have an effective route pattern of /users/show instead of /show because the application's scope argument will be prepended to the pattern. The route will then only match if the URL path is /users/show, and when the HttpRequest.url_for() function is called with the route name show_users, it will generate a URL with that same path.
Application guards and virtual hosting
You can think of a guard as a simple function that accepts a request object reference and returns true or false. Formally, a guard is any object that implements the Guard trait. ntex provides several guards. You can check the functions section of the API docs.
One of the provided guards is Host. It can be used as a filter based on request header information.
Configure
For simplicity and reusability both App and web::Scope provide the configure method. This function is useful for moving parts of the configuration to a different module or even library. For example, some of the resource's configuration could be moved to a different module.
The result of the above example would be:
/ -> "/"
/app -> "app"
/api/test -> "test"
Each ServiceConfig can have its own routes, services and external resources.