{"title":"Request & Response Objects","description":"Read HTTP requests and construct responses in Amber V2 controllers","section":"guides/controllers","version":"v2","path":"guides/controllers/request-and-response-objects","canonical_url":"https://amberframework.org/docs/v2/guides/controllers/request-and-response-objects","markdown_url":"https://amberframework.org/docs/v2/guides/controllers/request-and-response-objects.md","inherited":false,"content_markdown":"# Request & Response Objects\n\nEvery Amber controller delegates `request` and `response` to the current\n`HTTP::Server::Context`. Use Amber's controller helpers for ordinary rendering,\nredirects, and negotiated responses; reach for the underlying Crystal objects\nwhen you need a header, method, resource, or status directly.\n\n## Request\n\n`request` is Crystal's `HTTP::Request` with Amber routing extensions.\n\n**File: `src/controllers/diagnostics_controller.cr` — place this action inside\n`DiagnosticsController`, then register it in `config/routes.cr`.**\n\n```crystal\nclass DiagnosticsController < ApplicationController\n  def show\n    method = request.method\n    resource = request.resource\n    user_agent = request.headers[\"User-Agent\"]?\n    query = request.query\n\n    respond_with do\n      json({method: method, resource: resource, user_agent: user_agent, query: query}.to_json)\n    end\n  end\nend\n```\n\nCommon controller-level helpers include:\n\n| Helper | Result |\n|---|---|\n| `get?`, `post?`, `put?`, `patch?`, `delete?`, `head?` | Whether the request uses that HTTP method |\n| `params` | Amber route, query, and form parameters |\n| `format` | The requested response format inferred from the path or headers |\n| `port` | The request port |\n| `requested_url` | The parsed request URL |\n| `cookies` | Amber's cookie store |\n| `session`, `flash` | The current session and flash stores |\n\nThe raw request body is an `IO`. A parser or [request\nschema](../schema-api/index.md) is usually a better boundary for JSON or form\ninput than manually reading the stream in each action.\n\n## Response\n\n`response` is Crystal's `HTTP::Server::Response`. Its most useful direct\nproperties are `status_code`, `headers`, and `content_type`.\n\n**File: `src/controllers/health_controller.cr` — place this action inside\n`HealthController`, then register it in `config/routes.cr`.**\n\n```crystal\nclass HealthController < ApplicationController\n  def show\n    response.headers[\"Cache-Control\"] = \"no-store\"\n    set_response(\n      body: \"ok\",\n      status_code: 200,\n      content_type: \"text/plain\"\n    )\n  end\nend\n```\n\n**File: a controller action under `src/controllers/` — use `respond_with` when\nthat action offers these representations.**\n\n```crystal\nrespond_with do\n  html render(\"show.ecr\")\n  json({status: \"ok\"}.to_json)\n  text \"ok\"\nend\n```\n\n**File: a controller filter or action under `src/controllers/` — use `halt!`\nwhen the pipeline must stop with a plain response.**\n\n```crystal\nhalt!(403, \"forbidden\") unless authorized?\n```\n\n**File: a controller action under `src/controllers/` — use the redirect helper\nrather than setting a `Location` header by hand.**\n\n```crystal\nredirect_to location: \"/login\", status: 302\n```\n\nFor the upstream object APIs, see Crystal's `HTTP::Request` and\n`HTTP::Server::Response` reference. Amber-specific helpers and schema\nintegration should remain the first choice when they express the intent."}