or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

application.mdconfig.mdengine.mdindex.mdrequest-response.mdrouting.mdutilities.md

request-response.mddocs/

0

# Request and Response Handling

1

2

Comprehensive request parsing and response building capabilities with content transformation pipelines. Provides full access to HTTP request data and flexible response generation with content negotiation support.

3

4

## Capabilities

5

6

### Application Request Interface

7

8

Core interface for accessing incoming HTTP request data and metadata.

9

10

```kotlin { .api }

11

/**

12

* Represents client request with all HTTP data and metadata

13

*/

14

interface ApplicationRequest {

15

/** The call this request belongs to */

16

val call: ApplicationCall

17

18

/** Pipeline for processing request content */

19

val pipeline: ApplicationReceivePipeline

20

21

/** Query string parameters */

22

val queryParameters: Parameters

23

24

/** HTTP request headers */

25

val headers: Headers

26

27

/** Local connection information */

28

val local: RequestConnectionPoint

29

30

/** Request cookies accessor */

31

val cookies: RequestCookies

32

}

33

34

/**

35

* Pipeline-specific request implementation

36

*/

37

interface PipelineRequest : ApplicationRequest

38

```

39

40

### Request Connection Information

41

42

Interfaces for accessing connection and HTTP protocol information.

43

44

```kotlin { .api }

45

/**

46

* Connection information for incoming request

47

*/

48

interface RequestConnectionPoint {

49

/** URI scheme (http, https) */

50

val scheme: String

51

52

/** HTTP protocol version */

53

val version: String

54

55

/** Local port number */

56

val port: Int

57

58

/** Host name from Host header */

59

val host: String

60

61

/** Full request URI */

62

val uri: String

63

64

/** HTTP method */

65

val method: HttpMethod

66

67

/** Remote client host address */

68

val remoteHost: String

69

70

/** User-Agent header value */

71

val userAgent: String?

72

}

73

```

74

75

### Request Cookies

76

77

Interface for accessing HTTP cookies from the request.

78

79

```kotlin { .api }

80

/**

81

* Request cookies accessor

82

*/

83

interface RequestCookies {

84

/** Get cookie value by name */

85

operator fun get(name: String): String?

86

87

/** Get all cookies as a map */

88

fun getAll(): Map<String, String>

89

}

90

```

91

92

### Request Content Pipeline

93

94

Pipeline system for processing and transforming incoming request content.

95

96

```kotlin { .api }

97

/**

98

* Pipeline for processing incoming request content

99

*/

100

open class ApplicationReceivePipeline : Pipeline<Any, PipelineCall> {

101

companion object {

102

/** Phase for pre-processing before transformation */

103

val Before: PipelinePhase

104

105

/** Phase for content transformation */

106

val Transform: PipelinePhase

107

108

/** Phase for post-processing after transformation */

109

val After: PipelinePhase

110

}

111

}

112

```

113

114

### Request Extension Properties

115

116

Extension properties providing convenient access to common request data.

117

118

```kotlin { .api }

119

/** Full request URI */

120

val ApplicationRequest.uri: String

121

122

/** HTTP protocol version */

123

val ApplicationRequest.httpVersion: String

124

125

/** HTTP method */

126

val ApplicationRequest.httpMethod: HttpMethod

127

128

/** Request path without query parameters */

129

val ApplicationRequest.path: String

130

131

/** Document name from path */

132

val ApplicationRequest.document: String

133

134

/** Host header value */

135

val ApplicationRequest.host: String

136

137

/** Port number */

138

val ApplicationRequest.port: Int

139

140

/** User-Agent header */

141

val ApplicationRequest.userAgent: String?

142

143

/** Accept header */

144

val ApplicationRequest.accept: String?

145

146

/** Accept-Language header */

147

val ApplicationRequest.acceptLanguage: String?

148

149

/** Accept-Encoding header */

150

val ApplicationRequest.acceptEncoding: String?

151

152

/** Content-Type header parsed */

153

val ApplicationRequest.contentType: ContentType

154

155

/** Content-Length header */

156

val ApplicationRequest.contentLength: Long?

157

158

/** Authorization header */

159

val ApplicationRequest.authorization: String?

160

161

/** Cache-Control header */

162

val ApplicationRequest.cacheControl: String?

163

164

/** Whether request is multipart */

165

val ApplicationRequest.isMultipart: Boolean

166

167

/** Whether request is URL encoded */

168

val ApplicationRequest.isUrlEncoded: Boolean

169

170

/** Origin connection point */

171

val ApplicationRequest.origin: RequestConnectionPoint

172

```

