or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

additional.mdauth.mdclient.mdcore-server.mdindex.mdrequest-response.mdtesting.mdwebsockets.md

additional.mddocs/

0

# Additional Features

1

2

This document covers additional BlackSheep features including CORS, CSRF protection, sessions, OpenAPI documentation, security headers, compression, templating, and file handling utilities.

3

4

## CORS (Cross-Origin Resource Sharing)

5

6

BlackSheep provides comprehensive CORS support for handling cross-origin requests from web browsers.

7

8

### Basic CORS Setup

9

10

```python { .api }

11

from blacksheep import Application

12

from blacksheep.server.cors import CORSPolicy, CORSStrategy

13

14

app = Application()

15

16

# Simple CORS setup (allows all origins)

17

app.use_cors()

18

19

# Configure CORS with specific options

20

cors_strategy = app.use_cors(

21

allow_origins=["https://example.com", "https://app.example.com"],

22

allow_methods=["GET", "POST", "PUT", "DELETE", "OPTIONS"],

23

allow_headers=["Content-Type", "Authorization", "X-Requested-With"],

24

allow_credentials=True,

25

max_age=3600, # Cache preflight for 1 hour

26

expose_headers=["X-Total-Count", "X-Page-Info"]

27

)

28

```

29

30

### Named CORS Policies

31

32

```python { .api }

33

# Multiple CORS policies for different endpoints

34

app.use_cors() # Default policy

35

36

# API-specific policy

37

app.add_cors_policy("api",

38

allow_origins=["https://api-client.example.com"],

39

allow_methods=["GET", "POST", "PUT", "DELETE"],

40

allow_headers=["Authorization", "Content-Type"],

41

allow_credentials=True

42

)

43

44

# Public API policy

45

app.add_cors_policy("public",

46

allow_origins="*",

47

allow_methods=["GET", "OPTIONS"],

48

allow_headers=["Content-Type"],

49

allow_credentials=False

50

)

51

52

# Widget embed policy

53

app.add_cors_policy("embed",

54

allow_origins=["https://widget.example.com", "https://embed.example.com"],

55

allow_methods=["GET", "POST"],

56

allow_headers=["Content-Type", "X-Widget-Token"],

57

expose_headers=["X-Widget-Version"]

58

)

59

```

60

61

### CORS Policy Configuration

62

63

```python { .api }

64

from blacksheep.server.cors import CORSPolicy

65

66

# Manual CORS policy creation

67

cors_policy = CORSPolicy(

68

allow_methods=["GET", "POST", "PUT", "DELETE", "PATCH"],

69

allow_headers=["Content-Type", "Authorization", "X-API-Key"],

70

allow_origins=["https://trusted-domain.com"],

71

allow_credentials=True,

72

max_age=86400, # 24 hours

73

expose_headers=["X-Rate-Limit-Remaining", "X-Rate-Limit-Reset"]

74

)

75

76

# Advanced CORS strategy

77

strategy = CORSStrategy()

78

strategy.add_policy("default", cors_policy)

79

80

# Custom origin validation

81

def validate_origin(origin: str) -> bool:

82

# Custom logic for validating origins

83

allowed_patterns = ["*.example.com", "localhost:*"]

84

return any(matches_pattern(origin, pattern) for pattern in allowed_patterns)

85

86

cors_policy = CORSPolicy(

87

allow_origins=validate_origin, # Function for dynamic validation

88

allow_methods="*",

89

allow_headers="*"

90

)

91

```

92

93

### Route-Specific CORS

94

95

```python { .api }

96

from blacksheep.server.cors import cors

97

98

# Apply CORS to specific routes

99

@app.get("/api/public")

100

@cors("public") # Use "public" CORS policy

101

async def public_api():

102

return json({"public": True})

103

104

@app.get("/api/private")

105

@cors("api") # Use "api" CORS policy

106

async def private_api():

107

return json({"private": True})

108

109

# Route without CORS (uses default if set)

110

@app.get("/internal")

111

async def internal_api():

112

return json({"internal": True})

113

```

114

115

## CSRF Protection

116

117

Cross-Site Request Forgery protection for web applications.

118

119

### CSRF Setup

120

121

