Skip to content

Commit

Permalink
Improve the documentation for v1
Browse files Browse the repository at this point in the history
  • Loading branch information
RobertoPrevato committed Jul 16, 2023
1 parent a1ba10b commit 0443f98
Show file tree
Hide file tree
Showing 10 changed files with 150 additions and 89 deletions.
22 changes: 22 additions & 0 deletions docs/application.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,10 @@ Exceptions inheriting from `HTTPException` can be mapped to handlers by their ty
their status code, using `int` keys; while user defined exceptions are mapped to handlers
by their type.

When an exception handler is registered for a type of exception, all subclasses
are also handled by that handler. It is however possible to define a more
specific handler for one of the descendant classes.

### Configuring exception handlers using decorators

It is also possible to register exception handlers using decorators, instead
Expand All @@ -125,6 +129,24 @@ async def handler_example(self, request, exc: CustomException):

```

### Overriding the default exception handler for unhandled exceptions

To override how unhandled exceptions are handled, define a custom `Application`
class overriding its `handle_internal_server_error` method, like in the
following example:

```python

from blacksheep import Application, json
from blacksheep.messages import Request


class MyApp(Application):
async def handle_internal_server_error(self, request: Request, exc: Exception):
# TODO: handle this like you wish!
return json({"message": "Oh, no!"}, 500)
```

---

## Application events
Expand Down
10 changes: 10 additions & 0 deletions docs/authorization.md
Original file line number Diff line number Diff line change
Expand Up @@ -263,3 +263,13 @@ async def only_for_user_authenticated_with_github():
# authentication scheme, defined overriding the scheme @property
return ok("example")
```

## Failure response codes

When a request fails because of authorization reasons, the web framework
returns:

- status [`401 Unauthorized`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/401) if authentication failed, and no valid credentials were provided
- status [`403 Forbidden`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/403) if
authentication succeeded as valid credentials were provided, but the user is
not authorized to perform an action
5 changes: 5 additions & 0 deletions docs/controllers.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ This page describes:
It is recommended to follow the [MVC tutorial](../mvc-project-template/) before
reading this page.

!!! tip "For Flask users..."
If you come from Flask, controllers in BlackSheep would be the equivalent of
Flask's Blueprints, as they allow to group request handlers in dedicated
modules and classes.

## The Controller class
Controllers implement several methods to simplify returning responses. These
are the same described at [Responses](../responses/), but they can be overridden
Expand Down
98 changes: 13 additions & 85 deletions docs/develop-with-https.md
Original file line number Diff line number Diff line change
@@ -1,95 +1,23 @@
# Develop using HTTPS