173

174

### Request Receive Functions

175

176

Extension functions for receiving and parsing request content with type safety.

177

178

```kotlin { .api }

179

/**

180

* Receive request body converted to specified type

181

*/

182

suspend inline fun <reified T> ApplicationCall.receive(): T

183

184

/**

185

* Receive request body converted to specified type or null if conversion fails

186

*/

187

suspend inline fun <reified T> ApplicationCall.receiveOrNull(): T?

188

189

/**

190

* Receive request body with explicit type information

191

*/

192

suspend fun <T> ApplicationCall.receiveNullable(typeInfo: TypeInfo): T?

193

194

/**

195

* Receive request body as plain text

196

*/

197

suspend fun ApplicationCall.receiveText(): String

198

199

/**

200

* Receive form parameters from URL-encoded or multipart request

201

*/

202

suspend fun ApplicationCall.receiveParameters(): Parameters

203

204

/**

205

* Receive multipart data for file uploads and complex forms

206

*/

207

suspend fun ApplicationCall.receiveMultipart(): MultiPartData

208

209

/**

210

* Receive request body as byte array

211

*/

212

suspend fun ApplicationCall.receiveBytes(): ByteArray

213

214

/**

215

* Receive request body as input stream

216

*/

217

suspend fun ApplicationCall.receiveStream(): InputStream

218

```

219

220

### Application Response Interface

221

222

Core interface for building and sending HTTP responses.

223

224

```kotlin { .api }

225

/**

226

* Represents server response being built and sent to client

227

*/

228

interface ApplicationResponse {

229

/** The call this response belongs to */

230

val call: ApplicationCall

231

232

/** Pipeline for processing response content */

233

val pipeline: ApplicationSendPipeline

234

235

/** Response headers accessor */

236

val headers: ResponseHeaders

237

238

/** Response cookies accessor */

239

val cookies: ResponseCookies

240

241

/** Get current response status code */

242

fun status(): HttpStatusCode?

243

244

/** Set response status code */

245

fun status(value: HttpStatusCode)

246

}

247

248

/**

249

* Pipeline-specific response implementation

250

*/

251

interface PipelineResponse : ApplicationResponse

252

```

253

254

### Response Headers Management

255

256

Interface for managing HTTP response headers.

257

258

```kotlin { .api }

259

/**

260

* Response headers accessor with modification capabilities

261

*/

262

interface ResponseHeaders {

263

/** Get header value by name */

264

operator fun get(name: String): String?

265

266

/** Get all values for header name */

267

fun getAll(name: String): List<String>?

268

269

/** Check if header exists */

270

fun contains(name: String): Boolean

271

272

/** Add header value (allows multiple values) */

273

fun append(name: String, value: String)

274

275

/** Add header value only if name doesn't exist */

276

fun appendIfNameAbsent(name: String, value: String): Boolean

277

}

278

```

279

280

### Response Cookies Management

281

282

Interface for managing HTTP response cookies.

283

284

```kotlin { .api }

285

/**

286

* Response cookies accessor for setting cookies

287

*/

288

interface ResponseCookies {

289

/** Get cookie configuration by name */

290

operator fun get(name: String): Cookie?

291

292

/** Add cookie to response */

293

fun append(cookie: Cookie)

294

295

/** Add expired cookie to delete client-side cookie */

296

fun appendExpired(name: String, domain: String? = null, path: String? = null)

297

}

298

```

299

300

### Response Content Pipeline

301

302

Pipeline system for processing and transforming outgoing response content.

303

304

```kotlin { .api }

305

/**

306

* Pipeline for processing outgoing response content

307

*/

308

open class ApplicationSendPipeline : Pipeline<Any, PipelineCall> {

309

companion object {

310

/** Phase for pre-processing before transformation */

311

val Before: PipelinePhase

312

313

/** Phase for content transformation */

314

val Transform: PipelinePhase

315

316

/** Phase for post-processing after transformation */

317

val After: PipelinePhase

318

319

/** Phase for engine-specific processing */

320

val Engine: PipelinePhase

321

}

322

}

323

```

324

325

### Request Extension Properties

326

327

Extension properties for convenient request data access.

328

329