```python { .api }

122

from blacksheep.server.csrf import CSRFMiddleware, CSRFTokenProvider

123

124

# Basic CSRF protection

125

csrf_middleware = CSRFMiddleware(

126

secret_key="csrf-secret-key",

127

cookie_name="csrf_token",

128

header_name="X-CSRF-Token",

129

safe_methods=["GET", "HEAD", "OPTIONS", "TRACE"]

130

)

131

132

app.middlewares.append(csrf_middleware)

133

134

# Advanced CSRF configuration

135

csrf_middleware = CSRFMiddleware(

136

secret_key="csrf-secret-key",

137

cookie_name="csrf_token",

138

header_name="X-CSRF-Token",

139

form_field_name="csrf_token", # Form field name for token

140

token_length=32, # Token length in bytes

141

max_age=3600, # Token expiration in seconds

142

domain=".example.com", # Cookie domain

143

secure=True, # Secure cookie flag

144

same_site="Strict" # SameSite policy

145

)

146

```

147

148

### CSRF Token Usage

149

150

```python { .api }

151

from blacksheep import Request, Response

152

153

# Generate CSRF token for forms

154

@app.get("/form")

155

async def show_form(request: Request):

156

csrf_token = request.csrf_token # Auto-generated token

157

158

form_html = f"""

159

<form method="post" action="/submit">

160

<input type="hidden" name="csrf_token" value="{csrf_token}">

161

<input type="text" name="data" placeholder="Enter data">

162

<button type="submit">Submit</button>

163

</form>

164

"""

165

166

return HTMLContent(form_html)

167

168

# CSRF-protected endpoint

169

@app.post("/submit")

170

async def submit_form(request: Request, form_data: FromForm[dict]):

171

# CSRF validation happens automatically in middleware

172

data = form_data.value

173

return json({"submitted": True, "data": data})

174

175

# API endpoint with CSRF token in header

176

@app.post("/api/action")

177

async def api_action(request: Request, data: FromJSON[dict]):

178

# Client should send X-CSRF-Token header

179

return json({"action": "completed", "data": data.value})

180

```

181

182

### CSRF Exemptions

183

184

```python { .api }

185

from blacksheep.server.csrf import csrf_exempt

186

187

# Exempt specific endpoints from CSRF protection

188

@app.post("/webhook")

189

@csrf_exempt

190

async def webhook_handler(request: Request):

191

# Webhooks don't need CSRF protection

192

payload = await request.json()

193

return json({"webhook": "processed"})

194

195

# API endpoints with other authentication

196

@app.post("/api/oauth")

197

@csrf_exempt

198

async def oauth_endpoint(request: Request):

199

# OAuth endpoints have their own protection

200

return json({"oauth": "handled"})

201

```

202

203

## Sessions

204

205

Secure session management with encryption and signing.

206

207

### Session Configuration

208

209

```python { .api }

210

from blacksheep.sessions import SessionMiddleware

211

from blacksheep.sessions.crypto import Encryptor, Signer, SessionSerializer

212

213

# Basic session setup

214

app.use_sessions(

215

secret_key="session-secret-key",

216

session_cookie="session",

217

session_max_age=3600 # 1 hour

218

)

219

220

# Advanced session configuration

221

custom_encryptor = Encryptor("encryption-key-32-bytes-long-key")

222

custom_signer = Signer("signing-key")

223

custom_serializer = SessionSerializer(

224

encryptor=custom_encryptor,

225

signer=custom_signer

226

)

227

228

session_middleware = SessionMiddleware(

229

secret_key="master-secret-key",

230

session_cookie="secure_session",

231

serializer=custom_serializer,

232

session_max_age=86400, # 24 hours

233

cookie_domain=".example.com",

234

cookie_secure=True,

235

cookie_same_site="Lax"

236

)

237

238

app.middlewares.append(session_middleware)

239

```

240

241

### Session Usage

242

243

```python { .api }

244

from blacksheep.sessions import Session

245

246

# Login with session

247

@app.post("/login")

248

async def login(request: Request, credentials: FromJSON[dict]):

249

username = credentials.value.get("username")

250

password = credentials.value.get("password")

251

252

# Validate credentials

253

user = await authenticate_user(username, password)

254

if not user:

255

raise Unauthorized("Invalid credentials")

256

257

# Set session data

258

session: Session = request.session

259

session["user_id"] = user.id

260

session["username"] = user.username

261

session["login_time"] = datetime.utcnow().isoformat()

262

session["permissions"] = user.permissions

263

264

return json({"message": "Login successful"})

265

266

# Access session data

267

@app.get("/dashboard")

268

async def dashboard(request: Request):

269

session = request.session

270

271

user_id = session.get("user_id")

272

if not user_id:

273

return redirect("/login")

274

275

return json({

276

"user_id": user_id,

277

"username": session.get("username"),

278

"login_time": session.get("login_time")

279

})

280

281

# Logout

282

@app.post("/logout")

283

async def logout(request: Request):

284

session = request.session

285

session.clear() # Clear all session data

286

return json({"message": "Logged out successfully"})

287

288

# Partial session update

289

@app.post("/preferences")

290

async def update_preferences(request: Request, prefs: FromJSON[dict]):

291

session = request.session

292

293

if "user_id" not in session:

294

raise Unauthorized("Not logged in")

295

296

# Update specific session keys

297

session["theme"] = prefs.value.get("theme", "light")

298

session["language"] = prefs.value.get("language", "en")

299

300

return json({"preferences": "updated"})

301

```

