CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-blacksheep

Fast web framework for Python asyncio

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

request-response.mddocs/

Request/Response Handling

This document covers BlackSheep's HTTP message handling system, including Request and Response classes, content types, headers, cookies, URL parsing, and data binding.

Request Class

The Request class represents incoming HTTP requests with methods for accessing headers, body content, and parsed data.

Request Properties

from blacksheep import Request, URL
from typing import Optional, Dict, List

# Request creation and properties
request = Request("GET", b"https://example.com/users/123?q=search", headers=[
    (b"Content-Type", b"application/json"),
    (b"Authorization", b"Bearer token123")
])

# Basic properties
method: str = request.method                    # "GET", "POST", etc.
url: URL = request.url                         # Parsed URL object
query: Dict[str, List[str]] = request.query    # Query parameters
route_values: Optional[Dict[str, str]] = request.route_values  # Route params
cookies: Dict[str, str] = request.cookies      # Request cookies
content: Optional[Content] = request.content   # Request body content

# URL components
scheme: str = request.scheme                   # "http", "https"
host: str = request.host                       # "example.com"
path: str = request.path                       # "/users/123"
base_path: str = request.base_path             # Application base path

# Client information
client_ip: str = request.client_ip             # Client IP address
original_client_ip: str = request.original_client_ip  # With setter

Request Headers

from blacksheep.headers import Headers

# Header access methods
content_type: bytes = request.content_type()
etag: Optional[bytes] = request.etag
if_none_match: Optional[bytes] = request.if_none_match

# Generic header methods
first_header = request.get_first_header(b"Authorization")
all_auth_headers = request.get_headers(b"Authorization") 
single_header = request.get_single_header(b"Content-Type")
has_auth = request.has_header(b"Authorization")

# Header manipulation
request.add_header(b"X-Custom", b"value")
request.set_header(b"Content-Type", b"application/json")
request.remove_header(b"X-Remove-Me")

# Content type checking
is_json = request.declares_json()
is_xml = request.declares_xml()
has_content_type = request.declares_content_type(b"application/json")
has_body = request.has_body()

Request Body Parsing

import json
from blacksheep.contents import FormPart
from typing import Any, Union

# Raw body access
body_bytes: Optional[bytes] = await request.read()
body_text: str = await request.text()

# Streaming body
async for chunk in request.stream():
    process_chunk(chunk)

# Structured data parsing
json_data: Any = await request.json()
json_with_custom = await request.json(loads=custom_json_loads)

# Form data parsing  
form_data: Union[Dict, None] = await request.form()
# Returns: {"field1": "value1", "field2": ["value2", "value3"]}

# Multipart form data
multipart_parts: List[FormPart] = await request.multipart()
for part in multipart_parts:
    name = part.name.decode()
    data = part.data
    filename = part.file_name.decode() if part.file_name else None
    content_type = part.content_type

# File uploads
uploaded_files: List[FormPart] = await request.files()
specific_files = await request.files("avatar")  # Files with name "avatar"

for file_part in uploaded_files:
    filename = file_part.file_name.decode()
    content_type = file_part.content_type.decode()
    file_data = file_part.data

Request Cookies and Session

# Cookie access
cookie_value = request.get_cookie("session_id")
all_cookies = request.cookies  # Dict[str, str]

# Session access (when sessions are configured)
from blacksheep.sessions import Session

session: Session = request.session
session["user_id"] = 123
user_id = session.get("user_id")

# Set cookies on request (for processing)
request.set_cookie("temp_cookie", "temp_value")

Request Context

# Authentication context
from guardpost import Identity

identity: Optional[Identity] = request.identity
if identity:
    user_id = identity.id
    claims = identity.claims
    is_authenticated = identity.is_authenticated()

# ASGI scope access
scope = request.scope
asgi_version = scope.get("asgi", {}).get("version")

Request Utility Functions

BlackSheep provides utility functions for common request processing tasks.

from blacksheep.messages import (
    is_cors_request, is_cors_preflight_request,
    get_request_absolute_url, get_absolute_url_to_path
)

# CORS request detection
def is_cors_request(request: Request) -> bool:
    """Check if request is a CORS request"""
    pass