```kotlin { .api }

330

/** Get request URI */

331

val ApplicationRequest.uri: String

332

333

/** Get HTTP method */

334

val ApplicationRequest.httpMethod: HttpMethod

335

336

/** Get request path */

337

val ApplicationRequest.path: String

338

339

/** Get request host */

340

val ApplicationRequest.host: String

341

342

/** Get request port */

343

val ApplicationRequest.port: Int

344

345

/** Get Content-Type header */

346

val ApplicationRequest.contentType: ContentType

347

348

/** Get User-Agent header */

349

val ApplicationRequest.userAgent: String?

350

351

/** Get request origin information */

352

val ApplicationRequest.origin: RequestConnectionPoint

353

```

354

355

### Response Extension Properties

356

357

Extension properties for convenient response header management.

358

359

```kotlin { .api }

360

/** Get or set Content-Type header */

361

var ApplicationResponse.contentType: ContentType?

362

363

/** Get or set Content-Length header */

364

var ApplicationResponse.contentLength: Long?

365

366

/** Get or set Cache-Control header */

367

var ApplicationResponse.cacheControl: CacheControl?

368

369

/** Get or set ETag header */

370

var ApplicationResponse.etag: String?

371

372

/** Get or set Last-Modified header */

373

var ApplicationResponse.lastModified: ZonedDateTime?

374

```

375

376

### Response Send Functions

377

378

Extension functions for sending various types of response content.

379

380

```kotlin { .api }

381

/**

382

* Send response content with automatic type handling

383

*/

384

suspend fun ApplicationCall.respond(message: Any)

385

386

/**

387

* Send response with specific status code

388

*/

389

suspend fun ApplicationCall.respond(status: HttpStatusCode, message: Any)

390

391

/**

392

* Send plain text response

393

*/

394

suspend fun ApplicationCall.respondText(

395

text: String,

396

contentType: ContentType? = null,

397

status: HttpStatusCode? = null

398

)

399

400

/**

401

* Send byte array response

402

*/

403

suspend fun ApplicationCall.respondBytes(

404

bytes: ByteArray,

405

contentType: ContentType? = null,

406

status: HttpStatusCode? = null

407

)

408

409

/**

410

* Send file response with optional content type detection

411

*/

412

suspend fun ApplicationCall.respondFile(

413

file: File,

414

contentType: ContentType? = null

415

)

416

417

/**

418

* Send redirect response

419

*/

420

suspend fun ApplicationCall.respondRedirect(

421

url: String,

422

permanent: Boolean = false

423

)

424

425

/**

426

* Send redirect response with status code

427

*/

428

suspend fun ApplicationCall.respondRedirect(

429

url: String,

430

status: HttpStatusCode

431

)

432

433

/**

434

* Send streaming response from input stream

435

*/

436

suspend fun ApplicationCall.respondOutputStream(

437

contentType: ContentType? = null,

438

status: HttpStatusCode? = null,

439

producer: suspend OutputStream.() -> Unit

440

)

441

442

/**

443

* Send streaming response from writer

444

*/

445

suspend fun ApplicationCall.respondTextWriter(

446

contentType: ContentType? = null,

447

status: HttpStatusCode? = null,

448

producer: suspend Writer.() -> Unit

449

)

450

```

451

452

## Usage Examples

453

454

### Request Data Access

455

456

```kotlin

457

import io.ktor.server.application.*

458

import io.ktor.server.request.*

459

import io.ktor.server.response.*

460

import io.ktor.server.routing.*

461

462

fun Application.requestHandling() {

463

routing {

464

get("/request-info") {

465

val request = call.request

466

467

val info = mapOf(

468

"method" to request.httpMethod.value,

469

"uri" to request.uri,

470

"path" to request.path,

471

"host" to request.host,

472

"port" to request.port,

473

"userAgent" to request.userAgent,

474

"contentType" to request.contentType.toString(),

475

"accept" to request.accept,

476

"headers" to request.headers.toMap(),

477

"queryParams" to request.queryParameters.toMap(),

478

"cookies" to request.cookies.getAll()

479

)

480

481

call.respond(info)

482

}

483

484

get("/users/{id}") {

485

val userId = call.parameters["id"]

486

val includeDetails = call.request.queryParameters["details"]?.toBoolean() ?: false

487

488

val user = getUserById(userId)

489

if (includeDetails) {

490

call.respond(user.withDetails())

491

} else {

492

call.respond(user.summary())

493

}

494

}

495

}

496

}

497

```

498

499

### Request Content Handling

500

501

