or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

core-api.mderror-handling.mdindex.mdmarshalling-fields.mdmodels-validation.mdrequest-parsing.md

error-handling.mddocs/

0

# Error Handling and Utilities

1

2

Flask-RESTX provides comprehensive error handling capabilities with HTTP status code management, custom exception types, and utilities for API responses. It also includes CORS support and Swagger documentation generation for complete API development support.

3

4

## Capabilities

5

6

### Error Handling Functions

7

8

Core functions for handling API errors and exceptions.

9

10

```python { .api }

11

def abort(code=500, message=None, **kwargs):

12

"""

13

Abort the current request with HTTP status code and message.

14

15

Parameters:

16

- code: HTTP status code (default: 500)

17

- message: Optional error message

18

- kwargs: Additional data to include in error response

19

20

Raises:

21

HTTPException: Flask HTTP exception with specified status code

22

"""

23

```

24

25

### Exception Classes

26

27

Custom exception types for different error scenarios.

28

29

```python { .api }

30

class RestError(Exception):

31

def __init__(self, msg):

32

"""

33

Base class for all Flask-RESTX errors.

34

35

Parameters:

36

- msg: Error message

37

"""

38

39

def __str__(self):

40

"""Return error message as string."""

41

42

class ValidationError(RestError):

43

def __init__(self, msg):

44

"""

45

Exception for input validation errors.

46

47

Parameters:

48

- msg: Validation error details

49

"""

50

51

class SpecsError(RestError):

52

def __init__(self, msg):

53

"""

54

Exception for API specification errors.

55

56

Parameters:

57

- msg: Specification error details

58

"""

59

```

60

61

### CORS Support

62

63

Cross-Origin Resource Sharing functionality for API access control.

64

65

```python { .api }

66

def crossdomain(

67

origin=None,

68

methods=None,

69

headers=None,

70

expose_headers=None,

71

max_age=21600,

72

attach_to_all=True,

73

automatic_options=True,

74

credentials=False

75

):

76

"""

77

Decorator for enabling CORS on API endpoints.

78

79

Parameters:

80

- origin: Allowed origins ('*' for all, list for specific origins)

81

- methods: Allowed HTTP methods (list of strings)

82

- headers: Allowed request headers (list of strings)

83

- expose_headers: Headers to expose to client (list of strings)

84

- max_age: Preflight request cache duration in seconds

85

- attach_to_all: Whether to attach CORS headers to all responses

86

- automatic_options: Whether to automatically handle OPTIONS requests

87

- credentials: Whether to allow credentials in CORS requests

88

89

Returns:

90

Decorator function for CORS-enabled endpoints

91

"""

92

```

93

94

### Swagger Documentation

95

96

Automatic API documentation generation with OpenAPI/Swagger specification.

97

98

```python { .api }

99

class Swagger:

100

def __init__(self, api):

101

"""

102

Swagger documentation generator.

103

104

Parameters:

105

- api: Api instance to generate documentation for

106

"""

107

108

def as_dict(self):

109

"""

110

Generate Swagger specification as dictionary.

111

112

Returns:

113

dict: Complete Swagger/OpenAPI specification

114

"""

115

116

@property

117

def paths(self):

118

"""Dictionary of API paths and operations."""

119

120

@property

121

def definitions(self):

122

"""Dictionary of model definitions."""

123

124

@property

125

def tags(self):

126

"""List of API tags."""

127

```

128

129

### HTTP Status Constants

130

131

Comprehensive HTTP status code constants for consistent error handling.

132

133