def is_cors_preflight_request(request: Request) -> bool:
    """Check if request is a CORS preflight request"""
    pass

# URL utilities
def get_request_absolute_url(request: Request) -> URL:
    """Get the absolute URL of the request"""
    pass

def get_absolute_url_to_path(request: Request, path: str) -> URL:
    """Convert a path to absolute URL using request context"""
    pass

Usage Examples

@app.middleware
async def cors_middleware(request: Request, handler):
    if is_cors_preflight_request(request):
        return Response(200, headers=[
            (b"Access-Control-Allow-Origin", b"*"),
            (b"Access-Control-Allow-Methods", b"GET, POST, PUT, DELETE"),
            (b"Access-Control-Allow-Headers", b"Content-Type, Authorization")
        ])
    
    if is_cors_request(request):
        response = await handler(request)
        response.headers.add(Header(b"Access-Control-Allow-Origin", b"*"))
        return response
    
    return await handler(request)

@app.get("/api/resource")
async def get_resource(request: Request):
    # Get absolute URL for current request
    current_url = get_request_absolute_url(request)
    
    # Generate absolute URLs for related resources
    related_url = get_absolute_url_to_path(request, "/api/related")
    
    return json({
        "current_url": current_url.value.decode(),
        "related_url": related_url.value.decode()
    })

Response Class

The Response class represents HTTP responses with status codes, headers, cookies, and content.

Response Creation

from blacksheep import Response, Content, TextContent, JSONContent
from blacksheep.headers import Headers

# Basic response creation
response = Response(200)  # Status only
response = Response(200, content=TextContent("Hello World"))

# With headers
response = Response(
    status=200,
    headers=[(b"Content-Type", b"application/json")],
    content=JSONContent({"message": "success"})
)

# Response properties
status: int = response.status                    # HTTP status code
reason: str = response.reason                    # Status reason phrase
content: Optional[Content] = response.content    # Response body
cookies: Cookies = response.cookies              # Response cookies

Response Headers

# Header manipulation (inherits from Message)
response.add_header(b"X-API-Version", b"1.0")
response.set_header(b"Cache-Control", b"no-cache")
response.remove_header(b"X-Powered-By")

# Content type setting
response.set_header(b"Content-Type", b"application/json")

# Custom headers
response.headers.add(b"X-Response-Time", b"0.123")
response.headers.set(b"X-Request-ID", request_id.encode())

Response Cookies

from blacksheep.cookies import Cookie, CookieSameSiteMode
from datetime import datetime, timedelta

# Cookie creation
cookie = Cookie(
    name="session_id",
    value="abc123",
    expires=datetime.utcnow() + timedelta(days=7),
    domain="example.com",
    path="/",
    http_only=True,
    secure=True,
    max_age=604800,  # 7 days in seconds
    same_site=CookieSameSiteMode.LAX
)

# Set cookies on response
response.set_cookie(cookie)
response.set_cookies([cookie1, cookie2, cookie3])

# Remove cookies
response.unset_cookie("old_session")
response.remove_cookie("temp_cookie")  # Alias for unset_cookie

# Get response cookies
all_cookies = response.get_cookies()
specific_cookie = response.get_cookie("session_id")

Response Utilities

# Response type checking
is_redirect: bool = response.is_redirect()  # 3xx status codes

# Response modification
new_response = response.with_content(new_content)

Response Helper Functions

BlackSheep provides convenient helper functions for creating common HTTP responses.

from blacksheep.server.responses import (
    ok, created, accepted, no_content,
    bad_request, unauthorized, forbidden, not_found,
    file, redirect, json, text, html,
    ContentDispositionType
)

# Success responses
def ok(message: Any = None) -> Response:
    """Returns HTTP 200 OK response"""
    pass

def created(message: Any = None, location: AnyStr = "") -> Response:
    """Returns HTTP 201 Created response with optional location header"""
    pass

def accepted(message: Any = None) -> Response:
    """Returns HTTP 202 Accepted response"""
    pass

def no_content() -> Response:
    """Returns HTTP 204 No Content response"""
    pass

# Error responses
def bad_request(message: Any = None) -> Response:
    """Returns HTTP 400 Bad Request response"""
    pass