```kotlin

502

import io.ktor.server.application.*

503

import io.ktor.server.request.*

504

import io.ktor.server.response.*

505

import io.ktor.server.routing.*

506

507

fun Application.contentHandling() {

508

routing {

509

// Receive JSON data

510

post("/users") {

511

try {

512

val user = call.receive<User>()

513

val savedUser = userService.createUser(user)

514

call.respond(HttpStatusCode.Created, savedUser)

515

} catch (e: ContentTransformationException) {

516

call.respond(HttpStatusCode.BadRequest, "Invalid user data")

517

}

518

}

519

520

// Receive form data

521

post("/form") {

522

val formParameters = call.receiveParameters()

523

val name = formParameters["name"] ?: return@post call.respond(HttpStatusCode.BadRequest)

524

val email = formParameters["email"] ?: return@post call.respond(HttpStatusCode.BadRequest)

525

526

call.respond("Hello $name, your email is $email")

527

}

528

529

// Receive multipart data (file upload)

530

post("/upload") {

531

val multipart = call.receiveMultipart()

532

var fileName: String? = null

533

var fileBytes: ByteArray? = null

534

535

multipart.forEachPart { part ->

536

when (part) {

537

is PartData.FormItem -> {

538

if (part.name == "description") {

539

// Handle form field

540

}

541

}

542

is PartData.FileItem -> {

543

fileName = part.originalFileName

544

fileBytes = part.streamProvider().readBytes()

545

}

546

is PartData.BinaryItem -> {

547

// Handle binary data

548

}

549

}

550

part.dispose()

551

}

552

553

if (fileName != null && fileBytes != null) {

554

// Save file

555

call.respond("File $fileName uploaded successfully")

556

} else {

557

call.respond(HttpStatusCode.BadRequest, "No file provided")

558

}

559

}

560

561

// Receive raw text

562

put("/data/{id}") {

563

val id = call.parameters["id"]

564

val textData = call.receiveText()

565

566

dataService.updateData(id, textData)

567

call.respond(HttpStatusCode.OK)

568

}

569

}

570

}

571

```

572

573

### Response Building

574

575

```kotlin

576

import io.ktor.server.application.*

577

import io.ktor.server.response.*

578

import io.ktor.server.routing.*

579

580

fun Application.responseHandling() {

581

routing {

582

// JSON response

583

get("/api/users/{id}") {

584

val userId = call.parameters["id"]

585

val user = userService.findById(userId)

586

587

if (user != null) {

588

call.respond(user)

589

} else {

590

call.respond(HttpStatusCode.NotFound, "User not found")

591

}

592

}

593

594

// Custom headers and status

595

get("/api/data") {

596

val data = dataService.getData()

597

598

call.response.headers.append("X-Data-Version", "1.0")

599

call.response.headers.append("X-Total-Count", data.size.toString())

600

call.response.cacheControl = CacheControl.MaxAge(maxAgeSeconds = 300)

601

602

call.respond(HttpStatusCode.OK, data)

603

}

604

605

// File response

606

get("/files/{filename}") {

607

val filename = call.parameters["filename"]

608

val file = File("uploads/$filename")

609

610

if (file.exists() && file.isFile) {

611

call.respondFile(file)

612

} else {

613

call.respond(HttpStatusCode.NotFound)

614

}

615

}

616

617

// Streaming response

618

get("/large-data") {

619

call.respondTextWriter(ContentType.Text.Plain) {

620

for (i in 1..1000000) {

621

write("Line $i\n")

622

if (i % 1000 == 0) {

623

flush() // Flush periodically for streaming

624

}

625

}

626

}

627

}

628

629

// Binary response

630

get("/image/{id}") {

631

val imageId = call.parameters["id"]

632

val imageData = imageService.getImageBytes(imageId)

633

634

if (imageData != null) {

635

call.respondBytes(

636

bytes = imageData,

637

contentType = ContentType.Image.PNG

638

)

639

} else {

640

call.respond(HttpStatusCode.NotFound)

641

}

642

}

643

644

// Redirect response

645

get("/old-api/{path...}") {

646

val path = call.parameters.getAll("path")?.joinToString("/") ?: ""

647

call.respondRedirect("/api/v2/$path", permanent = true)

648

}

649

}

650

}

651

```

652

653

### Cookie Management

654

655