```python { .api }

134

class HTTPStatus:

135

"""

136

HTTP status code constants with names, phrases, and descriptions.

137

Complete enumeration of all standard HTTP status codes.

138

"""

139

140

# Informational 1xx

141

CONTINUE = 100

142

SWITCHING_PROTOCOLS = 101

143

PROCESSING = 102

144

145

# Success 2xx

146

OK = 200

147

CREATED = 201

148

ACCEPTED = 202

149

NON_AUTHORITATIVE_INFORMATION = 203

150

NO_CONTENT = 204

151

RESET_CONTENT = 205

152

PARTIAL_CONTENT = 206

153

MULTI_STATUS = 207

154

ALREADY_REPORTED = 208

155

IM_USED = 226

156

157

# Redirection 3xx

158

MULTIPLE_CHOICES = 300

159

MOVED_PERMANENTLY = 301

160

FOUND = 302

161

SEE_OTHER = 303

162

NOT_MODIFIED = 304

163

USE_PROXY = 305

164

TEMPORARY_REDIRECT = 307

165

PERMANENT_REDIRECT = 308

166

167

# Client Error 4xx

168

BAD_REQUEST = 400

169

UNAUTHORIZED = 401

170

PAYMENT_REQUIRED = 402

171

FORBIDDEN = 403

172

NOT_FOUND = 404

173

METHOD_NOT_ALLOWED = 405

174

NOT_ACCEPTABLE = 406

175

PROXY_AUTHENTICATION_REQUIRED = 407

176

REQUEST_TIMEOUT = 408

177

CONFLICT = 409

178

GONE = 410

179

LENGTH_REQUIRED = 411

180

PRECONDITION_FAILED = 412

181

REQUEST_ENTITY_TOO_LARGE = 413

182

REQUEST_URI_TOO_LONG = 414

183

UNSUPPORTED_MEDIA_TYPE = 415

184

REQUESTED_RANGE_NOT_SATISFIABLE = 416

185

EXPECTATION_FAILED = 417

186

UNPROCESSABLE_ENTITY = 422

187

LOCKED = 423

188

FAILED_DEPENDENCY = 424

189

UPGRADE_REQUIRED = 426

190

PRECONDITION_REQUIRED = 428

191

TOO_MANY_REQUESTS = 429

192

REQUEST_HEADER_FIELDS_TOO_LARGE = 431

193

194

# Server Error 5xx

195

INTERNAL_SERVER_ERROR = 500

196

NOT_IMPLEMENTED = 501

197

BAD_GATEWAY = 502

198

SERVICE_UNAVAILABLE = 503

199

GATEWAY_TIMEOUT = 504

200

HTTP_VERSION_NOT_SUPPORTED = 505

201

VARIANT_ALSO_NEGOTIATES = 506

202

INSUFFICIENT_STORAGE = 507

203

LOOP_DETECTED = 508

204

NOT_EXTENDED = 510

205

NETWORK_AUTHENTICATION_REQUIRED = 511

206

207

# Utility functions for HTTP status handling

208

def is_informational(status_code):

209

"""Check if status code is informational (1xx)."""

210

211

def is_success(status_code):

212

"""Check if status code indicates success (2xx)."""

213

214

def is_redirect(status_code):

215

"""Check if status code indicates redirection (3xx)."""

216

217

def is_client_error(status_code):

218

"""Check if status code indicates client error (4xx)."""

219

220

def is_server_error(status_code):

221

"""Check if status code indicates server error (5xx)."""

222

```

223

224

### Response Utilities

225

226

Functions for creating and formatting API responses.

227

228

```python { .api }

229

def output_json(data, code, headers=None):

230

"""

231

Create JSON response with proper headers.

232

233

Parameters:

234

- data: Response data to serialize as JSON

235

- code: HTTP status code

236

- headers: Optional additional headers

237

238

Returns:

239

Flask Response object with JSON content

240

"""

241

242

def make_response(data, code=200, headers=None):

243

"""

244

Create Flask response from data.

245

246

Parameters:

247

- data: Response data

248

- code: HTTP status code

249

- headers: Optional response headers

250

251

Returns:

252

Flask Response object

253

"""

254

```

255

256

## Usage Examples

257

258

### Basic Error Handling

259

260