def unauthorized(message: str = "Unauthorized") -> Response:
    """Returns HTTP 401 Unauthorized response"""
    pass

def forbidden(message: str = "Forbidden") -> Response:
    """Returns HTTP 403 Forbidden response"""
    pass

def not_found() -> Response:
    """Returns HTTP 404 Not Found response"""
    pass

# File responses
def file(
    value: FileInput,
    content_type: str,
    *,
    file_name: str = None,
    content_disposition: ContentDispositionType = ContentDispositionType.ATTACHMENT,
) -> Response:
    """Returns file response with content type and disposition"""
    pass

# Content responses
def json(obj: Any) -> Response:
    """Returns JSON response"""
    pass

def text(message: str) -> Response:
    """Returns plain text response"""
    pass

def html(content: str) -> Response:
    """Returns HTML response"""
    pass

def redirect(location: str, permanent: bool = False) -> Response:
    """Returns redirect response (302 or 301)"""
    pass

Response Helper Examples

# File download with custom filename
@app.get("/download/{file_id}")
async def download_file(file_id: int):
    file_data = await get_file_data(file_id)
    return file(
        file_data,
        "application/pdf",
        file_name="document.pdf",
        content_disposition=ContentDispositionType.ATTACHMENT
    )

# File display inline
@app.get("/preview/{image_id}")
async def preview_image(image_id: int):
    image_data = await get_image_data(image_id)
    return file(
        image_data,
        "image/jpeg",
        content_disposition=ContentDispositionType.INLINE
    )

# Created response with location
@app.post("/users")
async def create_user(user_data: FromJSON[dict]):
    user = await create_user_in_db(user_data.value)
    return created(
        {"id": user.id, "name": user.name},
        location=f"/users/{user.id}"
    )

Content Types

BlackSheep provides various content types for request and response bodies.

Base Content Class

from blacksheep.contents import Content

# Content properties and methods
class CustomContent(Content):
    def __init__(self, data: str):
        content_type = b"application/custom"
        encoded_data = data.encode('utf-8')
        super().__init__(content_type, encoded_data)
    
    async def read(self) -> bytes:
        return self.body
    
    def dispose(self):
        # Cleanup resources
        pass

# Content properties
content_type: bytes = content.type
content_body: bytes = content.body
content_length: int = content.length

Text Content

from blacksheep.contents import TextContent

# Plain text content
text_content = TextContent("Hello, World!")
# Content-Type: text/plain; charset=utf-8

# Custom encoding (defaults to UTF-8)
text_content = TextContent("Hello", encoding="latin1")

HTML Content

from blacksheep.contents import HTMLContent, HtmlContent

# HTML content (both aliases work)
html_content = HTMLContent("<h1>Hello</h1>")
html_content = HtmlContent("<p>Paragraph</p>")
# Content-Type: text/html; charset=utf-8

# Template rendering example
template = "<h1>Hello, {{name}}!</h1>"
rendered = template.replace("{{name}}", "Alice")
html_content = HTMLContent(rendered)

JSON Content

from blacksheep.contents import JSONContent
import json

# JSON content with default serializer
data = {"users": [{"id": 1, "name": "Alice"}]}
json_content = JSONContent(data)
# Content-Type: application/json

# Custom JSON serializer
def custom_json_dumps(obj):
    return json.dumps(obj, indent=2, sort_keys=True)

json_content = JSONContent(data, dumps=custom_json_dumps)

# Complex data serialization
from dataclasses import dataclass, asdict

@dataclass
class User:
    id: int
    name: str

user = User(1, "Alice")
json_content = JSONContent(asdict(user))

Form Content

from blacksheep.contents import FormContent
from typing import Dict, List, Tuple, Union

# URL-encoded form data
form_data: Dict[str, str] = {
    "username": "alice",
    "password": "secret",
    "remember": "on"
}
form_content = FormContent(form_data)
# Content-Type: application/x-www-form-urlencoded

# Multiple values for same key
form_data: List[Tuple[str, str]] = [
    ("tags", "python"),
    ("tags", "web"),
    ("tags", "framework"),
    ("title", "My Post")
]
form_content = FormContent(form_data)

