or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

cli-commands.mdcore-classes.mdindex.mdopenapi-models.mdutilities.mdvalidation.md

validation.mddocs/

0

# Request Validation

1

2

Automatic request validation using Pydantic models for different parameter types. Flask-OpenAPI3 automatically validates incoming requests against Pydantic models and handles validation errors with customizable responses.

3

4

## Capabilities

5

6

### Automatic Request Validation

7

8

The core validation decorator that automatically validates requests based on function parameter type hints.

9

10

```python { .api }

11

def validate_request():

12

"""

13

Decorator for automatic request validation against Pydantic models.

14

15

Applied automatically when using typed parameters in route handlers.

16

Validates path, query, header, cookie, form, and body parameters.

17

18

Raises:

19

ValidationError: When request data doesn't match the expected schema

20

"""

21

```

22

23

**Usage Example:**

24

25

```python

26

from flask_openapi3 import OpenAPI, Info

27

from pydantic import BaseModel

28

29

app = OpenAPI(__name__, info=Info(title="API", version="1.0.0"))

30

31

class UserQuery(BaseModel):

32

name: str

33

age: int = None

34

35

class UserBody(BaseModel):

36

email: str

37

password: str

38

39

# Validation applied automatically based on parameter types

40

@app.post("/users")

41

def create_user(query: UserQuery, body: UserBody):

42

# query and body are automatically validated

43

return {"message": f"User {query.name} created with email {body.email}"}

44

```

45

46

### Internal Validation Functions

47

48

Core validation logic for different parameter types.

49

50

```python { .api }

51

def _validate_request(

52

header: Optional[Type[BaseModel]] = None,

53

cookie: Optional[Type[BaseModel]] = None,

54

path: Optional[Type[BaseModel]] = None,

55

query: Optional[Type[BaseModel]] = None,

56

form: Optional[Type[BaseModel]] = None,

57

body: Optional[Type[BaseModel]] = None,

58

raw: Optional[Type[RawModel]] = None,

59

path_kwargs: Optional[dict[Any, Any]] = None,

60

) -> dict:

61

"""

62

Core request validation logic.

63

64

Args:

65

header: Pydantic model for header parameters

66

cookie: Pydantic model for cookie parameters

67

path: Pydantic model for path parameters

68

query: Pydantic model for query parameters

69

form: Pydantic model for form data

70

body: Pydantic model for request body

71

raw: RawModel for raw request data

72

path_kwargs: Path parameters extracted from URL

73

74

Returns:

75

Dictionary of validated request parameters

76

"""

77

```

78

79

### Path Parameter Validation

80

81

Validates URL path parameters against Pydantic models.

82

83

```python { .api }

84

def _validate_path(path: Type[BaseModel], path_kwargs: dict, func_kwargs: dict):

85

"""

86

Validate path parameters from URL route.

87

88

Args:

89

path: Pydantic model defining expected path parameters

90

path_kwargs: Extracted path parameters from URL

91

func_kwargs: Function arguments to populate

92

93

Raises:

94

ValidationError: When path parameters don't match model

95

"""

96

```

97

98

**Usage Example:**

99

100

```python

101

from pydantic import BaseModel

102

103

class UserPath(BaseModel):

104

user_id: int

105

106

@app.get("/users/<int:user_id>")

107

def get_user(path: UserPath):

108

# path.user_id is validated as integer

109

return {"user_id": path.user_id}

110

```

111

112

### Query Parameter Validation

113

114

Validates URL query parameters against Pydantic models.

115

116

```python { .api }

117

def _validate_query(query: Type[BaseModel], func_kwargs: dict):

118

"""

119

Validate query parameters from URL query string.

120

121

Args:

122

query: Pydantic model defining expected query parameters

123

func_kwargs: Function arguments to populate

124

125

Raises:

126

ValidationError: When query parameters don't match model

127

"""

128

```

129

130

**Usage Example:**

131

132