```kotlin

656

import io.ktor.server.application.*

657

import io.ktor.server.request.*

658

import io.ktor.server.response.*

659

import io.ktor.server.routing.*

660

661

fun Application.cookieHandling() {

662

routing {

663

get("/set-cookie") {

664

// Set simple cookie

665

call.response.cookies.append("session_id", "abc123")

666

667

// Set cookie with options

668

call.response.cookies.append(Cookie(

669

name = "user_pref",

670

value = "dark_mode",

671

maxAge = 86400, // 24 hours

672

domain = ".example.com",

673

path = "/",

674

secure = true,

675

httpOnly = true,

676

extensions = mapOf("SameSite" to "Strict")

677

))

678

679

call.respond("Cookies set")

680

}

681

682

get("/get-cookies") {

683

val sessionId = call.request.cookies["session_id"]

684

val userPref = call.request.cookies["user_pref"]

685

val allCookies = call.request.cookies.getAll()

686

687

call.respond(mapOf(

688

"sessionId" to sessionId,

689

"userPref" to userPref,

690

"allCookies" to allCookies

691

))

692

}

693

694

post("/logout") {

695

// Delete cookies by setting expired ones

696

call.response.cookies.appendExpired("session_id")

697

call.response.cookies.appendExpired("user_pref", domain = ".example.com", path = "/")

698

699

call.respond("Logged out")

700

}

701

}

702

}

703

```

704

705

### Content Negotiation and Type Conversion

706

707

```kotlin

708

import io.ktor.server.application.*

709

import io.ktor.server.request.*

710

import io.ktor.server.response.*

711

import io.ktor.server.routing.*

712

713

data class User(val id: Int, val name: String, val email: String)

714

data class CreateUserRequest(val name: String, val email: String)

715

716

fun Application.contentNegotiation() {

717

routing {

718

// Accept both JSON and XML

719

post("/users") {

720

val contentType = call.request.contentType

721

722

when {

723

contentType.match(ContentType.Application.Json) -> {

724

val request = call.receive<CreateUserRequest>()

725

val user = userService.create(request)

726

call.respond(user)

727

}

728

contentType.match(ContentType.Application.Xml) -> {

729

val xmlData = call.receiveText()

730

val user = parseUserFromXml(xmlData)

731

call.respond(user)

732

}

733

else -> {

734

call.respond(HttpStatusCode.UnsupportedMediaType)

735

}

736

}

737

}

738

739

// Content negotiation for response format

740

get("/users/{id}") {

741

val userId = call.parameters["id"]?.toInt()

742

val user = userService.findById(userId)

743

744

if (user == null) {

745

call.respond(HttpStatusCode.NotFound)

746

return@get

747

}

748

749

val acceptHeader = call.request.accept

750

when {

751

acceptHeader?.contains("application/json") == true -> {

752

call.respond(user)

753

}

754

acceptHeader?.contains("text/xml") == true -> {

755

call.respondText(

756

user.toXml(),

757

ContentType.Text.Xml

758

)

759

}

760

acceptHeader?.contains("text/plain") == true -> {

761

call.respondText("User: ${user.name} (${user.email})")

762

}

763

else -> {

764

call.respond(user) // Default to JSON

765

}

766

}

767

}

768

}

769

}

770

```

771

772

### Exception Classes

773

774

Exception types related to request/response processing and content transformation.

775

776

```kotlin { .api }

777

/**

778

* Base exception for bad request scenarios

779

*/

780

open class BadRequestException(message: String, cause: Throwable? = null) : Exception(message, cause)

781

782

/**

783

* Thrown when a required request parameter is missing

784

*/

785

class MissingRequestParameterException(val parameterName: String) : BadRequestException(

786

"Request parameter '$parameterName' is missing"

787

)

788

789

/**

790

* Thrown when parameter conversion fails

791

*/

792

class ParameterConversionException(

793

val parameterName: String,

794

val type: String,

795

cause: Throwable? = null

796

) : BadRequestException("Cannot convert parameter '$parameterName' to $type", cause)

797

798

/**

799

* Thrown when a resource is not found

800

*/

801

class NotFoundException(message: String? = "Resource not found") : Exception(message)

802

803

/**

804

* Base exception for content transformation failures

805

*/

806

abstract class ContentTransformationException(message: String) : IOException(message)

807

808

/**

809

* Thrown when content cannot be transformed to the requested type

810

*/

811

class CannotTransformContentToTypeException(private val type: KType) : ContentTransformationException(

812

"Cannot transform content to $type"

813

)

814

815

/**

816

* Thrown when the request content type is not supported

817

*/

818

class UnsupportedMediaTypeException(private val contentType: ContentType?) : ContentTransformationException(

819

"Content type $contentType is not supported"

820

)

821

822

/**

823

* Thrown when request payload exceeds size limits

824

*/

825

class PayloadTooLargeException(private val sizeLimit: Long) : ContentTransformationException(

826

"Request payload size exceeds limit of $sizeLimit bytes"

827

)

828

```