# Parsing form data
from blacksheep.contents import parse_www_form

form_string = "name=Alice&tags=python&tags=web"
parsed: Dict[str, Union[str, List[str]]] = parse_www_form(form_string)
# {"name": "Alice", "tags": ["python", "web"]}

Multipart Form Data

from blacksheep.contents import MultiPartFormData, FormPart

# Create form parts
text_part = FormPart(
    name=b"title",
    data=b"My Blog Post",
    content_type=b"text/plain"
)

file_part = FormPart(
    name=b"avatar",
    data=image_bytes,
    content_type=b"image/jpeg",
    file_name=b"profile.jpg",
    charset=b"utf-8"
)

# Multipart form
multipart = MultiPartFormData([text_part, file_part])
# Content-Type: multipart/form-data; boundary=...

# Access multipart properties
parts = multipart.parts
boundary = multipart.boundary

Streamed Content

from blacksheep.contents import StreamedContent
from typing import AsyncIterable

# Streaming content for large responses
async def data_generator() -> AsyncIterable[bytes]:
    for i in range(1000):
        yield f"Data chunk {i}\n".encode()

streamed = StreamedContent(
    content_type=b"text/plain",
    data_provider=data_generator,
    data_length=-1  # Unknown length
)

# Known length streaming
def file_streamer():
    with open("large_file.txt", "rb") as f:
        while chunk := f.read(8192):
            yield chunk

file_size = os.path.getsize("large_file.txt")
streamed = StreamedContent(
    content_type=b"application/octet-stream",
    data_provider=file_streamer,
    data_length=file_size
)

ASGI Content

from blacksheep.contents import ASGIContent
from typing import Callable, Dict, Any

# Content from ASGI receive callable
async def asgi_handler(scope: Dict[str, Any], receive: Callable, send: Callable):
    # Create content from ASGI receive
    content = ASGIContent(receive)
    
    # Stream content
    async for chunk in content.stream():
        process_chunk(chunk)
    
    # Or read all at once
    body = await content.read()

Headers

BlackSheep provides comprehensive header handling with case-insensitive access and manipulation.

Headers Collection

from blacksheep.headers import Headers, Header
from typing import List, Tuple, Optional, Dict

# Header type
HeaderType = Tuple[bytes, bytes]

# Create headers collection
headers = Headers([
    (b"Content-Type", b"application/json"),
    (b"Authorization", b"Bearer token123"),
    (b"Accept", b"application/json"),
    (b"Accept", b"application/xml")  # Multiple values
])

# Empty headers
headers = Headers()

# Header access methods
content_type_headers: Tuple[HeaderType] = headers.get(b"Content-Type")
header_tuples: List[HeaderType] = headers.get_tuples(b"Accept")
first_auth: Optional[bytes] = headers.get_first(b"Authorization")
single_ct: bytes = headers.get_single(b"Content-Type")  # Throws if multiple

# Header manipulation
headers.add(b"X-Custom", b"value1")
headers.add(b"X-Custom", b"value2")  # Multiple values allowed
headers.set(b"Content-Type", b"text/html")  # Replace all existing
headers.remove(b"X-Remove-Me")  # Remove all with this name

# Dictionary-like access
headers[b"Content-Type"] = b"application/json"  # Set header
value = headers[b"Authorization"]  # Get first value
del headers[b"X-Temp"]  # Remove header
has_header = b"Content-Type" in headers  # Check existence

Header Utilities

# Header iteration
for name, value in headers.items():
    print(f"{name.decode()}: {value.decode()}")

# Header keys
header_names = headers.keys()

# Header merging
new_headers = [(b"X-New", b"value")]
headers.merge(new_headers)

# Dictionary update
header_dict = {b"Cache-Control": b"no-cache", b"X-Frame-Options": b"DENY"}
headers.update(header_dict)

# Add multiple headers
headers.add_many([
    (b"X-Custom-1", b"value1"),
    (b"X-Custom-2", b"value2")
])

# Or from dictionary
headers.add_many({
    b"X-API-Version": b"1.0",
    b"X-Rate-Limit": b"100"
})

# Header cloning
cloned_headers = headers.clone()