```python

261

from flask_restx import Resource, abort, HTTPStatus

262

263

class UserResource(Resource):

264

def get(self, user_id):

265

user = find_user(user_id)

266

if not user:

267

abort(HTTPStatus.NOT_FOUND, message=f'User {user_id} not found')

268

269

if not user.is_active:

270

abort(HTTPStatus.FORBIDDEN, message='User account is disabled')

271

272

return user

273

274

def delete(self, user_id):

275

if not is_admin():

276

abort(HTTPStatus.UNAUTHORIZED, message='Admin access required')

277

278

try:

279

delete_user(user_id)

280

return '', HTTPStatus.NO_CONTENT

281

except UserNotFound:

282

abort(HTTPStatus.NOT_FOUND, message='User not found')

283

except DatabaseError:

284

abort(HTTPStatus.INTERNAL_SERVER_ERROR, message='Database error occurred')

285

```

286

287

### Custom Error Responses

288

289

```python

290

from flask_restx import Api

291

292

api = Api()

293

294

@api.errorhandler(ValidationError)

295

def handle_validation_error(error):

296

"""Handle validation errors with custom response format."""

297

return {

298

'error': 'Validation failed',

299

'message': str(error),

300

'code': 'VALIDATION_ERROR'

301

}, HTTPStatus.BAD_REQUEST

302

303

@api.errorhandler(RestError)

304

def handle_rest_error(error):

305

"""Handle general REST API errors."""

306

return {

307

'error': 'API Error',

308

'message': str(error),

309

'code': 'REST_ERROR'

310

}, HTTPStatus.INTERNAL_SERVER_ERROR

311

312

@api.errorhandler(404)

313

def handle_not_found(error):

314

"""Handle 404 errors with custom format."""

315

return {

316

'error': 'Resource not found',

317

'message': 'The requested resource could not be found',

318

'code': 'NOT_FOUND'

319

}, HTTPStatus.NOT_FOUND

320

```

321

322

### Detailed Error Information

323

324

```python

325

class OrderResource(Resource):

326

def post(self):

327

try:

328

data = api.payload

329

order = create_order(data)

330

return order, HTTPStatus.CREATED

331

except InsufficientStock as e:

332

abort(

333

HTTPStatus.CONFLICT,

334

message='Insufficient stock',

335

details={

336

'requested_quantity': e.requested,

337

'available_quantity': e.available,

338

'product_id': e.product_id

339

},

340

code='INSUFFICIENT_STOCK'

341

)

342

except PaymentFailed as e:

343

abort(

344

HTTPStatus.PAYMENT_REQUIRED,

345

message='Payment processing failed',

346

details={

347

'payment_method': e.method,

348

'failure_reason': e.reason

349

},

350

code='PAYMENT_FAILED'

351

)

352

```

353

354

### CORS Configuration

355

356

```python

357

from flask_restx import cors

358

359

# Enable CORS for all origins

360

@api.route('/public-data')

361

class PublicData(Resource):

362

@cors.crossdomain(origin='*')

363

def get(self):

364

return {'data': 'public information'}

365

366

# Restrict CORS to specific origins

367

@api.route('/restricted-data')

368

class RestrictedData(Resource):

369

@cors.crossdomain(

370

origin=['https://example.com', 'https://app.example.com'],

371

methods=['GET', 'POST'],

372

headers=['Authorization', 'Content-Type']

373

)

374

def get(self):

375

return {'data': 'restricted information'}

376

377

@cors.crossdomain(

378

origin=['https://example.com'],

379

methods=['POST'],

380

headers=['Authorization', 'Content-Type'],

381

max_age=3600

382

)

383

def post(self):

384

return {'message': 'Data created'}, HTTPStatus.CREATED

385

```

386

387

### API-wide CORS Configuration

388

389

```python

390

from flask import Flask

391

from flask_restx import Api

392

from flask_cors import CORS

393

394

app = Flask(__name__)

395

396

# Enable CORS for entire Flask app

397

CORS(app, resources={

398

r"/api/*": {

399

"origins": ["https://example.com", "https://app.example.com"],

400

"methods": ["GET", "POST", "PUT", "DELETE"],

401

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

402

}

403

})

404

405

api = Api(app, prefix='/api/v1')

406

```

