1
0
mirror of https://github.com/actix/actix-website synced 2024-11-23 16:31:08 +01:00

migrate to docusaurus (v2) (#266)

Co-authored-by: ibraheemdev <ibrah1440@gmail.com>
This commit is contained in:
Santiago 2022-07-16 11:59:20 +02:00 committed by GitHub
parent a85b4ff5a3
commit 8393aea71a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
85 changed files with 23020 additions and 4357 deletions

24
.github/workflows/build-check.yml vendored Normal file
View File

@ -0,0 +1,24 @@
name: Build check
on:
pull_request:
branches:
- master
# Review gh actions docs if you want to further define triggers, paths, etc
# https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#on
jobs:
test-deploy:
name: Build check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v3
with:
node-version: 16.x
cache: npm
- name: Install dependencies
run: npm install
- name: Test build website
run: npm run build

View File

@ -1,32 +0,0 @@
name: Build and Deploy
on:
push:
branches:
- master
jobs:
build-and-deploy:
runs-on: ubuntu-latest
if: github.repository == 'actix/actix-website'
steps:
- name: Checkout
uses: actions/checkout@v2
with:
persist-credentials: false # This is needed to deploy on checkout@v2
- name: Setup Hugo
uses: peaceiris/actions-hugo@v2
with:
hugo-version: '0.79.1'
- name: Build
run: hugo --minify
- name: Deploy to GitHub Pages
uses: JamesIves/github-pages-deploy-action@4.1.1
with:
token: ${{ secrets.ACCESS_TOKEN }}
branch: master
folder: public
repository-name: actix/actix.github.io

11
.gitignore vendored
View File

@ -1,8 +1,5 @@
public
tmp
_site
Cargo.lock
node_modules/
build/
target/
.DS_Store
/.hugo_build.lock
.docusaurus
.cache-loader
Cargo.lock

1
.nvmrc Normal file
View File

@ -0,0 +1 @@
16

View File

@ -2,32 +2,25 @@
## Getting Started
Building the website depends on [Hugo]. So, first make sure that you have it installed. If on macOS and using [Homebrew], run the following:
```sh
brew update
brew install hugo
```
Then, get the website running locally:
Building the website depends on [Docusaurus], you must have `npm` or `yarn` installed. You can run the site locally with:
```sh
git clone https://github.com/actix/actix-website.git
cd actix-website
hugo server
npm install # or yarn install
npm start # or yarn start
```
Then visit http://localhost:1313.
Then visit http://localhost:3000.
## Updating diagrams
Diagrams are located under [/static/css/img/diagrams/](https://github.com/actix/actix-website/tree/master/static/img/diagrams) and built with [Mermaid CLI].
Diagrams are located under [/static/img/diagrams/](https://github.com/actix/actix-website/tree/master/static/img/diagrams) and built with [Mermaid CLI][mermaid cli].
For instance to edit `connection_overview` diagram:
```sh
cd static/css/img/diagrams
cd static/img/diagrams
vi connection_overview.mmd
# Compile diagrams:
mmdc -i connection_overview.mmd -o connection_overview.svg
@ -35,10 +28,14 @@ mmdc -i connection_overview.mmd -o connection_overview.svg
# License
Pretty murky. Right now a massive clone of the tokio website. Will get this figured out as we go along.
This site is licensed under either of
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
[http://www.apache.org/licenses/LICENSE-2.0])
* MIT license ([LICENSE-MIT](LICENSE-MIT) or
[http://opensource.org/licenses/MIT])
<!-- LINKS -->
[hugo]: https://gohugo.io
[homebrew]: https://brew.sh
[Docusaurus]: https://docusaurus.io/
[mermaid cli]: https://github.com/mermaidjs/mermaid.cli

Binary file not shown.

3
babel.config.js Normal file
View File

@ -0,0 +1,3 @@
module.exports = {
presets: [require.resolve('@docusaurus/core/lib/babel/preset')],
};

View File

@ -1,22 +0,0 @@
title = "actix"
canonifyURLs = true
googleAnalytics = ""
pygmentsUseClasses = true
pygmentsCodeFences = true
defaultContentLanguageInSubdir = false
enableRobotsTXT = true
enableMissingTranslationPlaceholders = true
DefaultContentLanguage = "en"
baseURL = "https://actix.rs"
[languages.en]
languageCode = "en-US"
languageName = "English"
weight = 1
[params]
actixVersion = "0.10"
actixWebVersion = "4"
actixRtVersion = "2"
actixWebMinRustVersion = "1.54"
actixMinRustVersion = "1.54"

View File

@ -1,3 +0,0 @@
---
title: Actix Web | A powerful, pragmatic, and extremely fast web framework for Rust.
---

View File

@ -1,9 +1,9 @@
---
title: Application
menu: docs_basics
weight: 140
---
import CodeBlock from "@site/src/components/code_block.js";
# Writing an Application
`actix-web` provides various primitives to build web servers and applications with Rust. It provides routing, middleware, pre-processing of requests, post-processing of responses, etc.
@ -14,7 +14,7 @@ An application's [`scope`][scope] acts as a namespace for all routes, i.e. all r
> For an application with scope `/app`, any request with the paths `/app`, `/app/`, or `/app/test` would match; however, the path `/application` would not match.
{{< include-example example="application" file="app.rs" section="setup" >}}
<CodeBlock example="application" file="app.rs" section="setup" />
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.
@ -26,11 +26,11 @@ Application state is shared with all routes and resources within the same scope.
Let's write a simple application and store the application name in the state:
{{< include-example example="application" file="state.rs" section="setup" >}}
<CodeBlock example="application" file="state.rs" section="setup" />
Next, pass in the state when initializing the App and start the application:
{{< include-example example="application" file="state.rs" section="start_app" >}}
<CodeBlock example="application" file="state.rs" section="start_app" />
Any number of state types could be registered within the application.
@ -42,11 +42,11 @@ Internally, [`web::Data`][data] uses `Arc`. So in order to avoid creating two `A
In the following example, we will write an application with mutable, shared state. First, we define our state and create our handler:
{{< include-example example="application" file="mutable_state.rs" section="setup_mutable" >}}
<CodeBlock example="application" file="mutable_state.rs" section="setup_mutable" />
and register the data in an `App`:
{{< include-example example="application" file="mutable_state.rs" section="make_app_mutable" >}}
<CodeBlock example="application" file="mutable_state.rs" section="make_app_mutable" />
Key takeaways:
- State initialized _inside_ the closure passed to `HttpServer::new` is local to the worker thread and may become de-synced if modified.
@ -58,7 +58,7 @@ The [`web::scope()`][webscope] method allows setting a resource group prefix. Th
For example:
{{< include-example example="application" file="scope.rs" section="scope" >}}
<CodeBlock example="application" file="scope.rs" section="scope" />
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()`][urlfor] function is called with the route name `show_users`, it will generate a URL with that same path.
@ -68,13 +68,13 @@ You can think of a guard as a simple function that accepts a _request_ object re
One of the provided guards is [`Header`][guardheader]. It can be used as a filter based on request header information.
{{< include-example example="application" file="vh.rs" section="vh" >}}
<CodeBlock example="application" file="vh.rs" section="vh" />
# Configure
## Configure
For simplicity and reusability both [`App`][appconfig] and [`web::Scope`][webscopeconfig] 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.
{{< include-example example="application" file="config.rs" section="config" >}}
<CodeBlock example="application" file="config.rs" section="config" />
The result of the above example would be:

View File

@ -1,7 +1,5 @@
---
title: Auto-Reloading
menu: docs_patterns
weight: 1000
---
# Auto-Reloading Development Server

View File

@ -1,7 +1,5 @@
---
title: Connection Lifecycle
menu: docs_architecture
weight: 1030
---
# Architecture overview

View File

@ -1,9 +1,9 @@
---
title: Databases
menu: docs_patterns
weight: 1010
---
import CodeBlock from "@site/src/components/code_block.js";
# Async Options
We have several example projects showing use of async database adapters:
@ -18,17 +18,17 @@ The current version of Diesel (v1) does not support asynchronous operations, so
You can create action functions that correspond to all the operations your app will perform on the database.
{{< include-example example="databases" file="main.rs" section="handler" >}}
<CodeBlock example="databases" file="main.rs" section="handler" />
Now you should set up the database pool using a crate such as `r2d2`, which makes many DB connections available to your app. This means that multiple handlers can manipulate the DB at the same time, and still accept new connections. Simply, the pool in your app state. (In this case, it's beneficial not to use a state wrapper struct because the pool handles shared access for you.)
{{< include-example example="databases" file="main.rs" section="main" >}}
<CodeBlock example="databases" file="main.rs" section="main" />
Now, in a request handler, use the `Data<T>` extractor to get the pool from app state and get a connection from it. This provides an owned database connection that can be passed into a [`web::block`][web-block] closure. Then just call the action function with the necessary arguments and `.await` the result.
This example also maps the error to an `HttpResponse` before using the `?` operator but this is not necessary if your return error type implements [`ResponseError`][response-error].
{{< include-example example="databases" file="main.rs" section="index" >}}
<CodeBlock example="databases" file="main.rs" section="index" />
That's it! See the full example here: https://github.com/actix/examples/tree/master/databases/diesel

View File

@ -1,9 +1,9 @@
---
title: Errors
menu: docs_advanced
weight: 180
---
import CodeBlock from "@site/src/components/code_block.js";
# Errors
Actix Web uses its own [`actix_web::error::Error`][actixerror] type and [`actix_web::error::ResponseError`][responseerror] trait for error handling from web handlers.
@ -42,23 +42,23 @@ See [the actix-web API documentation][responseerrorimpls] for a full list of for
Here's an example implementation for `ResponseError`, using the [derive_more] crate for declarative error enums.
{{< include-example example="errors" file="main.rs" section="response-error" >}}
<CodeBlock example="errors" file="main.rs" section="response-error" />
`ResponseError` has a default implementation for `error_response()` that will render a _500_ (internal server error), and that's what will happen when the `index` handler executes above.
Override `error_response()` to produce more useful results:
{{< include-example example="errors" file="override_error.rs" section="override" >}}
<CodeBlock example="errors" file="override_error.rs" section="override" />
# Error helpers
## Error helpers
Actix Web provides a set of error helper functions that are useful for generating specific HTTP error codes from other errors. Here we convert `MyError`, which doesn't implement the `ResponseError` trait, to a _400_ (bad request) using `map_err`:
{{< include-example example="errors" file="helpers.rs" section="helpers" >}}
<CodeBlock example="errors" file="helpers.rs" section="helpers" />
See the [API documentation for actix-web's `error` module][actixerror] for a full list of available error helpers.
# Error logging
## Error logging
Actix logs all errors at the `WARN` log level. If an application's log level is set to `DEBUG` and `RUST_BACKTRACE` is enabled, the backtrace is also logged. These are configurable with environmental variables:
@ -68,13 +68,13 @@ Actix logs all errors at the `WARN` log level. If an application's log level is
The `Error` type uses the cause's error backtrace if available. If the underlying failure does not provide a backtrace, a new backtrace is constructed pointing to the point where the conversion occurred (rather than the origin of the error).
# Recommended practices in error handling
## Recommended practices in error handling
It might be useful to think about dividing the errors an application produces into two broad groups: those which are intended to be user-facing, and those which are not.
An example of the former is that I might use failure to specify a `UserError` enum which encapsulates a `ValidationError` to return whenever a user sends bad input:
{{< include-example example="errors" file="recommend_one.rs" section="recommend-one" >}}
<CodeBlock example="errors" file="recommend_one.rs" section="recommend-one" />
This will behave exactly as intended because the error message defined with `display` is written with the explicit intent to be read by a user.
@ -82,11 +82,11 @@ However, sending back an error's message isn't desirable for all errors -- there
Here's an example that maps an internal error to a user-facing `InternalError` with a custom message:
{{< include-example example="errors" file="recommend_two.rs" section="recommend-two" >}}
<CodeBlock example="errors" file="recommend_two.rs" section="recommend-two" />
By dividing errors into those which are user facing and those which are not, we can ensure that we don't accidentally expose users to errors thrown by application internals which they weren't meant to see.
# Error Logging
## Error Logging
This is a basic example using `middleware::Logger` which depends on `env_logger` and `log`:
@ -96,7 +96,7 @@ env_logger = "0.8"
log = "0.4"
```
{{< include-example example="errors" file="logging.rs" section="logging" >}}
<CodeBlock example="errors" file="logging.rs" section="logging" />
[actixerror]: https://docs.rs/actix-web/4/actix_web/error/struct.Error.html
[errorhelpers]: https://docs.rs/actix-web/4/actix_web/trait.ResponseError.html

View File

@ -1,50 +1,50 @@
---
title: Extractors
menu: docs_basics
weight: 170
---
import CodeBlock from "@site/src/components/code_block.js";
# Type-safe information extraction
Actix Web provides a facility for type-safe request information access called _extractors_ (i.e., `impl FromRequest`). There are lots of built-in extractor implementations (see [implementors](https://actix.rs/actix-web/actix_web/trait.FromRequest.html#implementors)).
An extractor can be accessed as an argument to a handler function. Actix Web supports up to 12 extractors per handler function. Argument position does not matter.
{{< include-example example="extractors" file="main.rs" section="option-one" >}}
<CodeBlock example="extractors" file="main.rs" section="option-one" />
# Path
## Path
[_Path_][pathstruct] provides information that is extracted from the request's path. Parts of the path that are extractable are called "dynamic segments" and are marked with curly braces. You can deserialize any variable segment from the path.
For instance, for resource that registered for the `/users/{user_id}/{friend}` path, two segments could be deserialized, `user_id` and `friend`. These segments could be extracted as a tuple in the order they are declared (e.g., `Path<(u32, String)>`).
{{< include-example example="extractors" file="path_one.rs" section="path-one" >}}
<CodeBlock example="extractors" file="path_one.rs" section="path-one" />
It is also possible to extract path information to a type that implements the `Deserialize` trait from `serde` by matching dynamic segment names with field names. Here is an equivalent example that uses `serde` instead of a tuple type.
{{< include-example example="extractors" file="path_two.rs" section="path-two" >}}
<CodeBlock example="extractors" file="path_two.rs" section="path-two" />
As a non-type-safe alternative, it's also possible to query (see [`match_info` docs](docsrs_match_info)) the request for path parameters by name within a handler:
As a non-type-safe alternative, it's also possible to query (see [`match_info` docs][docsrs_match_info]) the request for path parameters by name within a handler:
{{< include-example example="extractors" file="path_three.rs" section="path-three" >}}
<CodeBlock example="extractors" file="path_three.rs" section="path-three" />
# Query
## Query
The [`Query<T>`][querystruct] type provides extraction functionality for the request's query parameters. Underneath it uses `serde_urlencoded` crate.
{{< include-example example="extractors" file="query.rs" section="query" >}}
<CodeBlock example="extractors" file="query.rs" section="query" />
# Json
## Json
[`Json<T>`][jsonstruct] allows deserialization of a request body into a struct. To extract typed information from a request's body, the type `T` must implement `serde::Deserialize`.
{{< include-example example="extractors" file="json_one.rs" section="json-one" >}}
<CodeBlock example="extractors" file="json_one.rs" section="json-one" />
Some extractors provide a way to configure the extraction process. To configure an extractor, pass its configuration object to the resource's `.app_data()` method. In the case of _Json_ extractor it returns a [_JsonConfig_][jsonconfig]. You can configure the maximum size of the JSON payload as well as a custom error handler function.
The following example limits the size of the payload to 4kb and uses a custom error handler.
{{< include-example example="extractors" file="json_two.rs" section="json-two" >}}
<CodeBlock example="extractors" file="json_two.rs" section="json-two" />
# URL-Encoded Forms
@ -52,9 +52,9 @@ A URL-encoded form body can be extracted to a struct, much like `Json<T>`. This
[_FormConfig_][formconfig] allows configuring the extraction process.
{{< include-example example="extractors" file="form.rs" section="form" >}}
<CodeBlock example="extractors" file="form.rs" section="form" />
# Other
## Other
Actix Web also provides several other extractors:
@ -70,15 +70,15 @@ Application state is accessible from the handler with the `web::Data` extractor;
Here is an example of a handler that stores the number of processed requests:
{{< include-example example="request-handlers" file="main.rs" section="data" >}}
<CodeBlock example="request-handlers" file="main.rs" section="data" />
Although this handler will work, `data.count` will only count the number of requests handled _by each worker thread_. To count the number of total requests across all threads, one should use shared `Arc` and [atomics][atomics].
{{< include-example example="request-handlers" file="handlers_arc.rs" section="arc" >}}
<CodeBlock example="request-handlers" file="handlers_arc.rs" section="arc" />
**Note**: If you want the _entire_ state to be shared across all threads, use `web::Data` and `app_data` as described in [Shared Mutable State][shared_mutable_state].
Be careful when using blocking synchronization primitives like `Mutex` or `RwLock` within your app state. Actix Web handles requests asynchronously. It is a problem if the [_critical section_](critical_section) in your handler is too big or contains an `.await` point. If this is a concern, we would advise you to also read [Tokio's advice on using blocking `Mutex` in async code](tokio_std_mutex).
Be careful when using blocking synchronization primitives like `Mutex` or `RwLock` within your app state. Actix Web handles requests asynchronously. It is a problem if the [_critical section_][critical_section] in your handler is too big or contains an `.await` point. If this is a concern, we would advise you to also read [Tokio's advice on using blocking `Mutex` in async code][tokio_std_mutex].
[pathstruct]: https://docs.rs/actix-web/4/actix_web/dev/struct.Path.html
[querystruct]: https://docs.rs/actix-web/4/actix_web/web/struct.Query.html
@ -92,6 +92,6 @@ Be careful when using blocking synchronization primitives like `Mutex` or `RwLoc
[docsrs_match_info]: https://docs.rs/actix-web/latest/actix_web/struct.HttpRequest.html#method.match_info
[actix]: https://actix.github.io/actix/actix/
[atomics]: https://doc.rust-lang.org/std/sync/atomic/
[shared_mutable_state]: ../application#shared-mutable-state
[shared_mutable_state]: /docs/application#shared-mutable-state
[critical_section]: https://en.wikipedia.org/wiki/Critical_section
[tokio_std_mutex]: https://tokio.rs/tokio/tutorial/shared-state#on-using-stdsyncmutex

View File

@ -1,14 +1,18 @@
---
title: Getting Started
menu: docs_basics
weight: 130
---
import RenderCodeBlock from '@theme/CodeBlock';
import CodeBlock from "@site/src/components/code_block.js";
import { rustVersion, actixWebMajorVersion } from "@site/vars";
## Installing Rust
If you don't have Rust yet, we recommend you use `rustup` to manage your Rust installation. The [official rust guide][rustguide] has a wonderful section on getting started.
Actix Web currently has a minimum supported Rust version (MSRV) of {{< rust-version "actix-web" >}}. Running `rustup update` will ensure you have the latest and greatest Rust version available. As such, this guide assumes you are running Rust {{< rust-version "actix-web" >}} or later.
<p>
Actix Web currently has a minimum supported Rust version (MSRV) of { rustVersion }. Running <code>rustup update</code> will ensure you have the latest and greatest Rust version available. As such, this guide assumes you are running Rust { rustVersion } or later.
</p>
## Hello, world!
@ -21,20 +25,23 @@ cd hello-world
Add `actix-web` as a dependency of your project by adding the following to your `Cargo.toml` file.
```toml
[dependencies]
actix-web = "{{< actix-version "actix-web" >}}"
```
<!-- DEPENDENCY -->
<RenderCodeBlock className="language-toml">
{`[dependencies]
actix-web = "${actixWebMajorVersion}"`}
</RenderCodeBlock>
Request handlers use async functions that accept zero or more parameters. These parameters can be extracted from a request (see `FromRequest` trait) and returns a type that can be converted into an `HttpResponse` (see `Responder` trait):
{{< include-example example="getting-started" section="handlers" >}}
<CodeBlock example="getting-started" section="handlers" />
Notice that some of these handlers have routing information attached directly using the built-in macros. These allow you to specify the method and path that the handler should respond to. You will see below how to register `manual_hello` (i.e. routes that do not use a routing macro).
Next, create an `App` instance and register the request handlers. Use `App::service` for the handlers using routing macros and `App::route` for manually routed handlers, declaring the path and method. Finally, the app is started inside an `HttpServer` which will serve incoming requests using your `App` as an "application factory".
{{< include-example example="getting-started" section="main" >}}
<CodeBlock example="getting-started" section="main" />
That's it! Compile and run the program with `cargo run`. The `#[actix_web::main]` macro executes the async main function within the actix runtime. Now you can go to `http://127.0.0.1:8080/` or any of the other routes you defined to see the results.

View File

@ -1,9 +1,9 @@
---
title: Handlers
menu: docs_basics
weight: 160
---
import CodeBlock from "@site/src/components/code_block.js";
# Request Handlers
A request handler is an async function that accepts zero or more parameters that can be extracted from a request (i.e., [_impl FromRequest_][implfromrequest]) and returns a type that can be converted into an HttpResponse (i.e., [_impl Responder_][respondertrait]).
@ -48,13 +48,13 @@ To return a custom type directly from a handler function, the type needs to impl
Let's create a response for a custom type that serializes to an `application/json` response:
{{< include-example example="responder-trait" file="main.rs" section="responder-trait" >}}
<CodeBlock example="responder-trait" file="main.rs" section="responder-trait" />
## Streaming response body
Response body can be generated asynchronously. In this case, body must implement the stream trait `Stream<Item=Bytes, Error=Error>`, i.e.:
{{< include-example example="async-handlers" file="stream.rs" section="stream" >}}
<CodeBlock example="async-handlers" file="stream.rs" section="stream" />
## Different return types (Either)
@ -62,7 +62,7 @@ Sometimes, you need to return different types of responses. For example, you can
For this case, the [Either][either] type can be used. `Either` allows combining two different responder types into a single type.
{{< include-example example="either" file="main.rs" section="either" >}}
<CodeBlock example="either" file="main.rs" section="either" />
[implfromrequest]: https://docs.rs/actix-web/4/actix_web/trait.FromRequest.html
[respondertrait]: https://docs.rs/actix-web/4/actix_web/trait.Responder.html

View File

@ -1,10 +1,12 @@
---
title: HTTP/2
menu: docs_protocols
weight: 250
---
Actix Web will prefer HTTP/2 connections if the client signals support for it via [TLS ALPN][tlsalpn].
import RenderCodeBlock from '@theme/CodeBlock';
import CodeBlock from '@site/src/components/code_block.js';
import { actixWebMajorVersion } from "@site/vars";
`actix-web` automatically upgrades connections to *HTTP/2* if possible.
# Negotiation
@ -12,13 +14,17 @@ Actix Web will prefer HTTP/2 connections if the client signals support for it vi
When either of the `rustls` or `openssl` features are enabled, `HttpServer` provides the [bind_rustls][bindrustls] method and [bind_openssl][bindopenssl] methods, respectively.
```toml
[dependencies]
actix-web = { version = "{{< actix-version "actix-web" >}}", features = ["openssl"] }
openssl = { version = "0.10", features = ["v110"] }
```
<!-- DEPENDENCY -->
{{< include-example example="http2" file="main.rs" section="main" >}}
<RenderCodeBlock className="language-toml">
{`[dependencies]
actix-web = { version = "${actixWebMajorVersion}", features = ["openssl"] }
openssl = { version = "0.10", features = ["v110"] }
`}
</RenderCodeBlock>
<CodeBlock example="http2" file="main.rs" section="main" />
Upgrades to HTTP/2 described in [RFC 7540 §3.2][rfcsection32] are not supported. Starting HTTP/2 with prior knowledge is supported for both cleartext and TLS connections ([RFC 7540 §3.4][rfcsection34]) (when using the lower level `actix-http` service builders).

View File

@ -1,10 +1,8 @@
---
title: HTTP Server Initialization
menu: docs_architecture
weight: 1020
---
## Architecture overview
# Architecture overview
Below is a diagram of HttpServer initialization, which happens on the following code

View File

@ -1,9 +1,9 @@
---
title: Middleware
menu: docs_advanced
weight: 220
---
import CodeBlock from "@site/src/components/code_block.js";
# Middleware
Actix Web's middleware system allows us to add additional behavior to request/response processing. Middleware can hook into an incoming request process, enabling us to modify requests as well as halt request processing to return a response early.
@ -21,23 +21,23 @@ Middleware is registered for each `App`, `scope`, or `Resource` and executed in
The following demonstrates creating a simple middleware:
{{< include-example example="middleware" file="main.rs" section="simple" >}}
<CodeBlock example="middleware" file="main.rs" section="simple" />
Alternatively, for simple use cases, you can use [_wrap_fn_][wrap_fn] to create small, ad-hoc middleware:
{{< include-example example="middleware" file="wrap_fn.rs" section="wrap-fn" >}}
<CodeBlock example="middleware" file="wrap_fn.rs" section="wrap-fn" />
> Actix Web provides several useful middleware, such as _logging_, _user sessions_, _compress_, etc.
**Warning: if you use `wrap()` or `wrap_fn()` multiple times, the last occurrence will be executed first.**
# Logging
## Logging
Logging is implemented as a middleware. It is common to register a logging middleware as the first middleware for the application. Logging middleware must be registered for each application.
The `Logger` middleware uses the standard log crate to log information. You should enable logger for _actix_web_ package to see access log ([env_logger][envlogger] or similar).
## Usage
### Usage
Create `Logger` middleware with the specified `format`. Default `Logger` can be created with `default` method, it uses the default format:
@ -45,7 +45,7 @@ Create `Logger` middleware with the specified `format`. Default `Logger` can be
%a %t "%r" %s %b "%{Referer}i" "%{User-Agent}i" %T
```
{{< include-example example="middleware" file="logger.rs" section="logger" >}}
<CodeBlock example="middleware" file="logger.rs" section="logger" />
The following is an example of the default logging format:
@ -54,7 +54,7 @@ INFO:actix_web::middleware::logger: 127.0.0.1:59934 [02/Dec/2017:00:21:43 -0800]
INFO:actix_web::middleware::logger: 127.0.0.1:59947 [02/Dec/2017:00:22:40 -0800] "GET /index.html HTTP/1.1" 200 0 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.13; rv:57.0) Gecko/20100101 Firefox/57.0" 0.000646
```
## Format
### Format
- `%%` The percent sign
- `%a` Remote IP-address (IP-address of proxy if using reverse proxy)
@ -69,11 +69,11 @@ INFO:actix_web::middleware::logger: 127.0.0.1:59947 [02/Dec/2017:00:22:40 -0800]
- `%{FOO}o` response.headers['FOO']
- `%{FOO}e` os.environ['FOO']
## Default headers
### Default headers
To set default response headers, the `DefaultHeaders` middleware can be used. The _DefaultHeaders_ middleware does not set the header if response headers already contain a specified header.
{{< include-example example="middleware" file="default_headers.rs" section="default-headers" >}}
<CodeBlock example="middleware" file="default_headers.rs" section="default-headers" />
## User sessions
@ -91,15 +91,15 @@ The constructors take a key as an argument. This is the private key for cookie s
In general, you create a `SessionStorage` middleware and initialize it with specific backend implementation, such as a `CookieSession`. To access session data the [`Session`][requestsession] extractor must be used. This method returns a [_Session_][sessionobj] object, which allows us to get or set session data.
{{< include-example example="middleware" file="user_sessions.rs" section="user-session" >}}
<CodeBlock example="middleware" file="user_sessions.rs" section="user-session" />
# Error handlers
## Error handlers
`ErrorHandlers` middleware allows us to provide custom handlers for responses.
You can use the `ErrorHandlers::handler()` method to register a custom error handler for a specific status code. You can modify an existing response or create a completly new one. The error handler can return a response immediately or return a future that resolves into a response.
{{< include-example example="middleware" file="errorhandler.rs" section="error-handler" >}}
<CodeBlock example="middleware" file="errorhandler.rs" section="error-handler" />
[sessionobj]: https://docs.rs/actix-session/0.3.0/actix_session/struct.Session.html
[requestsession]: https://docs.rs/actix-session/0.3.0/actix_session/struct.Session.html

View File

@ -1,7 +1,5 @@
---
title: Requests
menu: docs_advanced
weight: 200
---
# JSON Request
@ -28,17 +26,17 @@ futures = "0.3"
If you want to add default value for a field, refer to `serde`'s [documentation](https://serde.rs/attr-default.html).
{{< include-example example="requests" file="main.rs" section="json-request" >}}
<CodeBlock example="requests" file="main.rs" section="json-request" />
You may also manually load the payload into memory and then deserialize it.
In the following example, we will deserialize a _MyObj_ struct. We need to load the request body first and then deserialize the json into an object.
{{< include-example example="requests" file="manual.rs" section="json-manual" >}}
<CodeBlock example="requests" file="manual.rs" section="json-manual" />
> A complete example for both options is available in [examples directory][examples].
# Content Encoding
## Content Encoding
Actix Web automatically _decompresses_ payloads. The following codecs are supported:
@ -49,17 +47,17 @@ Actix Web automatically _decompresses_ payloads. The following codecs are suppor
If request headers contain a `Content-Encoding` header, the request payload is decompressed according to the header value. Multiple codecs are not supported, i.e: `Content-Encoding: br, gzip`.
# Chunked transfer encoding
## Chunked transfer encoding
Actix automatically decodes _chunked_ encoding. The [`web::Payload`][payloadextractor] extractor already contains the decoded byte stream. If the request payload is compressed with one of the supported compression codecs (br, gzip, deflate), then the byte stream is decompressed.
# Multipart body
## Multipart body
Actix Web provides multipart stream support with an external crate, [`actix-multipart`][multipartcrate].
> A full example is available in the [examples directory][multipartexample].
# Urlencoded body
## Urlencoded body
Actix Web provides support for _application/x-www-form-urlencoded_ encoded bodies with the [`web::Form`][formencoded] extractor which resolves to the deserialized instance. The type of the instance must implement the `Deserialize` trait from _serde_.
@ -70,15 +68,15 @@ The _UrlEncoded_ future can resolve into an error in several cases:
- content-length is greater than 256k
- payload terminates with error.
{{< include-example example="requests" file="urlencoded.rs" section="urlencoded" >}}
<CodeBlock example="requests" file="urlencoded.rs" section="urlencoded" />
# Streaming request
## Streaming request
_HttpRequest_ is a stream of `Bytes` objects. It can be used to read the request body payload.
In the following example, we read and print the request payload chunk by chunk:
{{< include-example example="requests" file="streaming.rs" section="streaming" >}}
<CodeBlock example="requests" file="streaming.rs" section="streaming" />
[examples]: https://github.com/actix/examples/tree/master/json/json
[multipartstruct]: https://docs.rs/actix-multipart/0.2/actix_multipart/struct.Multipart.html

View File

@ -1,9 +1,9 @@
---
title: Responses
menu: docs_advanced
weight: 210
---
import CodeBlock from "@site/src/components/code_block.js";
# Response
A builder-like pattern is used to construct an instance of `HttpResponse`. `HttpResponse` provides several methods that return a `HttpResponseBuilder` instance, which implements various convenience methods for building responses.
@ -12,9 +12,9 @@ A builder-like pattern is used to construct an instance of `HttpResponse`. `Http
The methods `.body`, `.finish`, and `.json` finalize response creation and return a constructed _HttpResponse_ instance. If this methods is called on the same builder instance multiple times, the builder will panic.
{{< include-example example="responses" file="main.rs" section="builder" >}}
<CodeBlock example="responses" file="main.rs" section="builder" />
# JSON Response
## JSON Response
The `Json` type allows to respond with well-formed JSON data: simply return a value of type `Json<T>` where `T` is the type of a structure to serialize into _JSON_. The type `T` must implement the `Serialize` trait from _serde_.
@ -25,11 +25,11 @@ For the following example to work, you need to add `serde` to your dependencies
serde = { version = "1.0", features = ["derive"] }
```
{{< include-example example="responses" file="json_resp.rs" section="json-resp" >}}
<CodeBlock example="responses" file="json_resp.rs" section="json-resp" />
Using the `Json` type this way instead of calling the `.json` method on a `HttpResponse` makes it immediately clear that the function returns JSON and not any other type of response.
# Content encoding
## Content encoding
Actix Web can automatically _compress_ payloads with the [_Compress middleware_][compressmidddleware]. The following codecs are supported:
@ -38,7 +38,7 @@ Actix Web can automatically _compress_ payloads with the [_Compress middleware_]
- Deflate
- Identity
{{< include-example example="responses" file="compress.rs" section="compress" >}}
<CodeBlock example="responses" file="compress.rs" section="compress" />
Response payload is compressed based on the _encoding_ parameter from the `middleware::BodyEncoding` trait. By default, `ContentEncoding::Auto` is used. If `ContentEncoding::Auto` is selected, then the compression depends on the request's `Accept-Encoding` header.
@ -46,23 +46,23 @@ Response payload is compressed based on the _encoding_ parameter from the `middl
For example, to enable `brotli` for a single handler use `ContentEncoding::Br`:
{{< include-example example="responses" file="brotli.rs" section="brotli" >}}
<CodeBlock example="responses" file="brotli.rs" section="brotli" />
or for the entire application:
{{< include-example example="responses" file="brotli_two.rs" section="brotli-two" >}}
<CodeBlock example="responses" file="brotli_two.rs" section="brotli-two" />
In this case we explicitly disable content compression by setting content encoding to an `Identity` value:
{{< include-example example="responses" file="identity.rs" section="identity" >}}
<CodeBlock example="responses" file="identity.rs" section="identity" />
When dealing with an already compressed body (for example when serving assets), set the content encoding to `Identity` to avoid compressing the already compressed data and set the `content-encoding` header manually:
{{< include-example example="responses" file="identity_two.rs" section="identity-two" >}}
<CodeBlock example="responses" file="identity_two.rs" section="identity-two" />
Also it is possible to set default content encoding on application level, by default `ContentEncoding::Auto` is used, which implies automatic content compression negotiation.
{{< include-example example="responses" file="auto.rs" section="auto" >}}
<CodeBlock example="responses" file="auto.rs" section="auto" />
[responsebuilder]: https://docs.rs/actix-web/4/actix_web/struct.HttpResponseBuilder.html
[compressmidddleware]: https://docs.rs/actix-web/4/actix_web/middleware/struct.Compress.html

View File

@ -1,9 +1,11 @@
---
title: Server
menu: docs_basics
weight: 150
---
import RenderCodeBlock from '@theme/CodeBlock';
import CodeBlock from '@site/src/components/code_block.js';
import { actixWebMajorVersion } from "@site/vars";
# The HTTP Server
The [**HttpServer**][httpserverstruct] type is responsible for serving HTTP requests.
@ -14,13 +16,13 @@ To start the web server it must first be bound to a network socket. Use [`HttpSe
After the `bind` is successful, use [`HttpServer::run()`][httpserver_run] to return a [`Server`][server] instance. The `Server` must be `await`ed or `spawn`ed to start processing requests and will run until it receives a shutdown signal (such as, by default, a `ctrl-c`; [read more here](#graceful-shutdown)).
{{< include-example example="server" section="main" >}}
<CodeBlock example="server" section="main" />
## Multi-Threading
`HttpServer` automatically starts a number of HTTP _workers_, by default this number is equal to the number of logical CPUs in the system. This number can be overridden with the [`HttpServer::workers()`][workers] method.
{{< include-example example="server" file="workers.rs" section="workers" >}}
<CodeBlock example="server" file="workers.rs" section="workers" />
Once the workers are created, they each receive a separate _application_ instance to handle requests. Application state is not shared between the threads, and handlers are free to manipulate their copy of the state with no concurrency concerns.
@ -56,13 +58,16 @@ Actix Web supports two TLS implementations out-of-the-box: `rustls` and `openssl
The `rustls` crate feature is for `rustls` integration and `openssl` is for `openssl` integration.
```toml
[dependencies]
actix-web = { version = "{{< actix-version "actix-web" >}}", features = ["openssl"] }
openssl = { version = "0.10" }
```
<!-- DEPENDENCY -->
{{< include-example example="server" file="ssl.rs" section="ssl" >}}
<RenderCodeBlock className="language-toml">
{`[dependencies]
actix-web = { version = "${actixWebMajorVersion}", features = ["openssl"] }
openssl = { version = "0.10" }
`}
</RenderCodeBlock>
<CodeBlock example="server" file="ssl.rs" section="ssl" />
To create the key.pem and cert.pem use the command. **Fill in your own subject**
@ -87,11 +92,12 @@ Actix Web keeps connections open to wait for subsequent requests.
- `KeepAlive::Os`: uses OS keep-alive.
- `None` or `KeepAlive::Disabled`: disables keep-alive.
{{< include-example example="server" file="keep_alive.rs" section="keep-alive" >}}
<CodeBlock example="server" file="keep_alive.rs" section="keep-alive" />
If the first option above is selected, then keep-alive is enabled for HTTP/1.1 requests if the response does not explicitly disallow it by, for example, setting the [connection type][httpconnectiontype] to `Close` or `Upgrade`. Force closing a connection can be done with [the `force_close()` method on `HttpResponseBuilder`](https://docs.rs/actix-web/4/actix_web/dev/struct.HttpResponseBuilder.html#method.force_close)
> Keep-alive is **off** for HTTP/1.0 and is **on** for HTTP/1.1 and HTTP/2.0.
<CodeBlock example="server" file="keep_alive_tp.rs" section="example" />
## Graceful shutdown

View File

@ -1,26 +1,26 @@
---
title: Static Files
menu: docs_advanced
weight: 230
---
import CodeBlock from "@site/src/components/code_block.js";
# Individual file
It is possible to serve static files with a custom path pattern and `NamedFile`. To match a path tail, we can use a `[.*]` regex.
{{< include-example example="static-files" file="main.rs" section="individual-file" >}}
<CodeBlock example="static-files" file="main.rs" section="individual-file" />
# Directory
## Directory
To serve files from specific directories and sub-directories, `Files` can be used. `Files` must be registered with an `App::service()` method, otherwise it will be unable to serve sub-paths.
{{< include-example example="static-files" file="directory.rs" section="directory" >}}
<CodeBlock example="static-files" file="directory.rs" section="directory" />
By default files listing for sub-directories is disabled. Attempt to load directory listing will return _404 Not Found_ response. To enable files listing, use [_Files::show_files_listing()_][showfileslisting] method.
Instead of showing files listing for directory, it is possible to redirect to a specific index file. Use the [_Files::index_file()_][indexfile] method to configure this redirect.
# Configuration
## Configuration
`NamedFiles` can specify various options for serving files:
@ -30,11 +30,11 @@ Instead of showing files listing for directory, it is possible to redirect to a
All of the above methods are optional and provided with the best defaults, But it is possible to customize any of them.
{{< include-example example="static-files" file="configuration.rs" section="config-one" >}}
<CodeBlock example="static-files" file="configuration.rs" section="config-one" />
The Configuration can also be applied to directory service:
{{< include-example example="static-files" file="configuration_two.rs" section="config-two" >}}
<CodeBlock example="static-files" file="configuration_two.rs" section="config-two" />
[showfileslisting]: https://docs.rs/actix-files/0.2/actix_files/struct.Files.html
[indexfile]: https://docs.rs/actix-files/0.2/actix_files/struct.Files.html#method.index_file

View File

@ -1,20 +1,20 @@
---
title: Testing
menu: docs_advanced
weight: 215
---
import CodeBlock from "@site/src/components/code_block.js";
# Testing
Every application should be well tested. Actix Web provides tools to perform unit and integration tests.
# Unit Tests
## Unit Tests
For unit testing, actix-web provides a request builder type. [_TestRequest_][testrequest] implements a builder-like pattern. You can generate a `HttpRequest` instance with `to_http_request()` and call your handler with it.
{{< include-example example="testing" file="main.rs" section="unit-tests" >}}
<CodeBlock example="testing" file="main.rs" section="unit-tests" />
# Integration tests
## Integration tests
There are a few methods for testing your application. Actix Web can be used to run the application with specific handlers in a real HTTP server.
@ -24,17 +24,17 @@ To create a `Service` for testing, use the `test::init_service` method which acc
> Check the [API documentation][actixdocs] for more information.
{{< include-example example="testing" file="integration_one.rs" section="integration-one" >}}
<CodeBlock example="testing" file="integration_one.rs" section="integration-one" />
If you need more complex application configuration, testing should be very similar to creating the normal application. For example, you may need to initialize application state. Create an `App` with a `data` method and attach state just like you would from a normal application.
{{< include-example example="testing" file="integration_two.rs" section="integration-two" >}}
<CodeBlock example="testing" file="integration_two.rs" section="integration-two" />
# Stream response tests
## Stream response tests
If you need to test stream generation, it would be enough to call `take_body()` and convert a resulting [_ResponseBody_][responsebody] into a future and execute it, for example when testing [_Server Sent Events_][serversentevents].
{{< include-example example="testing" file="stream_response.rs" section="stream-response" >}}
<CodeBlock example="testing" file="stream_response.rs" section="stream-response" />
[serversentevents]: https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events
[responsebody]: https://docs.rs/actix-web/4/actix_web/body/enum.ResponseBody.html

View File

@ -1,30 +1,30 @@
---
title: URL Dispatch
menu: docs_advanced
weight: 190
---
import CodeBlock from "@site/src/components/code_block.js";
# URL Dispatch
URL dispatch provides a simple way for mapping URLs to handler code using a simple pattern matching language. If one of the patterns matches the path information associated with a request, a particular handler object is invoked.
> A request handler is a function that accepts zero or more parameters that can be extracted from a request (i.e., [_impl FromRequest_][implfromrequest]) and returns a type that can be converted into an HttpResponse (i.e., [_impl Responder_][implresponder]). More information is available in the [handler section][handlersection].
# Resource configuration
## Resource configuration
Resource configuration is the act of adding a new resources to an application. A resource has a name, which acts as an identifier to be used for URL generation. The name also allows developers to add routes to existing resources. A resource also has a pattern, meant to match against the _PATH_ portion of a _URL_ (the portion following the scheme and port, e.g. _/foo/bar_ in the _URL_ _http://localhost:8080/foo/bar?q=value_). It does not match against the _QUERY_ portion (the portion that follows _?_, e.g. _q=value_ in _http://localhost:8080/foo/bar?q=value_).
The [_App::route()_][approute] method provides simple way of registering routes. This method adds a single route to application routing table. This method accepts a _path pattern_, _HTTP method_ and a handler function. `route()` method could be called multiple times for the same path, in that case, multiple routes register for the same resource path.
{{< include-example example="url-dispatch" section="main" >}}
<CodeBlock example="url-dispatch" section="main" />
While _App::route()_ provides simple way of registering routes, to access complete resource configuration, a different method has to be used. The [_App::service()_][appservice] method adds a single [resource][webresource] to application routing table. This method accepts a _path pattern_, guards, and one or more routes.
{{< include-example example="url-dispatch" file="resource.rs" section="resource" >}}
<CodeBlock example="url-dispatch" file="resource.rs" section="resource" />
If a resource does not contain any route or does not have any matching routes, it returns _NOT FOUND_ HTTP response.
## Configuring a Route
### Configuring a Route
Resource contains a set of routes. Each route in turn has a set of `guards` and a handler. New routes can be created with `Resource::route()` method which returns a reference to new _Route_ instance. By default the _route_ does not contain any guards, so matches all requests and the default handler is `HttpNotFound`.
@ -32,7 +32,7 @@ The application routes incoming requests based on route criteria which are defin
> A _Route_ can contain any number of _guards_ but only one handler.
{{< include-example example="url-dispatch" file="cfg.rs" section="cfg" >}}
<CodeBlock example="url-dispatch" file="cfg.rs" section="cfg" />
In this example, `HttpResponse::Ok()` is returned for _GET_ requests if the request contains `Content-Type` header, the value of this header is _text/plain_, and path equals to `/path`.
@ -44,7 +44,7 @@ If a resource can not match any route, a "NOT FOUND" response is returned.
- [_Route::method()_][routemethod] registers a method guard. Any number of guards can be registered for each route.
- [_Route::to()_][routeto] registers an async handler function for this route. Only one handler can be registered. Usually handler registration is the last config operation.
# Route matching
## Route matching
The main purpose of route configuration is to match (or not match) the request's `path` against a URL path pattern. `path` represents the path portion of the URL that was requested.
@ -54,7 +54,7 @@ When a route configuration is declared, it may contain route guard arguments. Al
If any route matches, the route matching process stops and the handler associated with the route is invoked. If no route matches after all route patterns are exhausted, a _NOT FOUND_ response get returned.
# Resource pattern syntax
## Resource pattern syntax
The syntax of the pattern matching language used by actix in the pattern argument is straightforward.
@ -100,7 +100,7 @@ The match for a segment replacement marker in a segment will be done only up to
foo/{name}.html
```
The literal path _/foo/biz.html_ will match the above route pattern, and the match result will be `Params {'name': 'biz'}`. However, the literal path _/foo/biz_ will not match, because it does not contain a literal _.html_ at the end of the segment represented by _{name}.html_ (it only contains biz, not biz.html).
The literal path `/foo/biz.html` will match the above route pattern, and the match result will be `Params {'name': 'biz'}`. However, the literal path `/foo/biz` will not match, because it does not contain a literal `.html` at the end of the segment represented by `{name}.html` (it only contains biz, not biz.html).
To capture both segments, two replacement markers can be used:
@ -108,14 +108,14 @@ To capture both segments, two replacement markers can be used:
foo/{name}.{ext}
```
The literal path _/foo/biz.html_ will match the above route pattern, and the match result will be _Params {'name': 'biz', 'ext': 'html'}_. This occurs because there is a literal part of _._ (period) between the two replacement markers _{name}_ and _{ext}_.
The literal path `/foo/biz.html` will match the above route pattern, and the match result will be `Params {'name': 'biz', 'ext': 'html'}`. This occurs because there is a literal part of `.` (period) between the two replacement markers `{name}` and `{ext}`.
Replacement markers can optionally specify a regular expression which will be used to decide whether a path segment should match the marker. To specify that a replacement marker should match only a specific set of characters as defined by a regular expression, you must use a slightly extended form of replacement marker syntax. Within braces, the replacement marker name must be followed by a colon, then directly thereafter, the regular expression. The default regular expression associated with a replacement marker _[^/]+_ matches one or more characters which are not a slash. For example, under the hood, the replacement marker _{foo}_ can more verbosely be spelled as _{foo:[^/]+}_. You can change this to be an arbitrary regular expression to match an arbitrary sequence of characters, such as _{foo:\d+}_ to match only digits.
Replacement markers can optionally specify a regular expression which will be used to decide whether a path segment should match the marker. To specify that a replacement marker should match only a specific set of characters as defined by a regular expression, you must use a slightly extended form of replacement marker syntax. Within braces, the replacement marker name must be followed by a colon, then directly thereafter, the regular expression. The default regular expression associated with a replacement marker `[^/]+` matches one or more characters which are not a slash. For example, under the hood, the replacement marker `{foo}` can more verbosely be spelled as `{foo:[^/]+}`. You can change this to be an arbitrary regular expression to match an arbitrary sequence of characters, such as `{foo:\d+}` to match only digits.
Segments must contain at least one character in order to match a segment replacement marker. For example, for the URL _/abc/_:
Segments must contain at least one character in order to match a segment replacement marker. For example, for the URL `/abc/`:
- _/abc/{foo}_ will not match.
- _/{foo}/_ will match.
- `/abc/{foo}` will not match.
- `/{foo}/` will match.
> **Note**: path will be URL-unquoted and decoded into valid unicode string before matching pattern and values representing matched path segments will be URL-unquoted too.
@ -162,7 +162,7 @@ foo/1/2/ -> Params {'bar': '1', 'tail': '2/'}
foo/abc/def/a/b/c -> Params {'bar': 'abc', 'tail': 'def/a/b/c'}
```
# Scoping Routes
## Scoping Routes
Scoping helps you organize routes sharing common root paths. You can nest scopes within scopes.
@ -174,47 +174,47 @@ Suppose that you want to organize paths to endpoints used to view "Users". Such
A scoped layout of these paths would appear as follows
{{< include-example example="url-dispatch" file="scope.rs" section="scope" >}}
<CodeBlock example="url-dispatch" file="scope.rs" section="scope" />
A _scoped_ path can contain variable path segments as resources. Consistent with un-scoped paths.
You can get variable path segments from `HttpRequest::match_info()`. [`Path` extractor][pathextractor] also is able to extract scope level variable segments.
# Match information
## Match information
All values representing matched path segments are available in [`HttpRequest::match_info`][matchinfo]. Specific values can be retrieved with [`Path::get()`][pathget].
{{< include-example example="url-dispatch" file="minfo.rs" section="minfo" >}}
<CodeBlock example="url-dispatch" file="minfo.rs" section="minfo" />
For this example for path '/a/1/2/', values v1 and v2 will resolve to "1" and "2".
## Path information extractor
### Path information extractor
Actix provides functionality for type safe path information extraction. [_Path_][pathstruct] extracts information, destination type could be defined in several different forms. Simplest approach is to use `tuple` type. Each element in tuple must correspond to one element from path pattern. i.e. you can match path pattern `/{id}/{username}/` against `Path<(u32, String)>` type, but `Path<(String, String, String)>` type will always fail.
{{< include-example example="url-dispatch" file="path.rs" section="path" >}}
<CodeBlock example="url-dispatch" file="path.rs" section="path" />
It also possible to extract path pattern information to a struct. In this case, this struct must implement _serde's_ `Deserialize` trait.
{{< include-example example="url-dispatch" file="path2.rs" section="path" >}}
<CodeBlock example="url-dispatch" file="path2.rs" section="path" />
[_Query_][query] provides similar functionality for request query parameters.
# Generating resource URLs
## Generating resource URLs
Use the [_HttpRequest.url_for()_][urlfor] method to generate URLs based on resource patterns. For example, if you've configured a resource with the name "foo" and the pattern "{a}/{b}/{c}", you might do this:
{{< include-example example="url-dispatch" file="urls.rs" section="url" >}}
<CodeBlock example="url-dispatch" file="urls.rs" section="url" />
This would return something like the string *http://example.com/test/1/2/3* (at least if the current protocol and hostname implied http://example.com). `url_for()` method returns [_Url object_][urlobj] so you can modify this url (add query parameters, anchor, etc). `url_for()` could be called only for _named_ resources otherwise error get returned.
# External resources
## External resources
Resources that are valid URLs, can be registered as external resources. They are useful for URL generation purposes only and are never considered for matching at request time.
{{< include-example example="url-dispatch" file="url_ext.rs" section="ext" >}}
<CodeBlock example="url-dispatch" file="url_ext.rs" section="ext" />
# Path normalization and redirecting to slash-appended routes
## Path normalization and redirecting to slash-appended routes
By normalizing it means:
@ -223,7 +223,7 @@ By normalizing it means:
The handler returns as soon as it finds a path that resolves correctly. The order of normalization conditions, if all are enabled, is 1) merge, 2) both merge and append and 3) append. If the path resolves with at least one of those conditions, it will redirect to the new path.
{{< include-example example="url-dispatch" file="norm.rs" section="norm" >}}
<CodeBlock example="url-dispatch" file="norm.rs" section="norm" />
In this example `//resource///` will be redirected to `/resource/`.
@ -231,35 +231,35 @@ In this example, the path normalization handler is registered for all methods, b
It is possible to register path normalization only for _GET_ requests only:
{{< include-example example="url-dispatch" file="norm2.rs" section="norm" >}}
<CodeBlock example="url-dispatch" file="norm2.rs" section="norm" />
## Using an Application Prefix to Compose Applications
### Using an Application Prefix to Compose Applications
The `web::scope()` method allows to set a specific application scope. 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 included callable's author intended while still maintaining the same resource names.
For example:
{{< include-example example="url-dispatch" file="scope.rs" section="scope" >}}
<CodeBlock example="url-dispatch" file="scope.rs" section="scope" />
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 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.
# Custom route guard
## Custom route guard
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`][guardtrait] trait. Actix provides several predicates, you can check [functions section][guardfuncs] of API docs.
Here is a simple guard that check that a request contains a specific _header_:
{{< include-example example="url-dispatch" file="guard.rs" section="guard" >}}
<CodeBlock example="url-dispatch" file="guard.rs" section="guard" />
In this example, _index_ handler will be called only if request contains _CONTENT-TYPE_ header.
Guards can not access or modify the request object, but it is possible to store extra information in [request extensions][requestextensions].
## Modifying guard values
### Modifying guard values
You can invert the meaning of any predicate value by wrapping it in a `Not` predicate. For example, if you want to return "METHOD NOT ALLOWED" response for all methods except "GET":
{{< include-example example="url-dispatch" file="guard2.rs" section="guard2" >}}
<CodeBlock example="url-dispatch" file="guard2.rs" section="guard2" />
The `Any` guard accepts a list of guards and matches if any of the supplied guards match. i.e:
@ -273,13 +273,13 @@ The `All` guard accepts a list of guard and matches if all of the supplied guard
guard::All(guard::Get()).and(guard::Header("content-type", "plain/text"))
```
# Changing the default Not Found response
## Changing the default Not Found response
If the path pattern can not be found in the routing table or a resource can not find matching route, the default resource is used. The default response is _NOT FOUND_. It is possible to override the _NOT FOUND_ response with `App::default_service()`. This method accepts a _configuration function_ same as normal resource configuration with `App::service()` method.
{{< include-example example="url-dispatch" file="dhandler.rs" section="default" >}}
<CodeBlock example="url-dispatch" file="dhandler.rs" section="default" />
[handlersection]: ../handlers/
[handlersection]: /docs/handlers/
[approute]: https://docs.rs/actix-web/4/actix_web/struct.App.html#method.route
[appservice]: https://docs.rs/actix-web/4/actix_web/struct.App.html?search=#method.service
[webresource]: https://docs.rs/actix-web/4/actix_web/struct.Resource.html
@ -299,4 +299,4 @@ If the path pattern can not be found in the routing table or a resource can not
[requestextensions]: https://docs.rs/actix-web/4/actix_web/struct.HttpRequest.html#method.extensions
[implfromrequest]: https://docs.rs/actix-web/4/actix_web/trait.FromRequest.html
[implresponder]: https://docs.rs/actix-web/4/actix_web/trait.Responder.html
[pathextractor]: ../extractors
[pathextractor]: /docs/extractors

View File

@ -1,14 +1,17 @@
---
title: Websockets
menu: docs_protocols
weight: 240
---
import CodeBlock from "@site/src/components/code_block.js";
# Websockets
Actix Web supports WebSockets with the `actix-web-actors` crate. It is possible to convert a request's `Payload` to a stream of [_ws::Message_][message] with a [_web::Payload_][payload] and then use stream combinators to handle actual messages, but it is simpler to handle websocket communications with an http actor.
The following is an example of a simple websocket echo server:
{{< include-example example="websockets" file="main.rs" section="websockets" >}}
<CodeBlock example="websockets" file="main.rs" section="websockets" />
> A simple websocket echo server example is available in the [examples directory][examples].

View File

@ -1,10 +1,7 @@
---
title: Documentation
title: Welcome
description: Guiding you through building web apps with Actix
menu:
docs_intro:
name: Welcome
weight: 10
slug: /
---
# Welcome to Actix

View File

@ -1,8 +1,7 @@
---
title: What is Actix Web
menu: docs_intro
weight: 100
---
import { rustVersion } from "@site/vars";
# Actix Web is part of an Ecosystem of Crates
@ -15,7 +14,9 @@ We call Actix Web a powerful and pragmatic framework. For all intents and purpos
An application developed with Actix Web will expose an HTTP server contained within a native executable. You can either put this behind another HTTP server like nginx or serve it up as-is. Even in the complete absence of another HTTP server Actix Web is powerful enough to provide HTTP/1 and HTTP/2 support as well as TLS (HTTPS). This makes it useful for building small services ready for production.
Most importantly: Actix Web runs on Rust {{< rust-version "actix-web" >}} or later and it works with stable releases.
<p>
Most importantly: Actix Web runs on Rust { rustVersion } or later and it works with stable releases.
</p>
<!-- TODO -->
<!-- which is built upon the fantastic [Tokio][tokio] asynchronous I/O system -->

67
docusaurus.config.js Normal file
View File

@ -0,0 +1,67 @@
const path = require('path');
module.exports = {
title: 'Actix',
tagline: 'Actix Web is a powerful, pragmatic, and extremely fast web framework for Rust',
url: 'https://actix.rs',
baseUrl: '/actix-website/',
onBrokenLinks: 'throw',
onBrokenMarkdownLinks: 'warn',
favicon: 'img/logo.png',
organizationName: 'actix', // Usually your GitHub org/user name.
projectName: 'actix-web', // Usually your repo name.
themeConfig: {
navbar: {
title: 'Actix',
logo: {
alt: 'Actix Logo',
src: 'img/logo.png',
},
items: [
{
to: 'docs',
activeBasePath: 'docs',
label: 'Documentation',
position: 'left',
},
{
to: 'community',
activeBasePath: 'community',
label: 'Community',
position: 'left',
},
{
to: 'code',
activeBasePath: 'code',
label: 'Code',
position: 'left',
},
],
},
footer: {
copyright: `Copyright © ${new Date().getFullYear()} The Actix Team`,
},
prism: {
// dracula is closest to docs.rs, where keywords are highlighted
theme: require('prism-react-renderer/themes/dracula'),
additionalLanguages: ['rust', 'toml'],
defaultLanguage: 'rust'
}
},
plugins: ["docusaurus-plugin-sass"],
presets: [
[
'@docusaurus/preset-classic',
{
docs: {
sidebarPath: require.resolve('./sidebars.js'),
editUrl:
'https://github.com/actix/actix-website/edit/master/',
},
theme: {
customCss: require.resolve('./src/css/custom.css'),
},
},
],
],
};

View File

@ -57,7 +57,7 @@ async fn index(pool: web::Data<DbPool>, name: web::Path<(String)>) -> impl Respo
eprintln!("{}", e);
HttpResponse::InternalServerError().finish()
})?;
Ok(HttpResponse::Ok().json(user))
}
// </index>

View File

@ -1,6 +1,7 @@
// <easy-form-handling>
use actix_web::{web, App, HttpResponse, HttpServer, Responder};
use serde::Deserialize;
// <easy-form-handling>
use actix_web::web::{Either, Json, Form};
#[derive(Deserialize)]
struct Register {
@ -8,25 +9,33 @@ struct Register {
country: String,
}
// register form is JSON
async fn json_register(form: web::Json<Register>) -> impl Responder {
format!("Hello {} from {}!", form.username, form.country)
}
// register form can be either JSON or URL-encoded
async fn register(form: Either<Json<Register>, Form<Register>>) -> impl Responder {
let Register { username, country } = form.into_inner();
format!("Hello {username} from {country}!")
}
// </easy-form-handling>
async fn index() -> HttpResponse {
HttpResponse::Ok()
.content_type("text/html; charset=utf-8")
.body(include_str!("../static/form.html"))
}
async fn register(form: web::Form<Register>) -> impl Responder {
format!("Hello {} from {}!", form.username, form.country)
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(|| {
App::new()
.route("/", web::get().to(index))
.route("/register", web::post().to(register))
.route("/json_register", web::post().to(json_register))
})
.bind(("127.0.0.1", 8080))?
.run()
.await
}
// </easy-form-handling>

View File

@ -14,6 +14,7 @@ async fn hello_world() -> impl Responder {
async fn current_temperature() -> impl Responder {
web::Json(Measurement { temperature: 42.3 })
}
// </flexible-responders>
#[actix_web::main]
async fn main() -> std::io::Result<()> {
@ -26,4 +27,3 @@ async fn main() -> std::io::Result<()> {
.run()
.await
}
// </flexible-responders>

View File

@ -1,6 +1,7 @@
use actix_web::{web, App, HttpResponse, HttpServer, Responder};
use serde::{Deserialize, Serialize};
// <powerful-extractors>
#[derive(Deserialize, Serialize)]
struct Event {
id: Option<i32>,
@ -9,6 +10,12 @@ struct Event {
tags: Vec<String>,
}
async fn capture_event(evt: web::Json<Event>) -> impl Responder {
let new_event = store_in_db(evt.timestamp, &evt.kind, &evt.tags);
format!("got event {}", new_event.id.unwrap())
}
// </powerful-extractors>
fn store_in_db(timestamp: f64, kind: &str, tags: &[String]) -> Event {
// store item in db and get new_event
// use id to lookup item
@ -20,10 +27,6 @@ fn store_in_db(timestamp: f64, kind: &str, tags: &[String]) -> Event {
}
}
async fn capture_event(evt: web::Json<Event>) -> impl Responder {
let new_event = store_in_db(evt.timestamp, &evt.kind, &evt.tags);
format!("got event {}", new_event.id.unwrap())
}
async fn index() -> HttpResponse {
HttpResponse::Ok()

View File

@ -1,6 +1,7 @@
// <request-routing>
use actix_web::{web, App, HttpRequest, HttpServer, Responder};
use actix_web::{get, web, App, HttpRequest, HttpServer, Responder};
#[get("/")]
async fn index(_req: HttpRequest) -> impl Responder {
"Hello from the index page."
}
@ -13,8 +14,8 @@ async fn hello(path: web::Path<String>) -> impl Responder {
async fn main() -> std::io::Result<()> {
HttpServer::new(|| {
App::new()
.service(web::resource("/").to(index))
.service(web::resource("/{name}").to(hello))
.service(index)
.route("/{name}", web::get().to(hello))
})
.bind(("127.0.0.1", 8080))?
.run()

View File

@ -0,0 +1 @@
max_width = 80

View File

@ -0,0 +1,14 @@
// <example>
use actix_web::{http, HttpRequest, HttpResponse};
async fn index(req: HttpRequest) -> HttpResponse {
HttpResponse::Ok()
.connection_type(http::ConnectionType::Close) // <- Close connection
.force_close() // <- Alternative method
.finish()
}
// </example>
// ConnectionType::Close
// ConnectionType::KeepAlive
// ConnectionType::Upgrade

View File

@ -0,0 +1,18 @@
// <pbuf>
use actix_web::{get, App, HttpRequest, HttpServer, Result};
use std::path::PathBuf;
#[get("/a/{tail:.*}")]
async fn index(req: HttpRequest) -> Result<String> {
let path: PathBuf = req.match_info().query("tail").parse().unwrap();
Ok(format!("Path {:?}", path))
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(|| App::new().service(index))
.bind("127.0.0.1:8080")?
.run()
.await
}
// </pbuf>

View File

@ -1,11 +0,0 @@
[home]
other = "Home"
[docs]
other = "Documentation"
[community]
other = "Community"
[code]
other = "Code"

View File

@ -1,27 +0,0 @@
{{ partial "header" . }}
{{ $currentURL := .RelPermalink }}
<div class="actix-pageheader">
<div class="container">
<h1 class="display-4">404: Page Not Found</h1>
<p class="lead">What you were looking for was not here</p>
</div>
</div>
<div class="container actix-community">
<div class="row">
<div class="col-md-6 offset-md-3">
<div class="actix-content">
<h1>Not Found</h1>
<p>We could not find the page you were looking for. This website was
recently restructured so maybe a link went dead.
<ul>
<li><a href="/">Home page</a>
<li><a href="/docs/">Documentation</a>
</ul>
</div>
</div>
</div>
</div>
{{ partial "footer" . }}

View File

@ -1,23 +0,0 @@
{{ partial "header" . }}
{{ $currentURL := .RelPermalink }}
<div class="actix-pageheader">
<div class="container">
<h1 class="display-4">{{ .Title }}</h1>
{{ if .Description }}
<p class="lead">{{ .Description }}</p>
{{ end }}
</div>
</div>
<div class="container actix-community">
<div class="row">
<div class="col-md-6 offset-md-3">
<div class="actix-content">
{{ block "main" . }}{{ end }}
</div>
</div>
</div>
</div>
{{ partial "footer" . }}

View File

@ -1,3 +0,0 @@
{{ define "main" }}
{{ .Content }}
{{ end }}

View File

@ -1,4 +0,0 @@
{{ define "main" }}
<p class="uplink"><a href="/code/">↑ Back to code</a>
{{ .Content }}
{{ end }}

View File

@ -1,23 +0,0 @@
{{ partial "header" . }}
{{ $currentURL := .RelPermalink }}
<div class="actix-pageheader">
<div class="container">
<h1 class="display-4">{{ .Title }}</h1>
{{ if .Description }}
<p class="lead">{{ .Description }}</p>
{{ end }}
</div>
</div>
<div class="container actix-community">
<div class="row">
<div class="col-md-6 offset-md-3">
<div class="actix-content">
{{ block "main" . }}{{ end }}
</div>
</div>
</div>
</div>
{{ partial "footer" . }}

View File

@ -1,3 +0,0 @@
{{ define "main" }}
{{ .Content }}
{{ end }}

View File

@ -1,4 +0,0 @@
{{ define "main" }}
<p class="uplink"><a href="/community/">↑ Back to community</a>
{{ .Content }}
{{ end }}

View File

@ -1,123 +0,0 @@
{{ partial "header" . }}
{{ $currentURL := .RelPermalink }}
<div class="actix-pageheader">
<div class="container">
<h1 class="display-4">{{ .Title }}</h1>
{{ if .Description }}
<p class="lead">{{ .Description }}</p>
{{ end }}
</div>
</div>
<div class="container actix-docs">
<div class="row">
<div class="col-md-3">
<button class="btn doctoggle" type="button" data-toggle="collapse" data-target="#collapsing-docnav" aria-expanded="false" aria-controls="collapsing-docnav">
Toggle navigation
</button>
<nav class="leftnav collapse show" id="collapsing-docnav">
<div class="" id="">
<h5>Introduction</h5>
<div>
<ul class="nav">
{{ range .Site.Menus.docs_intro }}
<li{{ if eq $currentURL .URL }} class="active"{{ end }}>
<a href="{{ .URL }}">{{ .Name }}</a>
</li>
{{ end }}
</ul>
</div>
<h5>Basics</h5>
<div>
<ul class="nav">
{{ range .Site.Menus.docs_basics }}
<li{{ if eq $currentURL .URL }} class="active"{{ end }}>
<a href="{{ .URL }}">{{ .Name }}</a>
</li>
{{ end }}
</ul>
</div>
<h5>Advanced</h5>
<div>
<ul class="nav">
{{ range .Site.Menus.docs_advanced }}
<li{{ if eq $currentURL .URL }} class="active"{{ end }}>
<a href="{{ .URL }}">{{ .Name }}</a>
</li>
{{ end }}
</ul>
</div>
<h5>Protocols</h5>
<div>
<ul class="nav">
{{ range .Site.Menus.docs_protocols }}
<li{{ if eq $currentURL .URL }} class="active"{{ end }}>
<a href="{{ .URL }}">{{ .Name }}</a>
</li>
{{ end }}
</ul>
</div>
<h5>Patterns</h5>
<div>
<ul class="nav">
{{ range .Site.Menus.docs_patterns }}
<li{{ if eq $currentURL .URL }} class="active"{{ end }}>
<a href="{{ .URL }}">{{ .Name }}</a>
</li>
{{ end }}
</ul>
</div>
<h5>Diagrams</h5>
<div>
<ul class="nav">
{{ range .Site.Menus.docs_architecture }}
<li{{ if eq $currentURL .URL }} class="active"{{ end }}>
<a href="{{ .URL }}">{{ .Name }}</a>
</li>
{{ end }}
</ul>
</div>
<h5>API Documentation</h5>
<div>
<ul class="nav">
<li><a href="https://docs.rs/actix" target="view_window">actix <span class="fa fa-external-link"></span></a></li>
<li><a href="https://docs.rs/actix-web/" target="view_window">actix-web <span class="fa fa-external-link"></span></a></li>
</ul>
</div>
</div>
</nav>
</div>
<div class="col-md-9">
<div class="actix-content">
{{ block "main" . }}{{ end }}
<div class="github-edit">
<a class="fa fa-github" href="https://github.com/actix/actix-website/tree/master/content/{{ .File.Path }}"> Edit on GitHub</a>
</div>
{{ if eq (len .Pages) 0 }}
{{ .Scratch.Set "Docs" .Parent.Pages.ByWeight }}
{{ else }}
{{ .Scratch.Set "Docs" .Pages.ByWeight }}
{{ end }}
{{ .Scratch.Set "CurrentWeight" .Weight }}
{{ range first 1 (where (.Scratch.Get "Docs") ".Weight" ">" (.Scratch.Get "CurrentWeight")) }}
<div class="actix-next"><b>Next up</b>: <a href = {{ .RelPermalink }}>{{ .Title }}</a></div>
{{ end }}
</div>
</div>
</div>
</div>
{{ partial "footer" . }}

View File

@ -1,3 +0,0 @@
{{ define "main" }}
{{ .Content }}
{{ end }}

View File

@ -1,3 +0,0 @@
{{ define "main" }}
{{ .Content }}
{{ end }}

View File

@ -1,175 +0,0 @@
{{ partial "header" . }}
<div id="act-home">
<div class="jumbotron">
<div class="actix-jumbotron">
<img src="/img/logo-large.png" class="align-middle actix-logo" alt="">
<p class="lead">
A powerful, pragmatic, and extremely fast web framework for Rust
</p>
<a href="/docs/getting-started/" class="btn btn-secondary actix-jumbotron-install">
Get Started
</a>
</div>
</div>
<div class="container actix-home">
<div class="row">
<div class="col-md-4">
<div class="actix-features">
<h2>
<i class="fa fa-fw fa-shield" aria-hidden="true"></i>
Type Safe
</h2>
<p>Forget about stringly typed objects, from request to response, everything has types.</p>
<h2>
<i class="fa fa-fw fa-battery-full" aria-hidden="true"></i>
Feature Rich
</h2>
<p>
Out of the box logging, body compression, static file serving, TLS, HTTP/2, and
much more.
</p>
<h2>
<i class="fa fa-fw fa-puzzle-piece" aria-hidden="true"></i>
Extensible
</h2>
<p>Easily create and share reusable components for any Actix Web application.</p>
<h2>
<i class="fa fa-fw fa-dashboard" aria-hidden="true"></i>
Blazingly Fast
</h2>
<p>Actix Web is blazingly fast. Don't take our word for it -- <a
href="https://www.techempower.com/benchmarks/#section=data-r20&hw=ph&test=fortune">see for yourself!</a></p>
</div>
</div>
<div class="col-md-8">
<div class="actix-content">
{{ highlight `use actix_web::{get, web, App, HttpServer, Responder};
#[get("/hello/{name}")]
async fn greet(name: web::Path<String>) -> impl Responder {
format!("Hello {name}!")
}
#[actix_web::main] // or #[tokio::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(|| {
App::new()
.route("/hello", web::get().to(|| async { "Hello World!" }))
.service(greet)
})
.bind(("127.0.0.1", 8080))?
.run()
.await
}
` "rust" "" }}
</div>
</div>
</div>
<div class="actix-showcase">
<div class="col-md-9">
<div class="actix-feature" id="responders">
<h2>Flexible Responders</h2>
<p>
Handler functions in Actix Web can return a wide range of objects that
implement the <code>Responder</code> trait. This makes it a breeze
to return consistent responses from your APIs.
</p>
{{ highlight `async fn current_temperature() -> impl Responder {
web::Json(json!({ "temperature": 42.3 }))
}
async fn hello_world() -> actix_web::Result<impl Responder> {
Ok("Hello World!")
}` "rust" "" }}
</div>
<div class="actix-feature" id="extractors">
<h2>Powerful Extractors</h2>
<p>
Actix Web comes with a powerful extractor system that extracts parts of the incoming
HTTP request and passes it to your handler functions.
</p>
<p>
A handler function can receive up to 12 arguments that implement the
<code>FromRequest</code> trait, in any order, and Actix Web will automatically extract
them from the request and provide them. It feels like magic!
</p>
{{ highlight `#[derive(Deserialize, Serialize)]
struct EventForm {
kind: String,
tags: Vec<String>,
}
async fn capture_event(evt: web::Json<EventForm>, db: web::Data<Db>) -> impl Responder {
let new_event = db.store(&evt.kind, &evt.tags).await;
format!("got event {}", new_event.id.unwrap())
}` "rust" "" }}
</div>
<div class="actix-feature" id="forms">
<h2>Easy Form Handling</h2>
<p>
Handling multipart/urlencoded form data is easy. Just define a structure that can be
deserialized and Actix Web will handle the rest.
</p>
{{ highlight `use actix_web::web::{Either, Json, Form};
#[derive(Deserialize)]
struct Register {
username: String,
country: String,
}
// register form is JSON
async fn register(form: web::Json<Register>) -> impl Responder {
format!("Hello {} from {}!", form.username, form.country)
}
// register form can be either JSON or URL-encoded
async fn register(form: Either<Json<Register>, Form<Register>>) -> impl Responder {
let Register { username, country } = form.into_inner();
format!("Hello {username} from {country}!")
}` "rust" "" }}
</div>
<div class="actix-feature" id="routing">
<h2>Request Routing</h2>
<p>
The built-in Actix Web request router can be used with or without macros attached to
handlers, and always provides flexible and composable methods of creating routing
tables.
</p>
<p>
Includes support for matching dynamic path segments, path prefix groups, and custom
routing guards which let you define your own rules.
</p>
{{ highlight `#[get("/")]
async fn index(_req: HttpRequest) -> impl Responder {
"Hello from the index page!"
}
async fn hello(path: web::Path<String>) -> impl Responder {
format!("Hello {}!", &path)
}
let app = App::new()
.service(index)
.route("/{name}", web::get().to(hello));
` "rust" "" }}
</div>
</div>
<div class="col-md-3 actix-feature-selectors">
<ul>
<li class="actix-feature-selector"><a href="#responders">flexible responders</a></li>
<li class="actix-feature-selector"><a href="#extractors">powerful extractors</a></li>
<li class="actix-feature-selector"><a href="#forms">easy form handling</a></li>
<li class="actix-feature-selector"><a href="#routing">request routing</a></li>
</ul>
</div>
</div>
</div>
</div>
{{ partial "footer" . }}

View File

@ -1,19 +0,0 @@
<footer class="actix-footer">
<div class="text-muted actix-footer-gray d-flex justify-content-between">
<div class="actix-footer-info">
Copyright © 2022 The Actix Team
</div>
<div class="actix-footer-social">
<a href="https://github.com/actix" class="text-muted"><i class="fa fa-github" aria-hidden="true"></i></a>
</div>
</div>
</footer>
<!-- jQuery first, then Tether, then Bootstrap JS. -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js" integrity="sha384-3ceskX3iaEnIogmQchP8opvBy3Mi7Ce34nWjpBIwVTHfGYWQS9jwHDVRnpKKHJg7" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/tether/1.3.7/js/tether.min.js" integrity="sha384-XTs3FgkjiBgo8qjEjBk0tGmf3wPrWtA6coPfQDfFEY8AnYJwjalXCiosYRBIBZX8" crossorigin="anonymous"></script>
<script src="/js/bootstrap.min.js"></script>
<script src="/js/actix.js"></script>
{{ template "_internal/google_analytics.html" . }}
</body>
</html>

View File

@ -1,59 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<link rel="stylesheet" href="/css/bootstrap-reboot.css">
<link rel="stylesheet" href="/css/bootstrap.css">
<link rel="stylesheet" href="/css/font-awesome.min.css">
<link rel="stylesheet" href="/css/actix.css">
<link rel="stylesheet" href="/css/highlight.css">
<link rel="icon" href="/favicon.ico" title="actix">
<title>{{ .Title }}</title>
</head>
<body>
<header class="navbar navbar-light navbar-toggleable-md bd-navbar">
<nav class="actix-main-nav">
<div class="d-flex justify-content-between hidden-lg-up">
<a href="/" class="navbar-brand">
<img src="/img/logo-nav.png" class="align-middle" alt="">
</a>
<button class="navbar-toggle collapsed" type="button" data-toggle="collapse" data-target="#actix-main-nav" aria-label="Toggle navigation" aria-controls="actix-main-nav" aria-expanded="false">
<span class="navbar-toggler-icon"></span>
</button>
</div>
<div class="navbar-collapse collapse" id="actix-main-nav">
<ul class="nav navbar-nav">
<li class="nav-item hd-lg-down">
<a class="navbar-brand" href="{{ "/" | absLangURL }}"><img src="/img/logo-nav.png" class="align-middle" alt=""></a>
</li>
<li class="nav-item">
<a class="nav-link" href="{{ "/" | absLangURL }}">{{ T "home" }}</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{{ "/docs/" | absLangURL }}">{{ T "docs" }}</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{{ "/community/" | absLangURL }}">{{ T "community" }}</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{{ "/code/" | absLangURL }}">{{ T "code" }}</a>
</li>
{{ if gt 0 (len $.Site.Home.AllTranslations) }}
<li class="nav-item language-selector">
<i class="fa fa-fw fa-globe" aria-hidden="false"></i>
<ul class="subitem">
{{ range $.Site.Home.AllTranslations }}
<li class="submenu-item"><a href="{{ .Permalink }}">{{ .Language.LanguageName }}</a></li>
{{ end }}
</ul>
</li>
{{ end }}
</ul>
</div>
</nav>
</header>

View File

@ -1,7 +0,0 @@
{{- if eq (.Get 0) "actix" -}}
{{- .Page.Site.Params.actixVersion -}}
{{- else if eq (.Get 0) "actix-web" -}}
{{- .Page.Site.Params.actixWebVersion -}}
{{- else if eq (.Get 0) "actix-rt" -}}
{{- .Page.Site.Params.actixRtVersion -}}
{{- end -}}

View File

@ -1,3 +0,0 @@
<div class="alert alert-info" role="alert">
{{ .Inner | markdownify }}
</div>

View File

@ -1,7 +0,0 @@
{{$example := .Get "example"}}
{{$file := .Get "file" | default "main.rs"}}
{{$section := .Get "section"}}
{{$source := readFile (printf "examples/%s/src/%s" $example $file)}}
{{$source := index (findRE (printf "(?ms)^// <%s>$(.*?)^// </%s>$" $section $section) $source) 0 }}
{{$source := trim (replaceRE "(?m)^// <[^>]+>" "" $source) "\n" }}
{{- highlight $source "rust" "" -}}

View File

@ -1,5 +0,0 @@
{{- if eq (.Get 0) "actix" -}}
{{- .Page.Site.Params.actixMinRustVersion -}}
{{- else if eq (.Get 0) "actix-web" -}}
{{- .Page.Site.Params.actixWebMinRustVersion -}}
{{- end -}}

22059
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

44
package.json Normal file
View File

@ -0,0 +1,44 @@
{
"name": "actix-website",
"version": "2.0.0",
"private": true,
"scripts": {
"docusaurus": "docusaurus",
"start": "docusaurus start",
"build": "docusaurus build",
"swizzle": "docusaurus swizzle",
"deploy": "docusaurus deploy",
"clear": "docusaurus clear",
"serve": "docusaurus serve",
"write-translations": "docusaurus write-translations",
"write-heading-ids": "docusaurus write-heading-ids"
},
"dependencies": {
"@docusaurus/core": "^2.0.0-beta",
"@docusaurus/preset-classic": "^2.0.0-beta",
"@fortawesome/fontawesome-svg-core": "^1.2.35",
"@fortawesome/free-solid-svg-icons": "^5.15.3",
"@fortawesome/react-fontawesome": "^0.1.14",
"@mdx-js/react": "^1.6.21",
"clsx": "^1.1.1",
"docusaurus-plugin-sass": "^0.2.2",
"react": "^17.0.1",
"react-dom": "^17.0.1",
"sass": "^1.53.0"
},
"browserslist": {
"production": [
">0.5%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"devDependencies": {
"raw-loader": "^4.0.2"
}
}

48
sidebars.js Normal file
View File

@ -0,0 +1,48 @@
module.exports = {
docs: {
'Introduction': [
'welcome',
'whatis'
],
'Basics': [
'getting-started',
'application',
'server',
'extractors',
'handlers',
],
'Advanced': [
'errors',
'url-dispatch',
'request',
'response',
'testing',
'middleware',
'static-files',
],
'Protocols': [
'websockets',
'http2',
],
'Patterns': [
'autoreload',
'databases',
],
'Diagrams': [
'http_server_init',
'conn_lifecycle',
],
'API Documentation': [
{
type: 'link',
label: 'actix',
href: 'https://docs.rs/actix/latest/actix/'
},
{
type: 'link',
label: 'actix-web',
href: 'https://docs.rs/actix-web/latest/actix_web/'
}
],
},
};

View File

@ -0,0 +1,27 @@
import React, { useState, useEffect } from 'react';
import RenderCodeBlock from '@theme/CodeBlock';
const CodeBlock = ({ example, file, section }) => {
const [code, setCode] = useState("");
useEffect(() => {
let isMounted = true;
import(`!!raw-loader!@site/examples/${example}/src/${file || "main.rs"}`)
.then(source => {
source = source
.default
.match(new RegExp(`\/\/ <${section}>\n([\\s\\S]*)\/\/ <\/${section}>`))[1];
if (isMounted) setCode(source)
})
.catch(err => console.log(err));
return () => {
isMounted = false;
}
}, [])
return (
<RenderCodeBlock className="language-rust">{code}</RenderCodeBlock>
)
}
export default CodeBlock;

53
src/css/custom.css Normal file
View File

@ -0,0 +1,53 @@
:root {
--background-color-secondary: #f8f8f8;
--examples-odd-background-color: var(--background-color-secondary);
--logo-filter: unset;
/* docusaurus vars
https://docusaurus.io/docs/styling-layout#styling-your-site-with-infima
*/
--ifm-color-primary: #7036ab;
--ifm-color-primary-dark: #65319a;
--ifm-color-primary-darker: #5f2e91;
--ifm-color-primary-darkest: #4e2678;
--ifm-color-primary-light: #7b3bbc;
--ifm-color-primary-lighter: #8140c3;
--ifm-color-primary-lightest: #925acb;
--ifm-font-size-base: 16px;
--ifm-code-font-size: 90%;
--ifm-background-color: #fff;
--ifm-footer-background-color: var(--ifm-background-surface-color);
--ifm-menu-color-background-active: var(--ifm-color-emphasis-200);
}
html[data-theme='dark'] {
--background-color-secondary: #0a191c;
--examples-odd-background-color: var(--ifm-hero-background-color);
--logo-filter: brightness(0) invert(1);
/* docusaurus vars */
--ifm-background-color: #0a141c;
--ifm-color-primary: #e98ef5;
--ifm-color-primary-dark: #e26af2;
--ifm-color-primary-darker: #df59f0;
--ifm-color-primary-darkest: #d423ec;
--ifm-color-primary-light: #f0b2f8;
--ifm-color-primary-lighter: #f3c3fa;
--ifm-color-primary-lightest: #fef9fe;
--ifm-footer-background-color: #0a141c;
--ifm-navbar-background-color: var(--ifm-background-color);
--ifm-hero-background-color: var(--background-color-secondary);
--ifm-menu-color-background-active: #21243d;
}
.docusaurus-highlight-code-line {
background-color: rgb(72, 77, 91);
display: block;
margin: 0 calc(-1 * var(--ifm-pre-padding));
padding: 0 var(--ifm-pre-padding);
}
.navbar__logo {
filter: var(--logo-filter);
}

View File

@ -1,6 +1,6 @@
---
title: Contributor Covenant Code of Conduct
description:
description: The code of conduct
---
# Our Pledge

View File

@ -9,4 +9,5 @@ Want to talk to others about questions? The Actix Web [Discord server](https://d
If you think you found a bug it's best to file an issue directly on the repo. [actix/actix-web](https://github.com/actix/actix-web) for the web framework. [actix/actix](https://github.com/actix/actix) for issues with the actor framework
We're a welcoming community so don't be afraid to engage. Interactions are [governed by our code of conduct](coc/).
We're a welcoming community so don't be afraid to engage. Interactions are
[governed by our code of conduct](community/coc).

210
src/pages/index.js Normal file
View File

@ -0,0 +1,210 @@
import clsx from 'clsx';
import React from 'react';
import Link from '@docusaurus/Link';
import Layout from '@theme/Layout';
import CodeBlock from '../components/code_block.js';
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
import useBaseUrl from '@docusaurus/useBaseUrl';
import styles from './styles.module.scss';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import {
faShieldAlt,
faBatteryFull,
faPuzzlePiece,
faTachometerAlt,
} from '@fortawesome/free-solid-svg-icons';
const Home = () => {
const context = useDocusaurusContext();
const siteConfig = context;
return (
<Layout description={siteConfig.tagline}>
<Hero />
<main className={styles.main}>
<MainExample />
<Highlights />
<Examples />
</main>
</Layout>
);
};
const highlights = [
{
icon: faShieldAlt,
title: 'Type Safe',
description: (
<>
Forget about stringly typed objects, from request to response,
everything has types.
</>
),
},
{
icon: faBatteryFull,
title: 'Feature Rich',
description: (
<>Actix provides a lot of features out of box. HTTP/2, logging, etc.</>
),
},
{
icon: faPuzzlePiece,
title: 'Extensible',
description: (
<>Easily create your own libraries that any Actix application can use.</>
),
},
{
icon: faTachometerAlt,
title: 'Blazingly Fast',
description: (
<>
Actix is blazingly fast. Don't take our word for it -- see for yourself!
</>
),
},
];
const Hero = () => {
const context = useDocusaurusContext();
const { siteConfig } = context;
return (
<header id="hero" className={clsx('hero', styles.banner)}>
<div className="container">
<img
src={useBaseUrl(`img/logo.png`)}
alt="Actix Logo"
className={styles.logo}
/>
<h1 className="hero__title">{siteConfig.title}</h1>
<p className={clsx('hero__subtitle', styles.subtitle)}>
{siteConfig.tagline}
</p>
<div className={styles.buttons}>
<Link
className="button button--primary button--lg"
to={useBaseUrl('docs/')}
>
Get Started
</Link>
</div>
</div>
</header>
);
};
const MainExample = () => (
<div className={styles.main_example}>
<div className={styles.main_example__code}>
<CodeBlock example="main-example" section="main-example" />
</div>
</div>
);
const Highlights = () => {
return (
<>
<section id="highlights" className={styles.highlights}>
<div className="container">
<div className="row">
<div className="col col--11 col--offset-1">
<div className="row">
{highlights.map((highlight, idx) => (
<div
className={clsx('col col--6', styles.highlight)}
key={idx}
>
<div className="item">
<div className={styles.header}>
<div className={styles.icon}>
<FontAwesomeIcon icon={highlight.icon} size="lg" />
</div>
<h2 className={styles.title}>{highlight.title}</h2>
</div>
<p>{highlight.description}</p>
</div>
</div>
))}
</div>
</div>
</div>
</div>
</section>
</>
);
};
const Examples = () => {
return (
<div>
<div className={styles.example}>
<div className={styles.featureText}>
<h3 className={styles.featureTitle}>Flexible Responders</h3>
<p>
Handler functions in actix can return a wide range of objects that
implement the <code>Responder</code> trait. This makes it a breeze
to return consistent responses from your APIs.
</p>
</div>
<div className={styles.example__code}>
<CodeBlock
example="flexible-responders"
section="flexible-responders"
/>
</div>
</div>
<div className={styles.example}>
<div className={styles.featureText}>
<h3 className={styles.featureTitle}>Powerful Extractors</h3>
<p>
Actix comes with a powerful extractor system that extracts data from
the incoming HTTP request and passes it to your view functions. Not
only does this make for a convenient API but it also means that your
view functions can be synchronous code and still benefit from
asynchronous IO handling.
</p>
</div>
<div className={styles.example__code}>
<CodeBlock
example="powerful-extractors"
section="powerful-extractors"
/>
</div>
</div>
<div className={styles.example}>
<div className={styles.featureText}>
<h3 className={styles.featureTitle}>Easy Form Handling</h3>
<p>
Handling multipart/urlencoded form data is easy. Just define a
structure that can be deserialized and actix will handle the rest.
</p>
</div>
<div className={styles.example__code}>
<CodeBlock
example="easy-form-handling"
section="easy-form-handling"
/>
</div>
</div>
<div className={styles.example}>
<div className={styles.featureText}>
<h3 className={styles.featureTitle}>Request Routing</h3>
<p>
An actix app comes with a URL routing system that lets you match on
URLs and invoke individual handlers. For extra flexibility, scopes
can be used.
</p>
</div>
<div className={styles.example__code}>
<CodeBlock example="request-routing" section="request-routing" />
</div>
</div>
</div>
);
};
export default Home;

View File

@ -0,0 +1,183 @@
.main {
section {
padding: 5rem 0;
@media screen and (max-width: 996px) {
padding: 2.5rem 0;
}
}
}
.highlights {
display: flex;
align-items: center;
width: 100%;
padding-top: 3rem;
.highlight {
p {
margin-bottom: 0;
}
&:not(:last-child) {
margin-bottom: 3rem;
}
@media screen and (min-width: 576px) {
margin-bottom: 3rem;
text-align: center;
}
.header {
display: flex;
flex-direction: row;
align-items: center;
margin-bottom: 1rem;
@media screen and (min-width: 576px) {
justify-content: center;
}
.icon {
display: flex;
align-items: center;
margin-right: 1rem;
color: var(--ifm-color-primary);
}
.title {
font-size: 1.25rem;
margin-bottom: 0;
}
}
}
}
.banner {
padding: 4rem 0;
text-align: center;
position: relative;
overflow: hidden;
// background-image: url('/img/jumbotron.jpg');
// background-size: cover;
// color: #fff;
@media screen and (max-width: 966px) {
padding: 2rem;
}
}
.logo {
max-width: 160px;
filter: var(--logo-filter);
}
.buttons {
display: flex;
align-items: center;
justify-content: center;
}
.subtitle {
margin: 0 auto 2rem;
max-width: 500px;
}
.headline {
margin-bottom: 2rem;
}
.category {
display: inline-flex;
align-items: center;
margin-bottom: 1rem;
font-weight: bold;
text-transform: uppercase;
color: var(--ifm-color-primary-light);
}
.title {
max-width: 500px;
font-size: 2rem;
line-height: initial;
@media screen and (min-width: 576px) {
font-size: 2.4rem;
}
}
.subtitle {
margin-top: 2rem;
color: var(--ifm-color-emphasis-600);
}
.main_example {
display: flex;
justify-content: center;
padding-left: 1rem;
padding-right: 1rem;
flex-wrap: wrap;
padding-top: 2rem;
padding-bottom: 2rem;
}
.main_example__code {
width: 100%;
}
@media (min-width: 1024px) {
.main_example__code {
width: unset;
}
}
// features
.example {
display: flex;
width: 100%;
flex-direction: column;
flex-wrap: wrap;
justify-content: space-around;
align-items: center;
padding-left: 1rem;
padding-right: 1rem;
padding-top: 2rem;
padding-bottom: 2rem;
}
.example__code {
width: 100%;
}
.featureText {
justify-self: center;
padding-bottom: 1rem;
}
@media (min-width: 1024px) {
.example {
flex-direction: row;
padding-top: 7rem;
padding-bottom: 7rem;
}
.example:nth-child(odd) {
flex-direction: row-reverse;
background-color: var(--examples-odd-background-color);
}
.featureText {
width: 33%;
}
.example__code {
width: unset;
}
}
.featureTitle {
font-size: 36px;
font-weight: 500;
}
// Hide the title in docs pages, we render our own in markdown
article header h1 {
display: none;
}

13
src/plugin.js Normal file
View File

@ -0,0 +1,13 @@
module.exports = function (context, options) {
return {
configureWebpack(config, isServer, utils) {
return {
module: {
rules: [
{ test: /\.rs$/, use: 'raw-loader' },
],
},
};
},
};
};

0
static/.nojekyll Normal file
View File

View File

@ -1,762 +0,0 @@
@import url('https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,600|Roboto+Mono');
body {
font-family: Source Sans Pro, sans-serif;
}
a {
color: #306ebf;
}
a:hover {
color: #071540;
}
table,
td {
border: 1px solid black;
}
td {
padding: 4px;
}
img {
max-width: 100%;
}
/*
*
* ===== Content ====
*
*/
.actix-content {
line-height: 1.75rem;
}
.actix-content h1 {
margin-top: 2rem;
margin-bottom: 1.5rem;
font-size: 2.2rem;
}
.actix-content h1:first-child,
.actix-content h2:first-child {
margin-top: 0;
}
.actix-content h2,
.actix-content h3 {
margin-top: 2rem;
margin-bottom: 1rem;
}
.actix-content hr {
margin: 2rem 0;
}
.actix-content figure {
margin: 2.5rem auto;
max-width: 80%;
}
.actix-content figure figcaption {
font-size: 90%;
color: #818a91;
text-align: right;
}
.actix-content figure img {
max-width: 100%;
}
.actix-next {
text-align: right;
}
.github-edit .fa {
font-family: inherit;
}
.github-edit .fa:before {
font-family: FontAwesome;
display: inline-block;
font-style: normal;
font-weight: normal;
line-height: 1;
text-decoration: inherit;
}
.github-edit {
text-align: right;
padding-bottom: 1rem;
}
/*
*
* ===== Navbar =====
*
*/
.navbar-nav {
display: flex;
}
.navbar-nav img {
width: 100%;
}
.navbar-nav .nav-item {
margin-left: 1rem;
margin-right: 1rem;
}
.navbar-nav .nav-link {
padding-top: 0;
padding-bottom: 0;
font-weight: bold;
}
.navbar-brand {
margin-right: 0;
padding-top: 0;
padding-bottom: 0;
width: 52px;
height: auto;
}
@keyframes spin-logo {
100% {
transform: rotate(360deg);
}
}
.navbar-brand img {
animation: spin-logo 30s linear infinite;
width: 48px;
height: 48px;
}
.navbar-toggle {
padding: 0.25rem 0.35rem;
font-size: 1.25rem;
line-height: 1;
height: 2.6rem;
width: 2.8rem;
background: 0 0;
border: 1px solid transparent;
border-radius: 0.25rem;
}
.navbar-light .navbar-toggle {
border-color: rgba(0, 0, 0, 0.1);
color: rgba(0, 0, 0, 0.9);
margin-top: 4px;
}
/* reset border and change color outline */
.navbar-light .navbar-toggle:focus {
outline: none;
border-color: #888;
}
.navbar-toggler-icon {
display: inline-block;
width: 1.5em;
height: 1.5em;
vertical-align: middle;
content: '';
background: no-repeat center center;
-webkit-background-size: 100% 100%;
background-size: 100% 100%;
background-image: url("data:image/svg+xml;charset=utf8,%3Csvg viewBox='0 0 32 32' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath stroke='rgba(0, 0, 0, 0.5)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 8h24M4 16h24M4 24h24'/%3E%3C/svg%3E");
}
.navbar-nav li {
line-height: 60px;
height: 60px;
padding-top: 0;
list-style: none;
}
.navbar-nav .language-selector {
position: relative;
}
.navbar-nav .language-selector ul.subitem {
display: none;
position: absolute;
right: 0;
}
.navbar-nav .language-selector:hover .subitem {
margin: 0;
padding: 0;
display: block;
background-color: #dcfaf7;
}
.navbar-nav .language-selector ul li {
padding: 0;
margin: 0;
height: auto;
line-height: 1;
}
.navbar-nav .language-selector ul li a {
display: block;
padding: 1em;
}
.doctoggle {
margin: -1rem 0 2rem 0;
display: none;
}
.leftnav {
margin: 0 -1rem;
padding: 0 1rem;
}
.leftnav li {
margin: 1rem 0rem;
}
.leftnav li a {
color: #888;
width: 100%;
height: 100%;
word-wrap: break-word;
}
.leftnav li.active {
margin: -0.5rem -1rem -0.5rem -1rem;
padding: 0.5rem 1rem 0.5rem 1rem;
background-color: #dfe6ee;
}
.leftnav li.active a {
color: #163e8c;
}
.leftnav h5 {
margin: 2rem 0 1rem 0;
}
.leftnav h5:first-child {
margin-top: 0;
}
.leftnav a {
font-weight: 600;
}
.in .navbar-nav .nav-item {
text-align: left;
}
.navbar-toggleable-md .navbar-nav {
margin-top: 0;
margin-bottom: 0;
margin-left: auto;
margin-right: auto;
}
.navbar-toggleable-md .navbar-brand {
margin-top: 0;
}
/*
*
* ===== Page header =====
*
*/
.actix-pageheader,
.jumbotron {
background-image: url(/img/jumbotron.jpg);
background-size: cover;
background-color: #071540;
color: #fff;
}
.jumbotron {
border-radius: 0;
padding: 4rem 1rem;
}
.actix-jumbotron {
margin-left: auto;
margin-right: auto;
max-width: 700px;
text-align: center;
}
.jumbotron .actix-logo {
max-height: 10rem;
max-width: 80%;
}
.jumbotron .lead {
font-size: 1.5rem;
margin: 2rem 0 0 0;
}
.actix-pageheader {
padding: 1rem 1rem;
margin-bottom: 2.5rem;
}
.actix-pageheader h1 {
font-weight: 300;
font-size: 3rem;
}
.actix-pageheader .lead {
color: #dadada;
font-size: 1.5rem;
margin-bottom: 0;
}
.actix-pageheader #blog-title {
width: 100%;
height: 100%;
word-wrap: break-word;
}
@media (min-width: 576px) {
.actix-pageheader {
padding: 2rem 0rem;
}
}
/*
*
* ===== Home =====
*
*/
.actix-jumbotron-install {
min-width: 120px;
margin-top: 2rem;
display: inline-block;
}
.actix-features h2 {
font-size: 1.5rem;
margin-top: 2.25rem;
margin-bottom: 0.8rem;
}
.actix-features .fa {
margin-right: 0.4rem;
color: #163e8c;
}
.actix-features p {
font-size: 1rem;
}
.final-pitch {
text-align: center;
}
.actix-showcase {
overflow: hidden;
margin-top: 2rem;
margin-bottom: 2rem;
padding: 2rem 1rem 0 1rem;
background-color: #dfe6ee;
}
.actix-showcase ul {
list-style: none;
margin: 0;
padding: 0;
line-height: 2;
}
.actix-showcase input[type='radio'] {
display: none;
}
.actix-showcase div.feature {
display: none;
}
.actix-showcase input[type='radio']:checked + div.feature {
display: block;
}
.actix-showcase label {
display: block;
font-weight: bold;
color: #163e8c;
cursor: pointer;
}
.actix-showcase label:hover {
color: #003b3b;
}
.actix-feature-selectors {
padding-bottom: 2rem;
}
.actix-feature-selector {
text-align: center;
padding: 0.5rem 1rem;
font-weight: bold;
}
.actix-feature-selector.active {
background-color: white;
}
.actix-feature-selector.active a {
color: #333 !important;
text-decoration: none;
}
/*
*
* ===== Footer =====
*
*/
.actix-footer {
padding: 4rem 0;
margin-top: 4rem;
font-size: 85%;
line-height: 35px;
}
.actix-footer-gray {
padding-left: 1rem;
padding-right: 1rem;
margin: 0 auto;
}
.actix-footer-social a .fa {
font-size: 35px;
}
.actix-footer-social a .fa-github {
margin-left: 35px;
}
/*
*
* ===== Misc =====
*
*/
p.uplink {
margin: 0;
float: right;
}
/*
*
* ===== Highlight =====
*
*/
code {
color: #163e8c;
}
pre,
code {
font-family: 'Roboto Mono', monospace;
}
pre {
display: block;
overflow-x: auto;
margin: 2rem 0rem;
padding: 1rem;
background-color: #f7f7f7;
font-size: 90%;
line-height: 1.4rem;
}
/* Show section header on hover */
h2:hover a:after,
h3:hover a:after,
h4:hover a:after,
h5:hover a:after {
content: '\2002\00a7\2002';
}
h2:hover a,
h3:hover a,
h4:hover a,
h5:hover a {
text-decoration: none;
}
@media (min-width: 520px) {
.jumbotron {
padding: 4rem;
}
}
/*
*
* ===== Utilities =====
*
*/
.d-flex {
display: flex;
}
.justify-content-between {
justify-content: space-between;
}
/*
*
* ===== Media queries =====
*
*/
@media screen and (max-width: 767px) {
.doctoggle {
display: block;
}
}
@media (min-width: 768px) {
.leftnav {
display: block;
}
.bootstrap-vertical-nav .navbar .navbar-collapse {
padding: 0;
max-height: none;
}
.bootstrap-vertical-nav .navbar ul {
float: none;
}
.bootstrap-vertical-nav .navbar ul:not {
display: block;
}
.bootstrap-vertical-nav .navbar li {
float: none;
display: block;
}
.bootstrap-vertical-nav .navbar-nav .nav-item + .nav-item {
margin-left: 0;
}
}
@media screen and (min-width: 480px) {
.hidden-lg-up {
display: none !important;
}
.navbar-collapse {
display: flex !important;
}
}
@media screen and (max-width: 479px) {
.hd-lg-down {
display: none !important;
}
.navbar-toggleable-md > .container {
padding-right: 0;
padding-left: 0;
}
.navbar-brand {
margin-top: 0 !important;
margin-right: 1rem !important;
}
.navbar-nav {
display: flex;
flex-direction: column;
padding-left: 0;
margin-bottom: 0 !important;
margin-top: 0 !important;
list-style: none;
}
.navbar-nav .nav-link {
padding: 0;
}
.navbar-nav li {
line-height: 36px;
height: 36px;
padding-top: 0;
}
.navbar-nav .nav-item + .nav-item {
margin-left: 0;
}
.actix-footer-gray {
text-align: center;
flex-direction: column;
}
.actix-footer-social a .fa-github {
margin-right: 1rem;
}
.navbar-nav .language-selector:hover .subitem {
margin: 0 -2rem 0 -1rem;
display: block;
background-color: #e8f9fc;
}
}
@media (min-width: 480px) and (max-width: 576px) {
header .nav,
#heads-up {
width: 100%;
}
.hd-lg-down {
width: 50%;
}
}
@media (min-width: 576px) and (max-width: 768px) {
header .nav,
#heads-up {
width: 88%;
}
.hd-lg-down {
width: 40%;
}
}
@media (min-width: 768px) and (max-width: 992px) {
header .nav,
#heads-up {
width: 90%;
}
.hd-lg-down {
width: 60%;
}
}
@media (min-width: 992px) {
header .nav,
#heads-up {
width: 75%;
}
.hd-lg-down {
width: 60%;
}
.actix-pageheader .container,
.container {
width: 72%;
}
.actix-footer-gray {
width: 88%;
}
}
#act-cn-tabs {
padding: 2rem 2rem 1rem 2rem;
margin: 2rem auto;
background-color: #dfe6ee;
}
.act-menu li {
text-align: center;
line-height: 44px;
font-size: 15px;
overflow: hidden;
}
.act-menu li.off {
padding: 0 1.5rem;
background-color: #ffffff;
color: #589c9e;
font-weight: bold;
}
@media (min-width: 768px) {
#act-cn-tabs {
display: flex;
flex-flow: row;
padding: 2rem 1rem 1rem 2rem;
}
#act-cn-tabs #content {
width: 77%;
}
}
#heads-up {
margin-left: auto;
margin-right: auto;
margin-top: 1rem;
}
@media (prefers-color-scheme: dark) {
html body {
background: #111;
color:white;
}
div.actix-showcase {
background-color: #333;
}
a.nav-link,li.active * {
color:cornflowerblue !important;
}
a:hover{
color: cornflowerblue;
opacity: 0.8;
}
pre,p code {
background: #222;
color:white;
}
a.navbar-brand img {
filter:invert(1) brightness(2)
}
li.active {
background: #222 !important;
}
/*
you can strip out these by just choosing another code theme that is dark mode compatible
*/
span.s {
color: green !important;
}
span.k,span.kd {
color:violet !important;
}
span.sd,span.cp {
color: #888 !important;
}
span.kt,span.mi {
color:red !important;
}
span.nf{
color:dodgerblue !important;
}
span.nc,span.nb {
color:gold !important;
}
.navbar-toggler-icon {
filter: invert(100%);
}
}

View File

@ -1,2 +0,0 @@
/*! normalize.css v4.2.0 | MIT License | github.com/necolas/normalize.css */html{font-family:sans-serif;line-height:1.15;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block}audio:not([controls]){display:none;height:0}progress{vertical-align:baseline}[hidden],template{display:none}a{background-color:transparent;-webkit-text-decoration-skip:objects}a:active,a:hover{outline-width:0}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:inherit}b,strong{font-weight:bolder}dfn{font-style:italic}h1{font-size:2em;margin:.67em 0}mark{background-color:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}svg:not(:root){overflow:hidden}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}figure{margin:1em 40px}hr{-webkit-box-sizing:content-box;box-sizing:content-box;height:0;overflow:visible}button,input,optgroup,select,textarea{font:inherit;margin:0}optgroup{font-weight:700}button,input{overflow:visible}button,select{text-transform:none}[type=reset],[type=submit],button,html [type=button]{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}legend{-webkit-box-sizing:border-box;box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}textarea{overflow:auto}[type=checkbox],[type=radio]{-webkit-box-sizing:border-box;box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-cancel-button,[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-input-placeholder{color:inherit;opacity:.54}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}html{-webkit-box-sizing:border-box;box-sizing:border-box}*,::after,::before{-webkit-box-sizing:inherit;box-sizing:inherit}@-ms-viewport{width:device-width}html{font-size:16px;-ms-overflow-style:scrollbar;-webkit-tap-highlight-color:transparent}body{font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;font-size:1rem;line-height:1.5;color:#373a3c;background-color:#fff}[tabindex="-1"]:focus{outline:0!important}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem}p{margin-top:0;margin-bottom:1rem}abbr[data-original-title],abbr[title]{cursor:help;border-bottom:1px dotted #818a91}address{margin-bottom:1rem;font-style:normal;line-height:inherit}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}a{color:#0275d8;text-decoration:none}a:focus,a:hover{color:#014c8c;text-decoration:underline}a:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}a:not([href]):not([tabindex]){color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus,a:not([href]):not([tabindex]):hover{color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus{outline:0}pre{margin-top:0;margin-bottom:1rem;overflow:auto}figure{margin:0 0 1rem}img{vertical-align:middle}[role=button]{cursor:pointer}[role=button],a,area,button,input,label,select,summary,textarea{-ms-touch-action:manipulation;touch-action:manipulation}table{border-collapse:collapse;background-color:transparent}caption{padding-top:.75rem;padding-bottom:.75rem;color:#818a91;text-align:left;caption-side:bottom}th{text-align:left}label{display:inline-block;margin-bottom:.5rem}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}button,input,select,textarea{line-height:inherit}input[type=checkbox]:disabled,input[type=radio]:disabled{cursor:not-allowed}input[type=date],input[type=time],input[type=datetime-local],input[type=month]{-webkit-appearance:listbox}textarea{resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:.5rem;font-size:1.5rem;line-height:inherit}input[type=search]{-webkit-appearance:none}output{display:inline-block}[hidden]{display:none!important}
/*# sourceMappingURL=bootstrap-reboot.min.css.map */

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,66 +0,0 @@
/* Error */ .chroma .err { }
/* LineTableTD */ .chroma .lntd { vertical-align: top; padding: 0; margin: 0; border: 0; }
/* LineTable */ .chroma .lntable { border-spacing: 0; padding: 0; margin: 0; border: 0; width: auto; overflow: auto; display: block; }
/* LineHighlight */ .chroma .hl { display: block; width: 100%;background-color: #ffffcc }
/* LineNumbersTable */ .chroma .lnt { margin-right: 0.4em; padding: 0 0.4em 0 0.4em; }
/* LineNumbers */ .chroma .ln { margin-right: 0.4em; padding: 0 0.4em 0 0.4em; }
/* Keyword */ .chroma .k { color: #006a70; font-weight: bold }
/* KeywordConstant */ .chroma .kc { color: #006a70; font-weight: bold }
/* KeywordDeclaration */ .chroma .kd { color: #006a70; font-weight: bold }
/* KeywordNamespace */ .chroma .kn { color: #006a70; font-weight: bold }
/* KeywordPseudo */ .chroma .kp { color: #006a70 }
/* KeywordReserved */ .chroma .kr { color: #006a70; font-weight: bold }
/* KeywordType */ .chroma .kt { color: #902000 }
/* NameAttribute */ .chroma .na { color: #197a7a }
/* NameBuiltin */ .chroma .nb { color: #006a70 }
/* NameClass */ .chroma .nc { color: #137ea5; font-weight: bold }
/* NameConstant */ .chroma .no { color: #60add5 }
/* NameDecorator */ .chroma .nd { color: #555555; font-weight: bold }
/* NameEntity */ .chroma .ni { color: #d55537; font-weight: bold }
/* NameException */ .chroma .ne { color: #006a70 }
/* NameFunction */ .chroma .nf { color: #06287e }
/* NameLabel */ .chroma .nl { color: #002070; font-weight: bold }
/* NameNamespace */ .chroma .nn { color: #137ea5; font-weight: bold }
/* NameTag */ .chroma .nt { color: #062873; font-weight: bold }
/* NameVariable */ .chroma .nv { color: #bb60d5 }
/* LiteralString */ .chroma .s { color: #197a7a }
/* LiteralStringAffix */ .chroma .sa { color: #197a7a }
/* LiteralStringBacktick */ .chroma .sb { color: #197a7a }
/* LiteralStringChar */ .chroma .sc { color: #197a7a }
/* LiteralStringDelimiter */ .chroma .dl { color: #197a7a }
/* LiteralStringDoc */ .chroma .sd { color: #197a7a; font-style: italic }
/* LiteralStringDouble */ .chroma .s2 { color: #197a7a }
/* LiteralStringEscape */ .chroma .se { color: #197a7a; font-weight: bold }
/* LiteralStringHeredoc */ .chroma .sh { color: #197a7a }
/* LiteralStringInterpol */ .chroma .si { color: #70a0d0; font-style: italic }
/* LiteralStringOther */ .chroma .sx { color: #c65d09 }
/* LiteralStringRegex */ .chroma .sr { color: #235388 }
/* LiteralStringSingle */ .chroma .s1 { color: #197a7a }
/* LiteralStringSymbol */ .chroma .ss { color: #517918 }
/* LiteralNumber */ .chroma .m { color: #40a070 }
/* LiteralNumberBin */ .chroma .mb { color: #40a070 }
/* LiteralNumberFloat */ .chroma .mf { color: #40a070 }
/* LiteralNumberHex */ .chroma .mh { color: #40a070 }
/* LiteralNumberInteger */ .chroma .mi { color: #40a070 }
/* LiteralNumberIntegerLong */ .chroma .il { color: #40a070 }
/* LiteralNumberOct */ .chroma .mo { color: #40a070 }
/* Operator */ .chroma .o { color: #666666 }
/* OperatorWord */ .chroma .ow { color: #006a70; font-weight: bold }
/* Comment */ .chroma .c { color: #60a0b0; font-style: italic }
/* CommentHashbang */ .chroma .ch { color: #60a0b0; font-style: italic }
/* CommentMultiline */ .chroma .cm { color: #60a0b0; font-style: italic }
/* CommentSingle */ .chroma .c1 { color: #60a0b0; font-style: italic }
/* CommentSpecial */ .chroma .cs { color: #60a0b0; background-color: #fff0f0 }
/* CommentPreproc */ .chroma .cp { color: #006a70 }
/* CommentPreprocFile */ .chroma .cpf { color: #006a70 }
/* GenericDeleted */ .chroma .gd { color: #a00000 }
/* GenericEmph */ .chroma .ge { font-style: italic }
/* GenericError */ .chroma .gr { color: #ff0000 }
/* GenericHeading */ .chroma .gh { color: #000080; font-weight: bold }
/* GenericInserted */ .chroma .gi { color: #00a000 }
/* GenericOutput */ .chroma .go { color: #888888 }
/* GenericPrompt */ .chroma .gp { color: #c65d09; font-weight: bold }
/* GenericStrong */ .chroma .gs { font-weight: bold }
/* GenericSubheading */ .chroma .gu { color: #800080; font-weight: bold }
/* GenericTraceback */ .chroma .gt { color: #0044dd }
/* TextWhitespace */ .chroma .w { color: #bbbbbb }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

Before

Width:  |  Height:  |  Size: 434 KiB

Binary file not shown.

View File

@ -1,39 +0,0 @@
(function() {
function activateFeature(sel) {
$('div.actix-feature').hide();
$(sel).show();
$('li.actix-feature-selector').removeClass('active');
$('li.actix-feature-selector > a').each(function() {
if (this.getAttribute('href') === sel) {
$(this).parent().addClass('active');
}
});
}
function initFeatureSelector() {
$('div.actix-feature').hide();
var active = $(window.location.hash);
if (active.is('div.actix-feature')) {
activateFeature(window.location.hash);
$('html, body').animate({
scrollTop: $('.actix-showcase').offset().top
}, 1000);
} else {
var firstFeature = $('div.actix-feature')[0];
if (firstFeature) {
activateFeature('#' + firstFeature.id);
}
}
$('ul li.actix-feature-selector a').on('click', function(evt) {
evt.preventDefault();
history.replaceState({}, '', evt.target.href);
activateFeature(this.getAttribute('href'));
});
}
$(function() {
initFeatureSelector();
});
})();

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

4
vars.js Normal file
View File

@ -0,0 +1,4 @@
module.exports = {
rustVersion: '1.56',
actixWebMajorVersion: '4',
};