# Header combination
combined = headers + other_headers
headers += more_headers

Individual Header

from blacksheep.headers import Header

# Create individual header
header = Header(b"Content-Type", b"application/json")

# Header properties
name: bytes = header.name    # b"Content-Type"
value: bytes = header.value  # b"application/json"

# Header iteration (name, then value)
for item in header:
    print(item)  # b"Content-Type", then b"application/json"

# Header equality
header1 = Header(b"Content-Type", b"application/json")
header2 = Header(b"content-type", b"application/json")  
are_equal = header1 == header2  # Case-insensitive comparison

Cookies

BlackSheep provides comprehensive cookie support with security features and expiration handling.

Cookie Class

from blacksheep.cookies import Cookie, CookieSameSiteMode
from datetime import datetime, timedelta

# Basic cookie
cookie = Cookie("session_id", "abc123def456")

# Full cookie configuration
cookie = Cookie(
    name="user_session",
    value="encrypted_session_data",
    expires=datetime.utcnow() + timedelta(days=30),
    domain="example.com",
    path="/",
    http_only=True,      # Prevent JavaScript access
    secure=True,         # Require HTTPS
    max_age=2592000,     # 30 days in seconds
    same_site=CookieSameSiteMode.LAX
)

# Cookie properties
name: str = cookie.name
value: str = cookie.value  
expires: Optional[datetime] = cookie.expires
domain: Optional[str] = cookie.domain
path: Optional[str] = cookie.path
http_only: bool = cookie.http_only
secure: bool = cookie.secure
max_age: int = cookie.max_age
same_site: CookieSameSiteMode = cookie.same_site

SameSite Modes

from blacksheep.cookies import CookieSameSiteMode

# SameSite attribute values
CookieSameSiteMode.UNDEFINED  # Not specified
CookieSameSiteMode.LAX       # Lax mode (default for most browsers)
CookieSameSiteMode.STRICT    # Strict mode (no cross-site requests)
CookieSameSiteMode.NONE      # None mode (requires Secure=True)

# Usage examples
session_cookie = Cookie("session", "value", same_site=CookieSameSiteMode.LAX)
csrf_cookie = Cookie("csrf", "token", same_site=CookieSameSiteMode.STRICT)
tracking_cookie = Cookie("track", "id", same_site=CookieSameSiteMode.NONE, secure=True)

Cookie Utilities

from blacksheep.cookies import (
    datetime_to_cookie_format,
    datetime_from_cookie_format,
    parse_cookie,
    write_response_cookie
)

# DateTime formatting for cookies
expire_time = datetime.utcnow() + timedelta(hours=24)
cookie_date: bytes = datetime_to_cookie_format(expire_time)
parsed_date: datetime = datetime_from_cookie_format(cookie_date)

# Cookie parsing from header value
cookie_header = b"session_id=abc123; Path=/; HttpOnly"
parsed_cookie: Cookie = parse_cookie(cookie_header)

# Cookie serialization for Set-Cookie header
cookie = Cookie("session", "value123", http_only=True)
set_cookie_header: bytes = write_response_cookie(cookie)
# b"session=value123; HttpOnly"

# Cookie cloning and comparison
cloned_cookie = cookie.clone()
is_equal = cookie == "session"  # Compare by name
is_equal = cookie == cookie2    # Full comparison

URL Handling

BlackSheep provides comprehensive URL parsing, validation, and manipulation capabilities.

URL Class

from blacksheep.url import URL, InvalidURL

# URL creation and parsing
try:
    url = URL(b"https://api.example.com:8080/users/123?q=search&tag=python#section")
except InvalidURL as e:
    print(f"Invalid URL: {e}")

# URL components
schema: Optional[bytes] = url.schema      # b"https"
host: Optional[bytes] = url.host          # b"api.example.com"
port: int = url.port                      # 8080
path: bytes = url.path                    # b"/users/123"
query: bytes = url.query                  # b"q=search&tag=python"
fragment: Optional[bytes] = url.fragment  # b"section"
value: bytes = url.value                  # Original URL bytes

# URL properties
is_absolute: bool = url.is_absolute

URL Manipulation