302

303

## OpenAPI Documentation

304

305

Automatic API documentation generation with interactive UI.

306

307

### OpenAPI Setup

308

309

```python { .api }

310

from blacksheep.server.openapi.v3 import OpenAPIHandler

311

from blacksheep.server.openapi.ui import ReDocMiddleware, SwaggerUIMiddleware

312

313

app = Application()

314

315

# Configure OpenAPI

316

docs = OpenAPIHandler(

317

info={

318

"title": "My API",

319

"version": "1.0.0",

320

"description": "A comprehensive API built with BlackSheep"

321

},

322

servers=[

323

{"url": "https://api.example.com", "description": "Production"},

324

{"url": "https://staging-api.example.com", "description": "Staging"}

325

]

326

)

327

328

# Add OpenAPI documentation endpoints

329

docs.bind_app(app)

330

331

# Add Swagger UI

332

app.middlewares.append(SwaggerUIMiddleware(docs, path="/docs"))

333

334

# Add ReDoc UI

335

app.middlewares.append(ReDocMiddleware(docs, path="/redoc"))

336

```

337

338

### API Documentation

339

340

```python { .api }

341

from typing import Optional, List

342

from dataclasses import dataclass

343

from blacksheep.server.openapi.v3 import doc

344

345

@dataclass

346

class User:

347

id: int

348

name: str

349

email: str

350

age: Optional[int] = None

351

352

@dataclass

353

class CreateUserRequest:

354

name: str

355

email: str

356

age: Optional[int] = None

357

358

@dataclass

359

class ErrorResponse:

360

error: str

361

message: str

362

code: Optional[int] = None

363

364

# Document endpoints with OpenAPI decorators

365

@app.get("/users")

366

@doc(

367

summary="List all users",

368

description="Retrieve a paginated list of all users in the system",

369

responses={

370

200: "List of users retrieved successfully",

371

401: "Authentication required"

372

},

373

tags=["Users"]

374

)

375

async def list_users(

376

page: FromQuery[int] = FromQuery(1),

377

limit: FromQuery[int] = FromQuery(10)

378

) -> List[User]:

379

# Implementation here

380

users = await get_users(page.value, limit.value)

381

return json([user.__dict__ for user in users])

382

383

@app.post("/users")

384

@doc(

385

summary="Create new user",

386

description="Create a new user account with the provided information",

387

responses={

388

201: "User created successfully",

389

400: "Invalid user data",

390

409: "User already exists"

391

},

392

tags=["Users"]

393

)

394

async def create_user(data: FromJSON[CreateUserRequest]) -> User:

395

user = await create_user_in_db(data.value)

396

return json(user.__dict__)

397

398

@app.get("/users/{user_id}")

399

@doc(

400

summary="Get user by ID",

401

description="Retrieve a specific user by their unique identifier",

402

responses={

403

200: "User found and returned",

404

404: "User not found"

405

},

406

tags=["Users"]

407

)

408

async def get_user(user_id: FromRoute[int]) -> User:

409

user = await get_user_by_id(user_id.value)

410

if not user:

411

raise NotFound("User not found")

412

return json(user.__dict__)

413

```

414

415

### Security Schemes

416

417

```python { .api }

418

# Define security schemes

419

docs.security_schemes = {

420

"BearerAuth": {

421

"type": "http",

422

"scheme": "bearer",

423

"bearerFormat": "JWT"

424

},

425

"ApiKeyAuth": {

426

"type": "apiKey",

427

"in": "header",

428

"name": "X-API-Key"

429

},

430

"OAuth2": {

431

"type": "oauth2",

432

"flows": {

433

"authorizationCode": {

434

"authorizationUrl": "https://auth.example.com/oauth/authorize",

435

"tokenUrl": "https://auth.example.com/oauth/token",

436

"scopes": {

437

"read": "Read access",

438

"write": "Write access"

439

}

440

}

441

}

442

}

443

}

444

445

# Apply security to endpoints

446

@app.get("/protected")

447

@doc(

448

summary="Protected endpoint",

449

security=[{"BearerAuth": []}],

450

tags=["Protected"]

451

)

452

@auth()

453

async def protected_endpoint():

454

return json({"protected": True})

455

```