```python

133

from pydantic import BaseModel

134

from typing import Optional

135

136

class SearchQuery(BaseModel):

137

q: str

138

limit: int = 10

139

offset: int = 0

140

category: Optional[str] = None

141

142

@app.get("/search")

143

def search(query: SearchQuery):

144

# Query parameters automatically validated and converted

145

return {

146

"query": query.q,

147

"limit": query.limit,

148

"offset": query.offset,

149

"category": query.category

150

}

151

```

152

153

### Header Parameter Validation

154

155

Validates HTTP headers against Pydantic models.

156

157

```python { .api }

158

def _validate_header(header: Type[BaseModel], func_kwargs: dict):

159

"""

160

Validate HTTP headers.

161

162

Args:

163

header: Pydantic model defining expected headers

164

func_kwargs: Function arguments to populate

165

166

Raises:

167

ValidationError: When headers don't match model

168

"""

169

```

170

171

**Usage Example:**

172

173

```python

174

from pydantic import BaseModel, Field

175

from typing import Optional

176

177

class AuthHeaders(BaseModel):

178

authorization: str = Field(alias="Authorization")

179

content_type: str = Field(alias="Content-Type", default="application/json")

180

user_agent: Optional[str] = Field(alias="User-Agent", default=None)

181

182

@app.post("/protected")

183

def protected_endpoint(header: AuthHeaders, body: dict):

184

# Headers automatically validated and available

185

auth_token = header.authorization

186

return {"authorized": True}

187

```

188

189

### Cookie Parameter Validation

190

191

Validates HTTP cookies against Pydantic models.

192

193

```python { .api }

194

def _validate_cookie(cookie: Type[BaseModel], func_kwargs: dict):

195

"""

196

Validate HTTP cookies.

197

198

Args:

199

cookie: Pydantic model defining expected cookies

200

func_kwargs: Function arguments to populate

201

202

Raises:

203

ValidationError: When cookies don't match model

204

"""

205

```

206

207

**Usage Example:**

208

209

```python

210

from pydantic import BaseModel

211

from typing import Optional

212

213

class SessionCookies(BaseModel):

214

session_id: str

215

preferences: Optional[str] = None

216

217

@app.get("/profile")

218

def get_profile(cookie: SessionCookies):

219

# Cookies automatically validated

220

return {"session": cookie.session_id}

221

```

222

223

### Form Data Validation

224

225

Validates form data (application/x-www-form-urlencoded or multipart/form-data) against Pydantic models.

226

227

```python { .api }

228

def _validate_form(form: Type[BaseModel], func_kwargs: dict):

229

"""

230

Validate form data from request.

231

232

Args:

233

form: Pydantic model defining expected form fields

234

func_kwargs: Function arguments to populate

235

236

Raises:

237

ValidationError: When form data doesn't match model

238

"""

239

```

240

241

**Usage Example:**

242

243

```python

244

from pydantic import BaseModel

245

from flask_openapi3 import FileStorage

246

247

class UserForm(BaseModel):

248

name: str

249

email: str

250

age: int

251

avatar: Optional[FileStorage] = None

252

253

@app.post("/users/form")

254

def create_user_form(form: UserForm):

255

# Form data including file uploads validated

256

return {

257

"name": form.name,

258

"email": form.email,

259

"age": form.age,

260

"has_avatar": form.avatar is not None

261

}

262

```

263

264

### Request Body Validation

265

266

Validates JSON request bodies against Pydantic models.

267

268

```python { .api }

269

def _validate_body(body: Type[BaseModel], func_kwargs: dict):

270

"""

271

Validate JSON request body.

272

273

Args:

274

body: Pydantic model defining expected body structure

275

func_kwargs: Function arguments to populate

276

277

Raises:

278

ValidationError: When body doesn't match model

279

"""

280

```

281

282

**Usage Example:**

283

284

```python

285

from pydantic import BaseModel, EmailStr, validator

286

from typing import Optional

287

288

class CreateUserBody(BaseModel):

289

name: str

290

email: EmailStr

291

password: str

292

age: Optional[int] = None

293

294

@validator('password')

295

def validate_password(cls, v):

296

if len(v) < 8:

297

raise ValueError('Password must be at least 8 characters')

298

return v

299

300

@app.post("/users")

301

def create_user(body: CreateUserBody):

302

# JSON body automatically validated with custom validators

303

return {"id": 1, "name": body.name, "email": body.email}

304

```

