mirror of
https://github.com/actix/actix-website
synced 2024-11-24 00:41:07 +01:00
migrate to docusaurus (v2) (#266)
Co-authored-by: ibraheemdev <ibrah1440@gmail.com>
This commit is contained in:
parent
a85b4ff5a3
commit
8393aea71a
24
.github/workflows/build-check.yml
vendored
Normal file
24
.github/workflows/build-check.yml
vendored
Normal 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
|
32
.github/workflows/deploy.yml
vendored
32
.github/workflows/deploy.yml
vendored
@ -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
11
.gitignore
vendored
@ -1,8 +1,5 @@
|
|||||||
public
|
node_modules/
|
||||||
tmp
|
|
||||||
_site
|
|
||||||
Cargo.lock
|
|
||||||
build/
|
build/
|
||||||
target/
|
.docusaurus
|
||||||
.DS_Store
|
.cache-loader
|
||||||
/.hugo_build.lock
|
Cargo.lock
|
29
README.md
29
README.md
@ -2,32 +2,25 @@
|
|||||||
|
|
||||||
## Getting Started
|
## 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:
|
Building the website depends on [Docusaurus], you must have `npm` or `yarn` installed. You can run the site locally with:
|
||||||
|
|
||||||
```sh
|
|
||||||
brew update
|
|
||||||
brew install hugo
|
|
||||||
```
|
|
||||||
|
|
||||||
Then, get the website running locally:
|
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
git clone https://github.com/actix/actix-website.git
|
git clone https://github.com/actix/actix-website.git
|
||||||
cd actix-website
|
cd actix-website
|
||||||
|
npm install # or yarn install
|
||||||
hugo server
|
npm start # or yarn start
|
||||||
```
|
```
|
||||||
|
|
||||||
Then visit http://localhost:1313.
|
Then visit http://localhost:3000.
|
||||||
|
|
||||||
## Updating diagrams
|
## 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:
|
For instance to edit `connection_overview` diagram:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
cd static/css/img/diagrams
|
cd static/img/diagrams
|
||||||
vi connection_overview.mmd
|
vi connection_overview.mmd
|
||||||
# Compile diagrams:
|
# Compile diagrams:
|
||||||
mmdc -i connection_overview.mmd -o connection_overview.svg
|
mmdc -i connection_overview.mmd -o connection_overview.svg
|
||||||
@ -35,10 +28,14 @@ mmdc -i connection_overview.mmd -o connection_overview.svg
|
|||||||
|
|
||||||
# License
|
# 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 -->
|
<!-- LINKS -->
|
||||||
|
|
||||||
[hugo]: https://gohugo.io
|
[Docusaurus]: https://docusaurus.io/
|
||||||
[homebrew]: https://brew.sh
|
|
||||||
[mermaid cli]: https://github.com/mermaidjs/mermaid.cli
|
[mermaid cli]: https://github.com/mermaidjs/mermaid.cli
|
||||||
|
Binary file not shown.
3
babel.config.js
Normal file
3
babel.config.js
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
module.exports = {
|
||||||
|
presets: [require.resolve('@docusaurus/core/lib/babel/preset')],
|
||||||
|
};
|
22
config.toml
22
config.toml
@ -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"
|
|
@ -1,3 +0,0 @@
|
|||||||
---
|
|
||||||
title: Actix Web | A powerful, pragmatic, and extremely fast web framework for Rust.
|
|
||||||
---
|
|
@ -1,9 +1,9 @@
|
|||||||
---
|
---
|
||||||
title: Application
|
title: Application
|
||||||
menu: docs_basics
|
|
||||||
weight: 140
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
import CodeBlock from "@site/src/components/code_block.js";
|
||||||
|
|
||||||
# Writing an Application
|
# 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.
|
`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.
|
> 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.
|
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:
|
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:
|
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.
|
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:
|
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`:
|
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:
|
Key takeaways:
|
||||||
- State initialized _inside_ the closure passed to `HttpServer::new` is local to the worker thread and may become de-synced if modified.
|
- 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:
|
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.
|
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.
|
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.
|
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:
|
The result of the above example would be:
|
||||||
|
|
@ -1,7 +1,5 @@
|
|||||||
---
|
---
|
||||||
title: Auto-Reloading
|
title: Auto-Reloading
|
||||||
menu: docs_patterns
|
|
||||||
weight: 1000
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# Auto-Reloading Development Server
|
# Auto-Reloading Development Server
|
@ -1,7 +1,5 @@
|
|||||||
---
|
---
|
||||||
title: Connection Lifecycle
|
title: Connection Lifecycle
|
||||||
menu: docs_architecture
|
|
||||||
weight: 1030
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# Architecture overview
|
# Architecture overview
|
@ -1,9 +1,9 @@
|
|||||||
---
|
---
|
||||||
title: Databases
|
title: Databases
|
||||||
menu: docs_patterns
|
|
||||||
weight: 1010
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
import CodeBlock from "@site/src/components/code_block.js";
|
||||||
|
|
||||||
# Async Options
|
# Async Options
|
||||||
|
|
||||||
We have several example projects showing use of async database adapters:
|
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.
|
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.)
|
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.
|
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].
|
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
|
That's it! See the full example here: https://github.com/actix/examples/tree/master/databases/diesel
|
||||||
|
|
@ -1,9 +1,9 @@
|
|||||||
---
|
---
|
||||||
title: Errors
|
title: Errors
|
||||||
menu: docs_advanced
|
|
||||||
weight: 180
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
import CodeBlock from "@site/src/components/code_block.js";
|
||||||
|
|
||||||
# Errors
|
# 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.
|
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.
|
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.
|
`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:
|
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`:
|
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.
|
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:
|
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).
|
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.
|
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:
|
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.
|
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:
|
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.
|
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`:
|
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"
|
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
|
[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
|
[errorhelpers]: https://docs.rs/actix-web/4/actix_web/trait.ResponseError.html
|
@ -1,50 +1,50 @@
|
|||||||
---
|
---
|
||||||
title: Extractors
|
title: Extractors
|
||||||
menu: docs_basics
|
|
||||||
weight: 170
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
import CodeBlock from "@site/src/components/code_block.js";
|
||||||
|
|
||||||
# Type-safe information extraction
|
# 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)).
|
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.
|
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.
|
[_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)>`).
|
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.
|
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.
|
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`.
|
[`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.
|
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.
|
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
|
# 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.
|
[_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:
|
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:
|
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].
|
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].
|
**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
|
[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
|
[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
|
[docsrs_match_info]: https://docs.rs/actix-web/latest/actix_web/struct.HttpRequest.html#method.match_info
|
||||||
[actix]: https://actix.github.io/actix/actix/
|
[actix]: https://actix.github.io/actix/actix/
|
||||||
[atomics]: https://doc.rust-lang.org/std/sync/atomic/
|
[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
|
[critical_section]: https://en.wikipedia.org/wiki/Critical_section
|
||||||
[tokio_std_mutex]: https://tokio.rs/tokio/tutorial/shared-state#on-using-stdsyncmutex
|
[tokio_std_mutex]: https://tokio.rs/tokio/tutorial/shared-state#on-using-stdsyncmutex
|
@ -1,14 +1,18 @@
|
|||||||
---
|
---
|
||||||
title: Getting Started
|
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
|
## 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.
|
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!
|
## 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.
|
Add `actix-web` as a dependency of your project by adding the following to your `Cargo.toml` file.
|
||||||
|
|
||||||
```toml
|
|
||||||
[dependencies]
|
<!-- DEPENDENCY -->
|
||||||
actix-web = "{{< actix-version "actix-web" >}}"
|
|
||||||
```
|
<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):
|
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).
|
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".
|
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.
|
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.
|
||||||
|
|
@ -1,9 +1,9 @@
|
|||||||
---
|
---
|
||||||
title: Handlers
|
title: Handlers
|
||||||
menu: docs_basics
|
|
||||||
weight: 160
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
import CodeBlock from "@site/src/components/code_block.js";
|
||||||
|
|
||||||
# Request Handlers
|
# 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]).
|
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:
|
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
|
## 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.:
|
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)
|
## 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.
|
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
|
[implfromrequest]: https://docs.rs/actix-web/4/actix_web/trait.FromRequest.html
|
||||||
[respondertrait]: https://docs.rs/actix-web/4/actix_web/trait.Responder.html
|
[respondertrait]: https://docs.rs/actix-web/4/actix_web/trait.Responder.html
|
@ -1,10 +1,12 @@
|
|||||||
---
|
---
|
||||||
title: HTTP/2
|
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
|
# 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.
|
When either of the `rustls` or `openssl` features are enabled, `HttpServer` provides the [bind_rustls][bindrustls] method and [bind_openssl][bindopenssl] methods, respectively.
|
||||||
|
|
||||||
```toml
|
<!-- DEPENDENCY -->
|
||||||
[dependencies]
|
|
||||||
actix-web = { version = "{{< actix-version "actix-web" >}}", features = ["openssl"] }
|
|
||||||
openssl = { version = "0.10", features = ["v110"] }
|
|
||||||
```
|
|
||||||
|
|
||||||
{{< 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).
|
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).
|
||||||
|
|
@ -1,10 +1,8 @@
|
|||||||
---
|
---
|
||||||
title: HTTP Server Initialization
|
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
|
Below is a diagram of HttpServer initialization, which happens on the following code
|
||||||
|
|
@ -1,9 +1,9 @@
|
|||||||
---
|
---
|
||||||
title: Middleware
|
title: Middleware
|
||||||
menu: docs_advanced
|
|
||||||
weight: 220
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
import CodeBlock from "@site/src/components/code_block.js";
|
||||||
|
|
||||||
# Middleware
|
# 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.
|
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:
|
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:
|
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.
|
> 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.**
|
**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.
|
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).
|
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:
|
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
|
%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:
|
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
|
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
|
- `%%` The percent sign
|
||||||
- `%a` Remote IP-address (IP-address of proxy if using reverse proxy)
|
- `%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}o` response.headers['FOO']
|
||||||
- `%{FOO}e` os.environ['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.
|
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
|
## 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.
|
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.
|
`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.
|
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
|
[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
|
[requestsession]: https://docs.rs/actix-session/0.3.0/actix_session/struct.Session.html
|
@ -1,7 +1,5 @@
|
|||||||
---
|
---
|
||||||
title: Requests
|
title: Requests
|
||||||
menu: docs_advanced
|
|
||||||
weight: 200
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# JSON Request
|
# 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).
|
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.
|
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.
|
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].
|
> 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:
|
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`.
|
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.
|
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].
|
Actix Web provides multipart stream support with an external crate, [`actix-multipart`][multipartcrate].
|
||||||
|
|
||||||
> A full example is available in the [examples directory][multipartexample].
|
> 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_.
|
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
|
- content-length is greater than 256k
|
||||||
- payload terminates with error.
|
- 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.
|
_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:
|
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
|
[examples]: https://github.com/actix/examples/tree/master/json/json
|
||||||
[multipartstruct]: https://docs.rs/actix-multipart/0.2/actix_multipart/struct.Multipart.html
|
[multipartstruct]: https://docs.rs/actix-multipart/0.2/actix_multipart/struct.Multipart.html
|
@ -1,9 +1,9 @@
|
|||||||
---
|
---
|
||||||
title: Responses
|
title: Responses
|
||||||
menu: docs_advanced
|
|
||||||
weight: 210
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
import CodeBlock from "@site/src/components/code_block.js";
|
||||||
|
|
||||||
# Response
|
# 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.
|
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.
|
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_.
|
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"] }
|
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.
|
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:
|
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
|
- Deflate
|
||||||
- Identity
|
- 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.
|
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`:
|
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:
|
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:
|
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:
|
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.
|
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
|
[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
|
[compressmidddleware]: https://docs.rs/actix-web/4/actix_web/middleware/struct.Compress.html
|
@ -1,9 +1,11 @@
|
|||||||
---
|
---
|
||||||
title: Server
|
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 HTTP Server
|
||||||
|
|
||||||
The [**HttpServer**][httpserverstruct] type is responsible for serving HTTP requests.
|
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)).
|
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
|
## 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.
|
`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.
|
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.
|
The `rustls` crate feature is for `rustls` integration and `openssl` is for `openssl` integration.
|
||||||
|
|
||||||
```toml
|
<!-- DEPENDENCY -->
|
||||||
[dependencies]
|
|
||||||
actix-web = { version = "{{< actix-version "actix-web" >}}", features = ["openssl"] }
|
|
||||||
openssl = { version = "0.10" }
|
|
||||||
```
|
|
||||||
|
|
||||||
{{< 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**
|
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.
|
- `KeepAlive::Os`: uses OS keep-alive.
|
||||||
- `None` or `KeepAlive::Disabled`: disables 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)
|
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.
|
> 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
|
## Graceful shutdown
|
||||||
|
|
@ -1,26 +1,26 @@
|
|||||||
---
|
---
|
||||||
title: Static Files
|
title: Static Files
|
||||||
menu: docs_advanced
|
|
||||||
weight: 230
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
import CodeBlock from "@site/src/components/code_block.js";
|
||||||
|
|
||||||
# Individual file
|
# 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.
|
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.
|
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.
|
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.
|
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:
|
`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.
|
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:
|
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
|
[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
|
[indexfile]: https://docs.rs/actix-files/0.2/actix_files/struct.Files.html#method.index_file
|
@ -1,20 +1,20 @@
|
|||||||
---
|
---
|
||||||
title: Testing
|
title: Testing
|
||||||
menu: docs_advanced
|
|
||||||
weight: 215
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
import CodeBlock from "@site/src/components/code_block.js";
|
||||||
|
|
||||||
# Testing
|
# Testing
|
||||||
|
|
||||||
Every application should be well tested. Actix Web provides tools to perform unit and integration tests.
|
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.
|
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.
|
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.
|
> 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.
|
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].
|
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
|
[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
|
[responsebody]: https://docs.rs/actix-web/4/actix_web/body/enum.ResponseBody.html
|
@ -1,30 +1,30 @@
|
|||||||
---
|
---
|
||||||
title: URL Dispatch
|
title: URL Dispatch
|
||||||
menu: docs_advanced
|
|
||||||
weight: 190
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
import CodeBlock from "@site/src/components/code_block.js";
|
||||||
|
|
||||||
# URL Dispatch
|
# 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.
|
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].
|
> 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_).
|
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.
|
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.
|
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.
|
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`.
|
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.
|
> 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`.
|
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::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::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.
|
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.
|
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.
|
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
|
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:
|
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}
|
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.
|
- `/abc/{foo}` will not match.
|
||||||
- _/{foo}/_ will 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.
|
> **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'}
|
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.
|
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
|
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.
|
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.
|
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].
|
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".
|
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.
|
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.
|
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.
|
[_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:
|
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.
|
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.
|
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:
|
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.
|
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/`.
|
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:
|
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.
|
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:
|
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.
|
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.
|
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_:
|
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.
|
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].
|
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":
|
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:
|
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"))
|
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.
|
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
|
[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
|
[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
|
[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
|
[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
|
[implfromrequest]: https://docs.rs/actix-web/4/actix_web/trait.FromRequest.html
|
||||||
[implresponder]: https://docs.rs/actix-web/4/actix_web/trait.Responder.html
|
[implresponder]: https://docs.rs/actix-web/4/actix_web/trait.Responder.html
|
||||||
[pathextractor]: ../extractors
|
[pathextractor]: /docs/extractors
|
@ -1,14 +1,17 @@
|
|||||||
---
|
---
|
||||||
title: Websockets
|
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.
|
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:
|
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].
|
> A simple websocket echo server example is available in the [examples directory][examples].
|
||||||
|
|
@ -1,10 +1,7 @@
|
|||||||
---
|
---
|
||||||
title: Documentation
|
title: Welcome
|
||||||
description: Guiding you through building web apps with Actix
|
description: Guiding you through building web apps with Actix
|
||||||
menu:
|
slug: /
|
||||||
docs_intro:
|
|
||||||
name: Welcome
|
|
||||||
weight: 10
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# Welcome to Actix
|
# Welcome to Actix
|
@ -1,8 +1,7 @@
|
|||||||
---
|
---
|
||||||
title: What is Actix Web
|
title: What is Actix Web
|
||||||
menu: docs_intro
|
|
||||||
weight: 100
|
|
||||||
---
|
---
|
||||||
|
import { rustVersion } from "@site/vars";
|
||||||
|
|
||||||
# Actix Web is part of an Ecosystem of Crates
|
# 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.
|
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 -->
|
<!-- TODO -->
|
||||||
<!-- which is built upon the fantastic [Tokio][tokio] asynchronous I/O system -->
|
<!-- which is built upon the fantastic [Tokio][tokio] asynchronous I/O system -->
|
67
docusaurus.config.js
Normal file
67
docusaurus.config.js
Normal 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'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
],
|
||||||
|
};
|
@ -1,6 +1,7 @@
|
|||||||
// <easy-form-handling>
|
|
||||||
use actix_web::{web, App, HttpResponse, HttpServer, Responder};
|
use actix_web::{web, App, HttpResponse, HttpServer, Responder};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
// <easy-form-handling>
|
||||||
|
use actix_web::web::{Either, Json, Form};
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
struct Register {
|
struct Register {
|
||||||
@ -8,25 +9,33 @@ struct Register {
|
|||||||
country: String,
|
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 {
|
async fn index() -> HttpResponse {
|
||||||
HttpResponse::Ok()
|
HttpResponse::Ok()
|
||||||
.content_type("text/html; charset=utf-8")
|
.content_type("text/html; charset=utf-8")
|
||||||
.body(include_str!("../static/form.html"))
|
.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]
|
#[actix_web::main]
|
||||||
async fn main() -> std::io::Result<()> {
|
async fn main() -> std::io::Result<()> {
|
||||||
HttpServer::new(|| {
|
HttpServer::new(|| {
|
||||||
App::new()
|
App::new()
|
||||||
.route("/", web::get().to(index))
|
.route("/", web::get().to(index))
|
||||||
.route("/register", web::post().to(register))
|
.route("/register", web::post().to(register))
|
||||||
|
.route("/json_register", web::post().to(json_register))
|
||||||
})
|
})
|
||||||
.bind(("127.0.0.1", 8080))?
|
.bind(("127.0.0.1", 8080))?
|
||||||
.run()
|
.run()
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
// </easy-form-handling>
|
|
||||||
|
@ -14,6 +14,7 @@ async fn hello_world() -> impl Responder {
|
|||||||
async fn current_temperature() -> impl Responder {
|
async fn current_temperature() -> impl Responder {
|
||||||
web::Json(Measurement { temperature: 42.3 })
|
web::Json(Measurement { temperature: 42.3 })
|
||||||
}
|
}
|
||||||
|
// </flexible-responders>
|
||||||
|
|
||||||
#[actix_web::main]
|
#[actix_web::main]
|
||||||
async fn main() -> std::io::Result<()> {
|
async fn main() -> std::io::Result<()> {
|
||||||
@ -26,4 +27,3 @@ async fn main() -> std::io::Result<()> {
|
|||||||
.run()
|
.run()
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
// </flexible-responders>
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
use actix_web::{web, App, HttpResponse, HttpServer, Responder};
|
use actix_web::{web, App, HttpResponse, HttpServer, Responder};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
// <powerful-extractors>
|
||||||
#[derive(Deserialize, Serialize)]
|
#[derive(Deserialize, Serialize)]
|
||||||
struct Event {
|
struct Event {
|
||||||
id: Option<i32>,
|
id: Option<i32>,
|
||||||
@ -9,6 +10,12 @@ struct Event {
|
|||||||
tags: Vec<String>,
|
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 {
|
fn store_in_db(timestamp: f64, kind: &str, tags: &[String]) -> Event {
|
||||||
// store item in db and get new_event
|
// store item in db and get new_event
|
||||||
// use id to lookup item
|
// 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 {
|
async fn index() -> HttpResponse {
|
||||||
HttpResponse::Ok()
|
HttpResponse::Ok()
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
// <request-routing>
|
// <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 {
|
async fn index(_req: HttpRequest) -> impl Responder {
|
||||||
"Hello from the index page."
|
"Hello from the index page."
|
||||||
}
|
}
|
||||||
@ -13,8 +14,8 @@ async fn hello(path: web::Path<String>) -> impl Responder {
|
|||||||
async fn main() -> std::io::Result<()> {
|
async fn main() -> std::io::Result<()> {
|
||||||
HttpServer::new(|| {
|
HttpServer::new(|| {
|
||||||
App::new()
|
App::new()
|
||||||
.service(web::resource("/").to(index))
|
.service(index)
|
||||||
.service(web::resource("/{name}").to(hello))
|
.route("/{name}", web::get().to(hello))
|
||||||
})
|
})
|
||||||
.bind(("127.0.0.1", 8080))?
|
.bind(("127.0.0.1", 8080))?
|
||||||
.run()
|
.run()
|
||||||
|
1
examples/server/rustfmt.toml
Normal file
1
examples/server/rustfmt.toml
Normal file
@ -0,0 +1 @@
|
|||||||
|
max_width = 80
|
14
examples/server/src/keep_alive_tp.rs
Normal file
14
examples/server/src/keep_alive_tp.rs
Normal 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
|
18
examples/url-dispatch/src/pbuf.rs
Normal file
18
examples/url-dispatch/src/pbuf.rs
Normal 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>
|
11
i18n/en.toml
11
i18n/en.toml
@ -1,11 +0,0 @@
|
|||||||
[home]
|
|
||||||
other = "Home"
|
|
||||||
|
|
||||||
[docs]
|
|
||||||
other = "Documentation"
|
|
||||||
|
|
||||||
[community]
|
|
||||||
other = "Community"
|
|
||||||
|
|
||||||
[code]
|
|
||||||
other = "Code"
|
|
@ -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" . }}
|
|
@ -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" . }}
|
|
@ -1,3 +0,0 @@
|
|||||||
{{ define "main" }}
|
|
||||||
{{ .Content }}
|
|
||||||
{{ end }}
|
|
@ -1,4 +0,0 @@
|
|||||||
{{ define "main" }}
|
|
||||||
<p class="uplink"><a href="/code/">↑ Back to code</a>
|
|
||||||
{{ .Content }}
|
|
||||||
{{ end }}
|
|
@ -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" . }}
|
|
@ -1,3 +0,0 @@
|
|||||||
{{ define "main" }}
|
|
||||||
{{ .Content }}
|
|
||||||
{{ end }}
|
|
@ -1,4 +0,0 @@
|
|||||||
{{ define "main" }}
|
|
||||||
<p class="uplink"><a href="/community/">↑ Back to community</a>
|
|
||||||
{{ .Content }}
|
|
||||||
{{ end }}
|
|
@ -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" . }}
|
|
@ -1,3 +0,0 @@
|
|||||||
{{ define "main" }}
|
|
||||||
{{ .Content }}
|
|
||||||
{{ end }}
|
|
@ -1,3 +0,0 @@
|
|||||||
{{ define "main" }}
|
|
||||||
{{ .Content }}
|
|
||||||
{{ end }}
|
|
@ -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" . }}
|
|
@ -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>
|
|
@ -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>
|
|
@ -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 -}}
|
|
@ -1,3 +0,0 @@
|
|||||||
<div class="alert alert-info" role="alert">
|
|
||||||
{{ .Inner | markdownify }}
|
|
||||||
</div>
|
|
@ -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" "" -}}
|
|
@ -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
22059
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
44
package.json
Normal file
44
package.json
Normal 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
48
sidebars.js
Normal 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/'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
27
src/components/code_block.js
Normal file
27
src/components/code_block.js
Normal 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
53
src/css/custom.css
Normal 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);
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
title: Contributor Covenant Code of Conduct
|
title: Contributor Covenant Code of Conduct
|
||||||
description:
|
description: The code of conduct
|
||||||
---
|
---
|
||||||
|
|
||||||
# Our Pledge
|
# Our Pledge
|
@ -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
|
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
210
src/pages/index.js
Normal 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;
|
183
src/pages/styles.module.scss
Normal file
183
src/pages/styles.module.scss
Normal 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
13
src/plugin.js
Normal 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
0
static/.nojekyll
Normal 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%);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
2
static/css/bootstrap-reboot.css
vendored
2
static/css/bootstrap-reboot.css
vendored
@ -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 */
|
|
7
static/css/bootstrap.css
vendored
7
static/css/bootstrap.css
vendored
File diff suppressed because one or more lines are too long
4
static/css/font-awesome.min.css
vendored
4
static/css/font-awesome.min.css
vendored
File diff suppressed because one or more lines are too long
@ -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.
Binary file not shown.
Binary file not shown.
@ -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();
|
|
||||||
});
|
|
||||||
})();
|
|
||||||
|
|
7
static/js/bootstrap.js
vendored
7
static/js/bootstrap.js
vendored
File diff suppressed because one or more lines are too long
7
static/js/bootstrap.min.js
vendored
7
static/js/bootstrap.min.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue
Block a user