# URL modification
base_url = URL(b"https://api.example.com")
new_url = base_url.with_host(b"api2.example.com")
secure_url = base_url.with_scheme(b"https")

# URL joining
base = URL(b"https://api.example.com/v1")
relative = URL(b"users/123")
full_url = base.join(relative)  # https://api.example.com/v1/users/123

# Get base URL (scheme + host + port)
base_only = url.base_url()  # https://api.example.com:8080

# URL concatenation
url1 = URL(b"https://api.example.com")
url2 = url1 + b"/users"  # URL + bytes
url3 = url1 + URL(b"/posts")  # URL + URL

URL Validation

# URL validation
def validate_url(url_string: str) -> bool:
    try:
        URL(url_string.encode())
        return True
    except InvalidURL:
        return False

# Safe URL creation
def safe_url(url_string: str) -> Optional[URL]:
    try:
        return URL(url_string.encode())
    except InvalidURL:
        return None

Data Binding

BlackSheep provides powerful data binding capabilities to extract and convert request data into typed parameters.

Request Data Binding Types

from blacksheep.server.bindings import (
    FromJSON, FromQuery, FromRoute, FromForm,
    FromHeader, FromCookie, FromServices, FromFiles,
    FromBytes, FromText, 
    ClientInfo, ServerInfo, RequestUser, RequestURL, RequestMethod
)

# JSON body binding
@app.post("/users")
async def create_user(user_data: FromJSON[dict]):
    user = user_data.value  # Parsed JSON dict
    return json({"created": True, "user": user})

# Type-safe JSON binding with dataclass
from dataclasses import dataclass

@dataclass  
class CreateUserRequest:
    name: str
    email: str
    age: int

@app.post("/users")
async def create_user_typed(request: FromJSON[CreateUserRequest]):
    user = request.value  # CreateUserRequest instance
    return json({"name": user.name, "email": user.email})

Route Parameter Binding

# Route parameter binding with type conversion
@app.get("/users/{user_id:int}")
async def get_user(user_id: FromRoute[int]):
    user_id_value: int = user_id.value
    return json({"user_id": user_id_value})

# Multiple route parameters
@app.get("/users/{user_id:int}/posts/{post_id:int}")
async def get_user_post(
    user_id: FromRoute[int], 
    post_id: FromRoute[int]
):
    return json({
        "user_id": user_id.value,
        "post_id": post_id.value
    })

# String route parameters (default)
@app.get("/categories/{category_name}")
async def get_category(category_name: FromRoute[str]):
    name: str = category_name.value
    return json({"category": name})

Query Parameter Binding

# Single query parameter
@app.get("/search")
async def search(q: FromQuery[str]):
    query: str = q.value
    return json({"query": query})

# Optional query parameters
@app.get("/users")  
async def list_users(
    page: FromQuery[int] = FromQuery(1),
    limit: FromQuery[int] = FromQuery(10)
):
    return json({
        "page": page.value,
        "limit": limit.value
    })

# Multiple values for same parameter
@app.get("/posts")
async def filter_posts(tags: FromQuery[List[str]]):
    tag_list: List[str] = tags.value  # ["python", "web", "api"]
    return json({"tags": tag_list})

Form Data Binding

# Form data binding
@app.post("/contact")
async def contact_form(form_data: FromForm[dict]):
    data: dict = form_data.value
    name = data.get("name")
    email = data.get("email")
    return json({"received": True})

# Typed form binding
@dataclass
class ContactForm:
    name: str
    email: str
    message: str

@app.post("/contact")  
async def contact_typed(form: FromForm[ContactForm]):
    contact: ContactForm = form.value
    return json({
        "name": contact.name,
        "email": contact.email
    })

Header and Cookie Binding

# Header binding
@app.get("/protected")
async def protected_endpoint(auth_header: FromHeader[str]):
    # Header: Authorization: Bearer token123
    token: str = auth_header.value  # "Bearer token123"
    return json({"authorized": True})

# Specific header name
@app.get("/api")
async def api_endpoint(api_key: FromHeader[str] = FromHeader(name="X-API-Key")):
    key: str = api_key.value
    return json({"api_key_received": True})

# Cookie binding
@app.get("/dashboard")
async def dashboard(session_id: FromCookie[str]):
    session: str = session_id.value
    return json({"session": session})

