# Python : Web : Starlette : Overview
\[ [PyPi](https://pypi.org/project/starlette/) | [Source](https://github.com/encode/starlette/) | [Docs](https://www.starlette.io/applications/) ]
## Overview
Starlette is an async, lightweight, modular ASGI framework that can be treated like a toolkit by using individual pieces.
- WebSockets
- in-process background tasks
- startup and shutdown events
- test client built on httpx
- CORS, GZip, Static Files, Streaming responses
- sessions and cookies
- no hard dependencies
```bash
$ pip install starlette, uvicorn
$ uvicorn [--reload] example:app
```
#### Optional Dependencies
- httpx -> Required if you want to use the TestClient.
- itsdangerous -> Required for SessionMiddleware support.
- jinja2 -> Required if you want to use Jinja2Templates.
- python-multipart -> Required if you want to support form parsing, with request.form().
- pyyaml -> Required for SchemaGenerator support.
#### Example
```python
[async] def homepage( request ):
return PlainTextResponse( "Hello, world!" )
[async] def profile( request ):
username = request.path_params[ "username" ]
return JSONResponse({ "username": username })
async def favicon( request ):
return FileResponse( "favicon.ico" ) # or Path instance
async def websocket_endpoint( websocket ):
await websocket.accept()
await websocket.send_text( "Hello, websocket!" )
await websocket.close()
routes = [
Route( "/", homepage ),
Route( "/user/{ username }", profile ),
WebSocketRoute( "/ws", websocket_endpoint ),
Mount( "/static", StaticFiles( directory="static" ) ),
]
app = Starlette( debug=True, routes=routes )
```
**Submounting**
```python
routes = [
Route( "/", homepage ),
Mount( "/users", routes=[
Route( "/", users, methods=[ "GET", "POST" ] ),
Route( "/{ username }", user ),
] )
]
```
## Reference
#### Imports
```python
from starlette.applications import Starlette
from starlette.responses import FileResponse, HTMLResponse, JSONResponse, PlainTextResponse
from starlette.requests import Request
from starlette.routing import Mount, Route, WebSocketRoute
from starlette.staticfiles import StaticFiles
from starlette.testclient import TestClient
from starlette.websockets import WebSocket, WebSocketDisconnect
from starlette.middleware import Middleware
from starlette.middleware.cors import CORSMiddleware
from starlette.middleware.gzip import GZipMiddleware
from starlette.middleware.httpsredirect import HTTPSRedirectMiddleware
from starlette.middleware.sessions import SessionMiddleware
from starlette.middleware.trustedhost import TrustedHostMiddleware
```
#### Applications
```python
app = Starlette(
debug = False # whether debug tracebacks should be returned on errors
routes = None # list of Route and WebsocketRoute instances
middleware = None # list
exception_handlers = None # { [ integer status code | exception class type ]: callable }
# (sync or async) callable( request, exc ) -> response
# lifespan is meant to replace on_startup + on_shutdown handlers
on_startup = None # list of callables (no arguments) (sync or async)
on_shutdown = None # list of callables (no arguments) (sync or async)
lifespan = None # lifespan context function - can be used to perform startup and shutdown tasks
)
# You can store arbitrary extra state on the application instance, using the generic app.state attribute.
app.state.ADMIN_EMAIL = '
[email protected]'
```
#### Routing
**Route Endpoints**
- A regular function or async function, which accepts a single request argument and which should return a response.
- A class that implements the ASGI interface, such as Starlette's `HTTPEndpoint`.
**WebSocketRoute Endpoints**
- An async function, which accepts a single websocket argument.
- A class that implements the ASGI interface, such as Starlette's `WebSocketEndpoint`.
```python
Route(
path
endpoint = func_or_class # reminder: expects a class, not an instance
methods = [ "GET", "POST" ] # defaults to "GET" only
name = "..." # optional
)
WebSocketRoute # same signature as Route, but endpoint must be async and receives "websocket" instead of "request"
Mount(
path
app = asgi_app # ex: Mount( "/static", app=StaticFiles( directory="static" ) )
routes = [ ... ] # optional
name = "..." # optional
)
# add mounts after creating app
app.mount( path, subapp, name=None )
```
**Paths**
```
"/foo/{ bar }" # bar will capture to end of path or next "/"; accessible via request.path_params[ "bar" ]
"/{ foo_id:int }" # using int "converter"
# Convertors: str (default), int, float, uuid, path (rest of path including additional "/" chars)
# You can also register custom convertors. (see docs)
```
**Host-Based Routing**
See [docs](https://www.starlette.io/routing/#host-based-routing).
#### Request
```python
request.app
request.client # None | namedtuple( host, port )
request.cookies # dict
request.headers # immutable case-insensitive multi-dict
request.method
request.path_params # dict
request.query_params # immnutable multi-dict
request.state # to attach state to request (for convenience)
request.url[.path|port|scheme...]
await request.body() # bytes
await request.json()
async with request.form() as form: # parsed as form data or multipart
...
async for chunk in request.stream(): # read body as stream
...
await request.is_disconnected()
```
#### Websocket
TODO
#### Response
```python
Response( content, status_code=200, headers=None, media_type=None ) # content -> str | bytes, media_type ex: "text/html"
response.set_cookie( ... )
response.delete_cookie( ... )
PlainTextResponse( ... ) # sets media_type = "text/plain"
HTMLResponse( ... ) # sets media_type = "text/html"
JSONResponse( ... ) # sets media_type = "application/json"; content -> Any
FileResponse( path, ... ) # will guess media type
RedirectResponse( url, status_code=307, headers=None )
StreamingResponse( ...TODO... )
```
#### Reverse URL Lookups
```python
request.url_for( name, **path_params ) -> URL # Route( ..., name="foo" ) + url_for( "foo" )
# If a Mount includes a name, then submounts should use a { prefix }:{ name } style.
request.url_for( "users:user_list" )
# Appending a subpath to a named route.
Mount( "/static", app=StaticFiles( directory="static" ), name="static" )
url = request.url_for( "static", path="/css/base.css" )
# Supplying params for path.
app.url_path_for( "user_detail", username="foo" ) -> "/user/foo"
```
## Middleware
A starlette application will always automatically include two middleware classes.
- outermost: `ServerErrorMiddleware` -> Ensures that application exceptions may return a custom 500 page, or display an application traceback in DEBUG mode.
- innermost: `ExceptionMiddleware` -> Adds exception handlers, so that particular types of expected exception cases can be associated with handler functions.
**Example**
```python
# Ensure that all requests include an 'example.com' or '*.example.com' host header, and strictly enforce https-only access.
middleware = [
Middleware(
TrustedHostMiddleware,
allowed_hosts=[ "example.com", "*.example.com" ],
),
Middleware( HTTPSRedirectMiddleware )
]
app = Starlette( routes=routes, middleware=middleware )
```
**Applying to a Subset of Routes**
Note that middleware used in this way is not wrapped in exception handling middleware like the middleware applied to the Starlette application is.
```python
Route( "/foo", endpoint=..., middleware=[ Middleware( GZipMiddleware ) ] )
Mount( "/foo", routes=[ ... ], middleware=[ Middleware( GZipMiddleware ) ] )
```
#### CORSMiddleware
```python
Middleware( CORSMiddleware, allow_origins=[ "*" ], allow_methods=[ "GET", "POST", "OPTIONS" ], allow_headers=[ "Authorization" ] )
allow_origins # A list of origins that should be permitted to make cross-origin requests.
# eg. [ 'https://example.org', 'https://www.example.org' ].
# You can use [ '*' ] to allow any origin.
allow_origin_regex # A regex string to match against origins that should be permitted to make cross-origin requests.
# eg. 'https://.*\.example\.org'.
allow_methods # A list of HTTP methods that should be allowed for cross-origin requests.
# Defaults to [ 'GET' ]. You can use [ '*' ] to allow all standard methods.
allow_headers # A list of HTTP request headers that should be supported for cross-origin requests.
# Defaults to []. You can use [ '*' ] to allow all headers.
# The Accept, Accept-Language, Content-Language and Content-Type headers are always allowed for CORS requests.
allow_credentials # Indicate that cookies should be supported for cross-origin requests.
# Defaults to False.
# Also, allow_origins, allow_methods and allow_headers cannot be set to [ '*' ] for credentials to be allowed, all of them must be explicitly specified.
expose_headers # Indicate any response headers that should be made accessible to the browser.
# Defaults to [].
max_age # Sets a maximum time in seconds for browsers to cache CORS responses.
# Defaults to 600.
```
#### HTTPSRedirectMiddleware
```python
Middleware( HTTPSRedirectMiddleware ) # no options
```
#### Third-Party
- [asgi-csrf](https://pypi.org/project/asgi-csrf/) -> Implements the Double Submit Cookie pattern, where a cookie is set, then it is compared to a csrftoken hidden form field or an x-csrftoken HTTP header.
- [starlette-authlib](https://pypi.org/project/starlette-authlib/) -> A drop-in replacement for Starlette session middleware, using authlib's [jwt](https://docs.authlib.org/en/latest/jose/jwt.html) module.
- [starlette-bugsnag](https://pypi.org/project/starlette-bugsnag/) → For logging exceptions to Bugsnag.
- [starlette-csrf](https://pypi.org/project/starlette-csrf/) -> Implements same pattern as asgi-csrf (Double Submit Cookie).
- [timing-asgi](https://pypi.org/project/timing-asgi/) -> Emit timing information (cpu and wall time) for each request.
- [rollbar](https://pypi.org/project/rollbar/) -> For logging exceptions, errors, and log messages to Rollbar.
---
TODO: How to write your own middleware.