407

408

### Global Error Handlers

409

410

```python

411

from werkzeug.exceptions import HTTPException

412

import logging

413

414

api = Api()

415

416

@api.errorhandler(Exception)

417

def handle_unexpected_error(error):

418

"""Handle all unexpected errors."""

419

logging.error(f"Unexpected error: {str(error)}", exc_info=True)

420

return {

421

'error': 'Internal server error',

422

'message': 'An unexpected error occurred'

423

}, HTTPStatus.INTERNAL_SERVER_ERROR

424

425

@api.errorhandler(HTTPException)

426

def handle_http_error(error):

427

"""Handle HTTP errors with consistent format."""

428

return {

429

'error': error.name,

430

'message': error.description,

431

'code': error.code

432

}, error.code

433

```

434

435

### Validation Error Details

436

437

```python

438

from jsonschema import ValidationError as JSONValidationError

439

440

@api.errorhandler(JSONValidationError)

441

def handle_json_validation_error(error):

442

"""Handle JSON schema validation errors with detailed information."""

443

return {

444

'error': 'Validation failed',

445

'message': error.message,

446

'path': list(error.absolute_path),

447

'invalid_value': error.instance,

448

'schema_path': list(error.schema_path)

449

}, HTTPStatus.BAD_REQUEST

450

451

class ValidationResource(Resource):

452

@api.expect(user_model, validate=True)

453

def post(self):

454

# Validation automatically handled by Flask-RESTX

455

# Custom validation errors can still be raised

456

data = api.payload

457

458

if User.query.filter_by(email=data['email']).first():

459

abort(

460

HTTPStatus.CONFLICT,

461

message='Email already exists',

462

field='email',

463

code='DUPLICATE_EMAIL'

464

)

465

466

return create_user(data), HTTPStatus.CREATED

467

```

468

469

### Swagger Documentation Customization

470

471

```python

472

# Custom Swagger configuration

473

api = Api(

474

app,

475

version='1.0',

476

title='My API',

477

description='Comprehensive REST API with detailed documentation',

478

terms_url='https://example.com/terms',

479

contact='support@example.com',

480

license='MIT',

481

license_url='https://opensource.org/licenses/MIT',

482

authorizations={

483

'Bearer': {

484

'type': 'apiKey',

485

'in': 'header',

486

'name': 'Authorization',

487

'description': 'JWT token in format: Bearer <token>'

488

},

489

'ApiKey': {

490

'type': 'apiKey',

491

'in': 'header',

492

'name': 'X-API-Key',

493

'description': 'API key for authentication'

494

}

495

}

496

)

497

498

# Access Swagger specification

499

@api.route('/swagger-spec')

500

class SwaggerSpec(Resource):

501

def get(self):

502

"""Return the complete Swagger specification."""

503

swagger = Swagger(api)

504

return swagger.as_dict()

505

```

506

507

### Rate Limiting Error Handling

508

509

```python

510

from flask_limiter import Limiter

511

from flask_limiter.util import get_remote_address

512

513

limiter = Limiter(

514

app,

515

key_func=get_remote_address,

516

default_limits=["100 per hour"]

517

)

518

519

@api.errorhandler(429) # Too Many Requests

520

def handle_rate_limit_error(error):

521

"""Handle rate limiting errors."""

522

return {

523

'error': 'Rate limit exceeded',

524

'message': 'Too many requests. Please try again later.',

525

'retry_after': error.retry_after,

526

'code': 'RATE_LIMIT_EXCEEDED'

527

}, HTTPStatus.TOO_MANY_REQUESTS

528

529

class RateLimitedResource(Resource):

530

@limiter.limit("10 per minute")

531

def post(self):

532

return {'message': 'Request processed'}, HTTPStatus.OK

533

```