# Optional cookie with default
@app.get("/preferences")
async def preferences(
    theme: FromCookie[str] = FromCookie("light", name="theme")
):
    user_theme: str = theme.value
    return json({"theme": user_theme})

File Upload Binding

# File upload binding
@app.post("/upload")
async def upload_file(files: FromFiles):
    uploaded_files = files.value  # List[FormPart]
    
    for file_part in uploaded_files:
        filename = file_part.file_name.decode() if file_part.file_name else "unknown"
        content_type = file_part.content_type.decode() if file_part.content_type else "unknown"
        file_size = len(file_part.data)
        
        # Save file
        with open(f"uploads/{filename}", "wb") as f:
            f.write(file_part.data)
    
    return json({"uploaded": len(uploaded_files)})

# Specific file field
@app.post("/avatar")
async def upload_avatar(avatar: FromFiles = FromFiles(name="avatar")):
    if avatar.value:
        file_part = avatar.value[0]  # First file
        # Process avatar
    return json({"avatar_uploaded": True})

Raw Body Binding

# Raw bytes binding
@app.post("/binary")
async def handle_binary(body: FromBytes):
    raw_data: bytes = body.value
    return Response(200, content=TextContent(f"Received {len(raw_data)} bytes"))

# Text binding  
@app.post("/text")
async def handle_text(text: FromText):
    content: str = text.value
    return json({"text_length": len(content)})

Dependency Injection Binding

# Service injection
@app.get("/users/{user_id:int}")
async def get_user(
    user_id: FromRoute[int],
    user_service: FromServices[UserService]
):
    service: UserService = user_service.value
    user = await service.get_by_id(user_id.value)
    return json(user)

# Multiple services
@app.post("/orders")
async def create_order(
    order_data: FromJSON[dict],
    order_service: FromServices[OrderService],
    email_service: FromServices[EmailService]
):
    order = await order_service.create(order_data.value)
    await email_service.send_confirmation(order.email)
    return json({"order_id": order.id})

Context Information Binding

BlackSheep provides several binding classes to access request context information like client details, server info, and user identity.

from blacksheep.server.bindings import (
    ClientInfo, ServerInfo, RequestUser, RequestURL, RequestMethod
)
from guardpost.authentication import Identity
from blacksheep import URL

# Context binding classes
class ClientInfo(BoundValue[Tuple[str, int]]):
    """Client IP and port information obtained from request scope"""
    pass

class ServerInfo(BoundValue[Tuple[str, int]]):
    """Server IP and port information obtained from request scope"""
    pass

class RequestUser(BoundValue[Identity]):
    """Returns the identity of the authenticated user"""
    pass

class RequestURL(BoundValue[URL]):
    """Returns the URL of the request"""
    pass

class RequestMethod(BoundValue[str]):
    """Returns the HTTP Method of the request"""
    pass

Context Binding Usage Examples

# Client connection information
@app.get("/info")
async def client_info(client: ClientInfo):
    ip, port = client.value
    return json({"client_ip": ip, "client_port": port})

# Server information  
@app.get("/server-info")
async def server_info(server: ServerInfo):
    host, port = server.value
    return json({"server_host": host, "server_port": port})

# Authenticated user information
@app.get("/profile")
async def user_profile(user: RequestUser):
    identity: Identity = user.value
    return json({
        "user_id": identity.claims.get("sub"),
        "name": identity.claims.get("name"),
        "roles": identity.claims.get("roles", [])
    })

# Request URL information
@app.get("/request-info")
async def request_info(url: RequestURL, method: RequestMethod):
    request_url: URL = url.value
    http_method: str = method.value
    return json({
        "method": http_method,
        "path": request_url.path,
        "query": request_url.query,
        "scheme": request_url.scheme,
        "host": request_url.host
    })

This comprehensive request/response handling system provides type-safe, efficient processing of HTTP messages with rich content type support and flexible data binding capabilities.

Install with Tessl CLI

npx tessl i tessl/pypi-blacksheep

docs

additional.md

auth.md

client.md

core-server.md

index.md

request-response.md

testing.md

websockets.md

tile.json