456

457

## Security Headers

458

459

HTTP security headers for improved application security.

460

461

### HSTS (HTTP Strict Transport Security)

462

463

```python { .api }

464

from blacksheep.server.security.hsts import HSTSMiddleware

465

466

# HSTS middleware

467

hsts_middleware = HSTSMiddleware(

468

max_age=31536000, # 1 year in seconds

469

include_subdomains=True,

470

preload=True # Include in HSTS preload list

471

)

472

473

app.middlewares.append(hsts_middleware)

474

475

# Custom HSTS configuration

476

hsts_middleware = HSTSMiddleware(

477

max_age=86400, # 1 day

478

include_subdomains=False,

479

preload=False,

480

only_https=True # Only add header for HTTPS requests

481

)

482

```

483

484

### Security Headers Middleware

485

486

```python { .api }

487

async def security_headers_middleware(request: Request, handler):

488

response = await handler(request)

489

490

# Add security headers

491

response.headers.add(b"X-Content-Type-Options", b"nosniff")

492

response.headers.add(b"X-Frame-Options", b"DENY")

493

response.headers.add(b"X-XSS-Protection", b"1; mode=block")

494

response.headers.add(b"Referrer-Policy", b"strict-origin-when-cross-origin")

495

496

# Content Security Policy

497

csp = (

498

"default-src 'self'; "

499

"script-src 'self' 'unsafe-inline'; "

500

"style-src 'self' 'unsafe-inline'; "

501

"img-src 'self' data: https:; "

502

"font-src 'self'; "

503

"connect-src 'self'; "

504

"frame-ancestors 'none';"

505

)

506

response.headers.add(b"Content-Security-Policy", csp.encode())

507

508

return response

509

510

app.middlewares.append(security_headers_middleware)

511

```

512

513

## Response Compression

514

515

Compress responses to reduce bandwidth usage.

516

517

### Gzip Compression

518

519

```python { .api }

520

from blacksheep.server.gzip import GzipMiddleware

521

522

# Basic gzip compression

523

gzip_middleware = GzipMiddleware(

524

minimum_size=1024, # Only compress responses > 1KB

525

compression_level=6, # Compression level (1-9)

526

content_types={ # MIME types to compress

527

"text/html",

528

"text/css",

529

"text/javascript",

530

"application/json",

531

"application/xml"

532

}

533

)

534

535

app.middlewares.append(gzip_middleware)

536

537

# Advanced compression configuration

538

gzip_middleware = GzipMiddleware(

539

minimum_size=500,

540

compression_level=9, # Maximum compression

541

content_types={

542

"text/*", # All text types

543

"application/json",

544

"application/xml",

545

"application/javascript"

546

},

547

exclude_paths={"/health", "/metrics"} # Paths to exclude

548

)

549

```

550

551

## Templating

552

553

Template engine integration for server-side rendering.

554

555

### Template Configuration

556

557

```python { .api }

558

from blacksheep import use_templates

559

from jinja2 import Environment, FileSystemLoader

560

561

# Configure Jinja2 templates

562

templates_env = Environment(

563

loader=FileSystemLoader("templates"),

564

autoescape=True,

565

enable_async=True

566

)

567

568

use_templates(app, templates_env)

569

570

# Template rendering endpoint

571

@app.get("/page/{name}")

572

async def render_page(name: str, request: Request):

573

# Render template with context

574

template = templates_env.get_template(f"{name}.html")

575

content = await template.render_async({

576

"title": f"Page: {name}",

577

"user": request.identity,

578

"current_time": datetime.now()

579

})

580

581

return HTMLContent(content)

582

583

# Template with form

584

@app.get("/contact")

585

async def contact_form():

586

template = templates_env.get_template("contact.html")

587

content = await template.render_async({

588

"csrf_token": generate_csrf_token()

589

})

590

return HTMLContent(content)

591

```

592

593

## File Handling

594

595

Advanced file serving and handling capabilities.

596

597

### Dynamic File Serving

598

599

