> ## Documentation Index
> Fetch the complete documentation index at: https://docs.lightning-go.cn/llms.txt
> Use this file to discover all available pages before exploring further.

# Responses

> Send JSON, text, HTML, XML, files, and redirects. Use structured helpers for consistent API responses.

Every response method lives on `*Context` and writes to the current request's response buffer. Call exactly one response method per handler — subsequent calls overwrite the previous ones.

## JSON

`ctx.JSON(code, obj)` serializes any value to JSON and sets `Content-Type: application/json; charset=utf-8`.

```go theme={null}
// From a struct
app.Get("/user", func(ctx *lightning.Context) {
    type User struct {
        Name string `json:"name"`
        Age  int    `json:"age"`
    }
    ctx.JSON(lightning.StatusOK, &User{Name: "Alice", Age: 30})
})

// From lightning.Map (shorthand for map[string]any)
app.Get("/ping", func(ctx *lightning.Context) {
    ctx.JSON(lightning.StatusOK, lightning.Map{
        "message": "pong",
    })
})
```

## Text

`ctx.Text(code, text)` sends a plain text response with `Content-Type: text/plain`.

```go theme={null}
app.Get("/health", func(ctx *lightning.Context) {
    ctx.Text(lightning.StatusOK, "ok")
})
```

## XML

`ctx.XML(code, obj)` marshals a struct to XML and sets `Content-Type: application/xml`.

```go theme={null}
type Product struct {
    ID    int    `xml:"id"`
    Name  string `xml:"name"`
    Price int    `xml:"price"`
}

app.Get("/product", func(ctx *lightning.Context) {
    ctx.XML(lightning.StatusOK, &Product{ID: 1, Name: "Widget", Price: 499})
})
```

## Redirects

`ctx.Redirect(code, url)` issues an HTTP redirect. Use `301` for permanent redirects and `302` for temporary ones.

```go theme={null}
// Permanent redirect
app.Get("/old-path", func(ctx *lightning.Context) {
    ctx.Redirect(lightning.StatusMovedPermanently, "/new-path")
})

// Temporary redirect
app.Get("/promo", func(ctx *lightning.Context) {
    ctx.Redirect(lightning.StatusFound, "/sale")
})
```

You can also use the named constants `lightning.StatusMovedPermanently` (301) and `lightning.StatusFound` (302).

## File

`ctx.File(filepath)` serves a file from the local filesystem. It returns an error if the file cannot be read.

```go theme={null}
app.Get("/download", func(ctx *lightning.Context) {
    if err := ctx.File("./files/report.pdf"); err != nil {
        ctx.JSONError(lightning.StatusNotFound, "file not found")
    }
})
```

## Structured API Helpers

These helpers enforce a consistent JSON envelope for application-level responses. They are most useful in API handlers where all responses — success and failure alike — should share the same top-level shape.

### `ctx.Success(data)`

Sends HTTP 200 with:

```json theme={null}
{"code": 0, "message": "ok", "data": <data>}
```

```go theme={null}
app.Get("/items", func(ctx *lightning.Context) {
    items := []string{"apple", "banana", "cherry"}
    ctx.Success(items)
})
```

### `ctx.Fail(code, message)`

Sends HTTP 200 with an application-level error code and message:

```json theme={null}
{"code": <code>, "message": "<message>"}
```

```go theme={null}
app.Post("/purchase", func(ctx *lightning.Context) {
    ctx.Fail(9999, "network error")
})
```

<Note>
  `ctx.Fail` always returns HTTP 200. The `code` field is an application-level error code, not an HTTP status code. Use this pattern when your API clients always expect 200 and inspect the body for errors.
</Note>

### `ctx.JSONError(code, message)`

Sends the given HTTP status code with a JSON body matching that code:

```json theme={null}
{"code": <code>, "message": "<message>"}
```

```go theme={null}
app.Get("/admin", func(ctx *lightning.Context) {
    ctx.JSONError(lightning.StatusForbidden, "access denied")
    // HTTP 403, body: {"code": 403, "message": "access denied"}
})
```

Use `ctx.JSONError` when you want the HTTP status code and the body code to match — for example, for standard REST error responses.

## Custom Response Headers

### Set, add, and delete headers

```go theme={null}
app.Get("/data", func(ctx *lightning.Context) {
    // Replace (or create) a header
    ctx.SetHeader("X-Request-Id", "abc-123")

    // Append a value to a header (allows multiple values for the same key)
    ctx.AddHeader("X-Tag", "first")
    ctx.AddHeader("X-Tag", "second")

    // Remove a header entirely
    ctx.DelHeader("X-Powered-By")

    ctx.JSON(lightning.StatusOK, lightning.Map{"status": "ok"})
})
```

## Cookies

Set a response cookie with `ctx.SetCookie(key, value)`:

```go theme={null}
app.Post("/login", func(ctx *lightning.Context) {
    ctx.SetCookie("session_id", "tok-abc123xyz")
    ctx.JSON(lightning.StatusOK, lightning.Map{"message": "logged in"})
})
```