To develop locally with HTTPS using a trusted certificate, a possible solution
is to generate a root CA, and a certificate for `localhost` using
[https://github.com/jsha/minica](minica), as recommended by [Let's
Encrypt](https://letsencrypt.org/docs/certificates-for-localhost/).
To develop locally with HTTPS using a trusted certificate, it is recommended to
use [`mkcert`](https://github.com/FiloSottile/mkcert), which _is a simple tool
for making locally-trusted development certificates_.

> If you want a little more realism in your development certificates, you can
> use minica to generate your own local root certificate, and issue end-entity
> (aka leaf) certificates signed by it. You would then import the root
> certificate rather than a self-signed end-entity certificate.
- Install [`mkcert`](https://github.com/FiloSottile/mkcert)
- Install the root certificates authority (CA) using the `mkcert --install` command
- Generate a certificate for local development using the command: `mkcert localhost 127.0.0.1 ::1`
- Use the feature of the `ASGI` server you select, to run with HTTPS.
For `uvicorn`, refer to this documentation: [https://www.uvicorn.org/deployment/#running-with-https](https://www.uvicorn.org/deployment/#running-with-https).

Summary:

1. install [Go](https://golang.org/doc/install)
2. clone the GitHub repository of [minica](https://github.com/jsha/minica)
3. `cd` into the repository's folder and build minica using `go build` as
described in [in `minica`
README](https://github.com/jsha/minica#installation)
4. create certificates for `localhost` using the command below
Example:

```bash
./minica --domains localhost
```

The output from the `minica` repository look like this (under the folder
`localhost`):

```
.
├── go.mod
├── LICENSE.txt
├── localhost
│   ├── cert.pem
│   └── key.pem
├── main.go
├── minica
├── minica-key.pem
├── minica.pem
└── README.md
```

Then:
mkcert --install

5. Configure `minica.pem` root certificate as trusted certificate in the system
(see instructions below for Linux and Windows)
6. Run your server using `key.pem` and `cert.pem` generated for localhost

---

### How to configure minica trusted CA

#### Under Linux

Configure the given `minica.pem` as trusted CA Authority for your PC. To do so,
install for example `certutil` package, and then use:

```bash
certutil -d sql:$HOME/.pki/nssdb -A -t "P,," -n "minica root" -i minica.pem
```
mkcert localhost 127.0.0.1 ::1

To list existing certificates with `certutil`:
```bash
# list certificates
certutil -L -d sql:${HOME}/.pki/nssdb
```

---

#### Under Windows

Use `openssl` to generate a PFX file, from the files generated by `minica`,
using the command below:

```bash
# Note: this command prompts for a password
openssl pkcs12 -inkey minica-key.pem -in minica.pem -export -out minica.pfx
```

Configure the generated PFX as trusted CA Authority for your PC. To do so,
click on the `.pfx` file, and follow the wizard to import the certificate as
Trusted Root Certificate for your machine.

---

Finally, to run using an SSL certificate trusted in the system, for example
with `uvicorn`:

```bash
uvicorn server:app --reload --ssl-keyfile ./key.pem --ssl-certfile ./cert.pem
uvicorn server:app --port 44555 --ssl-keyfile=./localhost-key.pem --ssl-certfile=./localhost.pem
```

Where `key.pem` and `cert.pem` are the files generated for `localhost`. The
development server can now be used at `https://localhost`. Note:
`https://127.0.0.1` won't work in this case.
![Local HTTPS](./img/mkcert-local-https.png)
Binary file added docs/img/mkcert-local-https.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
39 changes: 38 additions & 1 deletion docs/openapi.md
Original file line number Diff line number Diff line change
Expand Up @@ -602,7 +602,9 @@ components:
`FooOfTAndUAndX`.

### Describing parameters
It is possible to describe parameters explicitly, or using docstrings.

It is possible to describe parameters explicitly, using docstrings, or
leveraging `Pydantic`.

#### Documenting parameters explicitly

Expand Down Expand Up @@ -754,6 +756,41 @@ docs.ui_providers.append(ReDocUIProvider())
docs.include = lambda path, _: path.startswith("/api/")
```

### Changing operations ids
When OpenAPI Documentation is generated, operation ids are obtained from the
name of the Python function definitions.

For example, having a `get_foo` request handler, generates an object having
`operationId` equal to "get_foo":

```python
@app.router.get("/foo")
async def get_foo() -> Foo:
return Foo("Hello!")
```

```json
"paths": {
"/foo": {
"get": {
…,
"operationId": "get_foo"
}
}
},
```

To change how `operationId` is generated for endpoints, define a custom type
of `OpenAPIHandler` that overrides the `get_operation_id` method, to produce
the desired result:

```python
class CustomOpenAPIHandler(OpenAPIHandler):
def get_operation_id(self, docs: Optional[EndpointDocs], handler) -> str:
return handler.__name__.capitalize().replace("_", " ")
```


### For more details
For more details on the OpenAPI specification and understand some details such
as security settings, refer to the official [swagger.io web
Expand Down
6 changes: 6 additions & 0 deletions docs/remotes.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ load balancers, using provided classes to handle:
- [X] How to read information about the original clients in web requests

## Handling X-Forwarded headers

`X-Forwarded` headers are the _de-facto_ standard headers to propagate
information about original web requests to web applications.

Expand Down Expand Up @@ -173,3 +174,8 @@ absolute_url_to_path = get_absolute_url_to_path(request, "/example")
When configuring [OpenID Connect](../authentication/#oidc) authentication,
it can be necessary to handle forward headers, so that the application can
generate correct `redirect_uri` for authorization servers.

## ASGI root_path

When the `ASGI` scope includes the `root_path` information, it is automatically
used for the request `base_path` property.
2 changes: 1 addition & 1 deletion docs/request-handlers.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ This page describes `request handlers` in detail, presenting the following:
- [X] Using asynchronous and synchronous code.

## Request handler normalization.
A normal request handler in BlachSheep is defined as an asynchronous function
A normal request handler in BlackSheep is defined as an asynchronous function
having the following signature:

```python
Expand Down
50 changes: 48 additions & 2 deletions docs/templating.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ wondeful [`Jinja2` library](https://palletsprojects.com/p/jinja/) by the
This page describes:

- [X] How to configure server side templating.
- [X] Returning views using [response functions]().
- [X] Returning views using the [MVC features]().
- [X] Returning views using response functions.
- [X] Returning views using the MVC features.

!!! info
The [BlackSheep MVC project
Expand Down Expand Up @@ -71,6 +71,7 @@ is recommended to keep this setup in a single file, and import the `view`
function in files that define routes for the application.

## Async mode

It is possible to enable Jinja2 [async
mode](http://jinja.pocoo.org/docs/2.10/api/#async-support), using the parameter
`enable_async`. When `enable_async` is true, the function returned by
Expand All @@ -97,6 +98,7 @@ async def home():
```

## Loading templates

It is possible to load templates by name including '.html', or without file
extension; '.html' extension is added automatically. Extension must be lower
case.
Expand All @@ -114,3 +116,47 @@ async def home(request):
async def home(request):
return view("home", {"example": "Hello", "foo": "World"})
```

## Helpers and filters

To configure custom helpers and filters for Jinja, it is possible to access
its `Environment` using the `templates_environment` property of the application,
once server side templating is configured.

```
.
├── app
│   ├── __init__.py
│   └── views
│   └── index.html
└── server.py
```

```python
# server.py
from blacksheep import Application
from blacksheep.server.templating import use_templates
from jinja2 import PackageLoader, Environment

app = Application(show_error_details=True)

view = use_templates(app, PackageLoader("app", "views"))


def example():
return "This is an example"


app.templates_environment.globals.update({"my_function": example}) # <<<


@app.route("/")
async def home():
return view("index.html", {})
```

```html
<!-- index.html -->
<p>Hello, World!</p>
{{ my_function() }}
```
7 changes: 7 additions & 0 deletions overrides/main.html
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,13 @@
<meta name="twitter:image" content="{{ image }}">
{% endblock %}
{% block content %}
{% if not config.extra.is_current_version %}
<div class="admonition warning version-warning">
<p class="admonition-title">Version {{config.extra.version}}</p>
<p>This documentation refers to the version {{config.extra.version}}.x of the web framework.
The current version of the <code>main</code> branch is <a href="/blacksheep/">documented here</a>.</p>
</div>
{% endif %}
{{ super() }}
{% endblock %}
{% block analytics %}
Expand Down

0 comments on commit 0443f98

Please sign in to comment.