305

306

### Raw Data Validation

307

308

Validates raw request data for custom content types.

309

310

```python { .api }

311

class RawModel(Request):

312

"""Raw request data handling"""

313

mimetypes: list[str] = ["application/json"]

314

```

315

316

**Usage Example:**

317

318

```python

319

from flask_openapi3 import RawModel

320

321

class CSVRawModel(RawModel):

322

mimetypes = ["text/csv", "application/csv"]

323

324

@app.post("/upload-csv")

325

def upload_csv(raw: CSVRawModel):

326

# Raw CSV data accessible via raw.data

327

csv_content = raw.data.decode('utf-8')

328

return {"rows": len(csv_content.split('\n'))}

329

```

330

331

## Validation Error Handling

332

333

### Default Error Models

334

335

Built-in models for validation error responses.

336

337

```python { .api }

338

class ValidationErrorModel(BaseModel):

339

"""Default validation error response format"""

340

detail: list[dict[str, Any]]

341

342

class UnprocessableEntity(BaseModel):

343

"""422 error response format"""

344

detail: list[dict[str, Any]]

345

```

346

347

### Custom Validation Error Handling

348

349

You can customize validation error responses by providing custom error models and callbacks:

350

351

```python

352

from flask_openapi3 import OpenAPI, Info

353

from pydantic import BaseModel

354

355

class CustomErrorModel(BaseModel):

356

error: str

357

field_errors: dict[str, list[str]]

358

status_code: int

359

360

def custom_error_callback(e):

361

errors = {}

362

for error in e.errors():

363

field = ".".join(str(x) for x in error["loc"])

364

if field not in errors:

365

errors[field] = []

366

errors[field].append(error["msg"])

367

368

return {

369

"error": "Validation failed",

370

"field_errors": errors,

371

"status_code": 422

372

}

373

374

app = OpenAPI(

375

__name__,

376

info=Info(title="API", version="1.0.0"),

377

validation_error_model=CustomErrorModel,

378

validation_error_callback=custom_error_callback,

379

validation_error_status=422

380

)

381

```

382

383

## Advanced Validation Features

384

385

### Field Aliases

386

387

Use Pydantic field aliases to map between different parameter names:

388

389

```python

390

from pydantic import BaseModel, Field

391

392

class UserQuery(BaseModel):

393

user_id: int = Field(alias="userId")

394

full_name: str = Field(alias="fullName")

395

396

@app.get("/users")

397

def get_users(query: UserQuery):

398

# URL: /users?userId=123&fullName=John

399

return {"user_id": query.user_id, "name": query.full_name}

400

```

401

402

### Custom Validators

403

404

Use Pydantic validators for complex validation logic:

405

406

```python

407

from pydantic import BaseModel, validator

408

import re

409

410

class UserBody(BaseModel):

411

username: str

412

email: str

413

414

@validator('username')

415

def validate_username(cls, v):

416

if not re.match(r'^[a-zA-Z0-9_]+$', v):

417

raise ValueError('Username can only contain letters, numbers, and underscores')

418

return v

419

420

@validator('email')

421

def validate_email(cls, v):

422

if '@' not in v:

423

raise ValueError('Invalid email format')

424

return v.lower()

425

```

426

427

### Nested Models

428

429

Support for complex nested validation:

430

431

```python

432

from pydantic import BaseModel

433

from typing import List, Optional

434

435

class Address(BaseModel):

436

street: str

437

city: str

438

country: str

439

postal_code: str

440

441

class User(BaseModel):

442

name: str

443

email: str

444

addresses: List[Address]

445

primary_address: Optional[Address] = None

446

447

@app.post("/users")

448

def create_user(body: User):

449

# Nested validation automatically applied

450

return {"user_created": True, "address_count": len(body.addresses)}

451

```