```python { .api }

600

from blacksheep.server.files.dynamic import serve_files_dynamic

601

from blacksheep.server.files import DefaultFileOptions

602

603

# Dynamic file serving with real-time discovery

604

serve_files_dynamic(

605

app,

606

source_folder="./uploads",

607

discovery=True, # Allow directory listing

608

cache_time=300, # Cache for 5 minutes

609

extensions={".jpg", ".png", ".pdf", ".doc"},

610

root_path="/uploads",

611

allow_anonymous=False, # Require authentication

612

default_file_options=DefaultFileOptions(

613

cache_time=3600,

614

content_disposition_type="attachment" # Force download

615

)

616

)

617

618

# Secure file serving

619

@app.get("/secure-files/{filename}")

620

@auth()

621

async def secure_file_download(filename: str, request: Request):

622

# Validate user has access to file

623

user_id = request.identity.id

624

if not await user_can_access_file(user_id, filename):

625

raise Forbidden("Access denied")

626

627

# Serve file securely

628

file_path = f"secure_uploads/{filename}"

629

return file(file_path, content_disposition_type="attachment")

630

```

631

632

### File Upload Handling

633

634

```python { .api }

635

import os

636

from pathlib import Path

637

638

@app.post("/upload")

639

async def upload_files(files: FromFiles, request: Request):

640

uploaded_files = files.value

641

results = []

642

643

for file_part in uploaded_files:

644

# Validate file

645

if not file_part.file_name:

646

continue

647

648

filename = file_part.file_name.decode()

649

content_type = file_part.content_type.decode() if file_part.content_type else "unknown"

650

651

# Security: validate file type

652

allowed_types = {"image/jpeg", "image/png", "application/pdf"}

653

if content_type not in allowed_types:

654

results.append({"filename": filename, "error": "File type not allowed"})

655

continue

656

657

# Security: sanitize filename

658

safe_filename = sanitize_filename(filename)

659

660

# Save file

661

upload_path = Path("uploads") / safe_filename

662

upload_path.parent.mkdir(exist_ok=True)

663

664

with open(upload_path, "wb") as f:

665

f.write(file_part.data)

666

667

results.append({

668

"filename": safe_filename,

669

"size": len(file_part.data),

670

"type": content_type,

671

"uploaded": True

672

})

673

674

return json({"files": results})

675

676

def sanitize_filename(filename: str) -> str:

677

"""Sanitize filename to prevent directory traversal"""

678

# Remove path components and dangerous characters

679

safe_name = os.path.basename(filename)

680

safe_name = "".join(c for c in safe_name if c.isalnum() or c in "._-")

681

return safe_name[:100] # Limit length

682

```

683

684

## Utilities and Helpers

685

686

Various utility functions and helper classes.

687

688

### URL and Path Utilities

689

690

```python { .api }

691

from blacksheep.utils import ensure_bytes, ensure_str, join_fragments

692

693

# String/bytes conversion

694

text = "Hello, World!"

695

bytes_data = ensure_bytes(text) # b"Hello, World!"

696

text_again = ensure_str(bytes_data) # "Hello, World!"

697

698

# URL path joining

699

path = join_fragments("api", "v1", "users", "123") # "api/v1/users/123"

700

path_with_slashes = join_fragments("/api/", "/v1/", "/users/") # "api/v1/users"

701

```

702

703

### Asyncio Utilities

704

705

```python { .api }

706

from blacksheep.utils.aio import get_running_loop

707

import asyncio

708

709

# Get current event loop

710

loop = get_running_loop()

711

712

# Async context management helpers

713

@asynccontextmanager

714

async def database_context():

715

db = await connect_database()

716

try:

717

yield db

718

finally:

719

await db.close()

720

721

# Use context manager

722

async with database_context() as db:

723

users = await db.query("SELECT * FROM users")

724

```

725

726

### Middleware Utilities

727

728

```python { .api }

729

from blacksheep.middlewares import get_middlewares_chain

730

731

# Create middleware chain

732

async def middleware1(request, handler):

733

print("Middleware 1 - before")

734

response = await handler(request)

735

print("Middleware 1 - after")

736

return response

737

738

async def middleware2(request, handler):

739

print("Middleware 2 - before")

740

response = await handler(request)

741

print("Middleware 2 - after")

742

return response

743

744

async def final_handler(request):

745

return json({"message": "Final handler"})

746

747

# Chain middlewares

748

middlewares = [middleware1, middleware2]

749

chained_handler = get_middlewares_chain(middlewares, final_handler)

750

751

# Execute chain

752

response = await chained_handler(request)

753

# Output:

754

# Middleware 1 - before

755

# Middleware 2 - before

756

# Middleware 2 - after

757

# Middleware 1 - after

758

```

759

760

These additional features provide comprehensive functionality for building secure, performant, and well-documented web applications with BlackSheep. The framework's modular design allows you to use only the features you need while maintaining excellent performance and developer experience.