# Welcome

Package macaron is a high productive and modular web framework in Go. It takes basic ideology of [Martini](https://github.com/go-martini/martini) and extends in advance.

{% hint style="info" %}
The minimum requirement of Go is **1.6**.
{% endhint %}

## Quick Start

To install Macaron:

```
go get gopkg.in/macaron.v1
```

The very basic usage of Macaron:

```go
package main

import "gopkg.in/macaron.v1"

func main() {
    m := macaron.Classic()
    m.Get("/", func() string {
        return "Hello world!"
    })
    m.Run()
}
```

## Features

* Powerful routing with suburl.
* Flexible routes combinations.
* Unlimited nested group routers.
* Directly integrate with existing services.
* Dynamically change template files at runtime.
* Allow to use in-memory template and static files.
* Easy to plugin/unplugin features with modular design.
* Handy dependency injection powered by [inject](https://github.com/codegangsta/inject).
* Better router layer and less reflection make faster speed.

## Use Cases

* [Gogs](https://gogs.io): A painless self-hosted Git Service
* [Grafana](http://grafana.org/): The open source analytics & monitoring solution for every database
* [Peach Docs](https://peachdocs.org): A modern documentation web server
* [Go Walker](https://gowalker.org): Go online API documentation
* [Intel Stack](https://intelstack.com/): A 100% free intelligence marketplace

## Getting More

* New to Macaron? Check out the [Starter Guide](/starter_guide)!
* [Middlewares](/middlewares) that are built for Macaron.
* Have any questions? Maybe there are [answers](/faqs) for you!
* If you think anything is not clear in the documentation, just [file an issue](https://github.com/go-macaron/docs/issues)!


# Starter Guide

Before we get started, one thing you should know is that this documentation does not teach you how to use Go. Instead, we will explore Macaron based on the basic Go knowledge you already have.

To install Macaron:

```bash
go get gopkg.in/macaron.v1
```

And upgrade Macaron in the future:

```bash
go get -u gopkg.in/macaron.v1
```

## Minimal Example

Create a file called `main.go`, and type following code:

```go
package main

import "gopkg.in/macaron.v1"

func main() {
    m := macaron.Classic()
    m.Get("/", func() string {
        return "Hello world!"
    })
    m.Run()
}
```

Function [`macaron.Classic`](https://gowalker.org/gopkg.in/macaron.v1#Classic) creates and returns a [Classic Macaron](/core_concepts#classic-macaron).

Method [`m.Get`](https://gowalker.org/gopkg.in/macaron.v1#Router_Get) is for registering routes for HTTP GET method. In this case, we allow GET requests to root path `/` and has a [Handler](/core_concepts#handlers) function to simply returns string `Hello world!` as response.

You may have questions about why the handler function can return a string as response? The magic is the [Return Values](/core_concepts#return-values), this is a special case/syntax for responding requests by string.

Finally, we call method [`m.Run`](https://gowalker.org/gopkg.in/macaron.v1#Macaron_Run) to get server running. By default, Macaron [Instances](/core_concepts#instances) will listen on `0.0.0.0:4000`.

Then, execute command `go run main.go`, you should see a log message is printed to the console:

```bash
[Macaron] listening on 0.0.0.0:4000 (development)
```

Now, open your browser and visit [localhost:4000](http://localhost:4000), victory!

## Extended Example

Let’s modify the `main.go` and do some extended exercises.

```go
package main

import (
    "log"
    "net/http"

    "gopkg.in/macaron.v1"
)

func main() {
    m := macaron.Classic()
    m.Get("/", myHandler)

    log.Println("Server is running...")
    log.Println(http.ListenAndServe("0.0.0.0:4000", m))
}

func myHandler(ctx *macaron.Context) string {
    return "the request path is: " + ctx.Req.RequestURI
}
```

If you execute command `go run main.go` again, you’ll see string `the request path is: /` is on your screen.

So what’s different now?

First of all, we still use [Classic Macaron](/core_concepts#classic-macaron) and register route for HTTP GET method of root path `/`. We don’t use anonymous function anymore, but a named function called `myHandler`. Notice that there is no parentheses after function name when we register route because we do not call it at that point.

The function `myHandler` accepts one argument with type [`*macaron.Context`](https://github.com/go-macaron/docs/tree/417c55669a8c33f6b490acd3c1637698489874d0/middlewares/core_services.md#context) and returns a string. You may notice that we didn’t tell Macaron what arguments should pass to `myHandler` when we register routes, and if you look at the [`m.Get`](https://gowalker.org/gopkg.in/macaron.v1#Router_Get) method, you will see Macaron sees all handlers([`macaron.Handler`](https://gowalker.org/gopkg.in/macaron.v1#Handler)) as type `interface{}`. So how does Macaron know?

This is related to the concept of [Service Injection](/core_concepts#service-injection), [`*macaron.Context`](https://github.com/go-macaron/docs/tree/417c55669a8c33f6b490acd3c1637698489874d0/middlewares.md/core_services/README.md#context) is one of the default injected services, so you can use it directly. Don’t worry about how to inject your own services, it’s just not the time to tell you yet.

Like the previous example, we need to make server listen on a address. This time, we use function from Go standard library called [`http.ListenAndServe`](https://gowalker.org/net/http#ListenAndServe), which shows any Macaron [Instance](/core_concepts#instances) is fully compatible with Go standard library.

## Go Further

You now know about how to write simple code based on Macaron, please try to modify two examples above and make sure you fully understand the words you read.

When you feel comfortable, get up and keep reading on following chapters.


# Core Concepts

## Classic Macaron

To get up and running quickly, [`macaron.Classic`](https://gowalker.org/gopkg.in/macaron.v1#Classic) provides some reasonable defaults that work well for most of web applications:

```go
m := macaron.Classic()
// ... middleware and routing goes here
m.Run()
```

Below is some of the functionality [`macaron.Classic`](https://gowalker.org/gopkg.in/macaron.v1#Classic) pulls in automatically:

* Request/response logging - [`macaron.Logger`](/core_services#routing-logger)
* Panic recovery - [`macaron.Recovery`](/core_services#panic-recovery)
* Static file serving - [`macaron.Static`](/core_services#static-files)

## Instances

Any object with type [`macaron.Macaron`](https://gowalker.org/gopkg.in/macaron.v1#Macaron) can be seen as an instance of Macaron, you can have as many instances as you'd like in a single piece of code.

## Handlers

Handlers are the heart and soul of Macaron. A handler is basically any kind of callable function:

```go
m.Get("/", func() string {
    return "hello world"
})
```

Non-anonymous function is also allowed for the purpose of using it in multiple routes:

```go
m.Get("/", myHandler)
m.Get("/hello", myHandler)

func myHandler() string {
    return "hello world"
}
```

Besides, one route can have as many as handlers you want to register with:

```go
m.Get("/", myHandler1, myHandler2)

func myHandler1() {
    // ... do something
}

func myHandler2() string {
    return "hello world"
}
```

### Return Values

If a handler returns something, Macaron will write the result to the current [`http.ResponseWriter`](http://gowalker.org/net/http#ResponseWriter) as a string:

```go
m.Get("/", func() string {
    return "hello world" // HTTP 200 : "hello world"
})

m.Get("/", func() *string {
    str := "hello world"
    return &str // HTTP 200 : "hello world"
})

m.Get("/", func() []byte {
    return []byte("hello world") // HTTP 200 : "hello world"
})

m.Get("/", func() error {
    // Nothing happens if returns nil
    return nil 
}, func() error {
    // ... get some error
    return err // HTTP 500 : <error message>
})
```

You can also optionally return a status code (only applys for `string` and `[]byte` types):

```go
m.Get("/", func() (int, string) {
    return 418, "i'm a teapot" // HTTP 418 : "i'm a teapot"
})

m.Get("/", func() (int, *string) {
    str := "i'm a teapot"
    return 418, &str // HTTP 418 : "i'm a teapot"
})

m.Get("/", func() (int, []byte) {
    return 418, []byte("i'm a teapot") // HTTP 418 : "i'm a teapot"
})
```

### Service Injection

Handlers are invoked via reflection. Macaron makes use of [Dependency Injection](http://en.wikipedia.org/wiki/Dependency_injection) to resolve dependencies in a Handlers argument list. **This makes Macaron completely compatible with golang's** [**`http.HandlerFunc`**](https://gowalker.org/net/http#HandlerFunc) **interface.**

If you add an argument to your handler, Macaron will search its list of services and attempt to resolve the dependency via type assertion:

```go
m.Get("/", func(resp http.ResponseWriter, req *http.Request) {
    // resp and req are injected by Macaron
    resp.WriteHeader(200) // HTTP 200
})
```

The most commonly used service in your code should be [`*macaron.Context`](/core_services#context):

```go
m.Get("/", func(ctx *macaron.Context) {
    ctx.Resp.WriteHeader(200) // HTTP 200
})
```

The following services are included with [`macaron.Classic`](https://gowalker.org/gopkg.in/macaron.v1#Classic):

* [`*macaron.Context`](/core_services#context) - HTTP request context
* [`*log.Logger`](/core_services#global-logger) - Global logger for Macaron instances
* [`http.ResponseWriter`](/core_services#response-stream) - HTTP Response writer interface
* [`*http.Request`](/core_services#request-object) - HTTP Request

### Middleware Handlers

Middleware Handlers sit between the incoming HTTP request and the router. In essence they are no different than any other Handler in Macaron. You can add a middleware handler to the stack like so:

```go
m.Use(func() {
  // do some middleware stuff
})
```

You can have full control over the middleware stack with the `Handlers` function. This will replace any handlers that have been previously set:

```go
m.Handlers(
    Middleware1,
    Middleware2,
    Middleware3,
)
```

Middleware Handlers work really well for things like logging, authorization, authentication, sessions, gzipping, error pages and any other operations that must happen before or after an HTTP request:

```go
// validate an api key
m.Use(func(ctx *macaron.Context) {
    if ctx.Req.Header.Get("X-API-KEY") != "secret123" {
        ctx.Resp.WriteHeader(http.StatusUnauthorized)
    }
})
```

## Macaron Env

Some Macaron handlers make use of the `macaron.Env` global variable to provide special functionality for development environments vs production environments. It is recommended that the `MACARON_ENV=production` environment variable to be set when deploying a Macaron server into a production environment.

## Handler Workflow

![Handler Workflow](/files/-Lr_s1cEu-hRvvppo-El)


# Core Services

By default, Macaron injects some services to power your application, those services are known as **core services**, which means you can directly use them as handler arguments without any additional work.

## Context

This service is represented by type [`*macaron.Context`](https://gowalker.org/gopkg.in/macaron.v1#Context). This is the very core service for everything you do upon Macaron. It contains all the information you need for request, response, templating, data store, and inject or retrieve other services.

To use it:

```go
package main

import "gopkg.in/macaron.v1"

func Home(ctx *macaron.Context) {
    // ...
}
```

### Next()

Method [`Context.Next`](https://gowalker.org/gopkg.in/macaron.v1#Context_Next) is an optional feature that Middleware Handlers can call to yield the until after the other Handlers have been executed. This works really well for any operations that must happen after an HTTP request:

```go
// log before and after a request
m.Use(func(ctx *macaron.Context, log *log.Logger){
    log.Println("before a request")

    ctx.Next()

    log.Println("after a request")
})
```

### Cookie

The very basic usage of cookie is just:

* [`*macaron.Context.SetCookie`](https://gowalker.org/gopkg.in/macaron.v1#Context_SetCookie)
* [`*macaron.Context.GetCookie`](https://gowalker.org/gopkg.in/macaron.v1#Context_GetCookie), [`*macaron.Context.GetCookieInt`](https://gowalker.org/gopkg.in/macaron.v1#Context_GetCookieInt), [`*macaron.Context.GetCookieInt64`](https://gowalker.org/gopkg.in/macaron.v1#Context_GetCookieInt64), [`*macaron.Context.GetCookieFloat64`](https://gowalker.org/gopkg.in/macaron.v1#Context_GetCookieFloat64)

To use them:

```go
// ...
m.Get("/set", func(ctx *macaron.Context) {
    ctx.SetCookie("user", "Unknwon", 1)
})

m.Get("/get", func(ctx *macaron.Context) string {
    return ctx.GetCookie("user")
})
// ...
```

Use following arguments order to set more properties: `SetCookie(<name>, <value>, <max age>, <path>, <domain>, <secure>, <http only>,<expires>)`.

For example, the most advanced usage would be: `SetCookie("user", "unknwon", 999, "/", "localhost", true, true, time.Now())`.

Note that order is fixed.

There are also more secure cookie support. First, you need to call [`macaron.SetDefaultCookieSecret`](https://gowalker.org/gopkg.in/macaron.v1#Macaron_SetDefaultCookieSecret), then use it by calling:

* [`*macaron.Context.SetSecureCookie`](https://gowalker.org/gopkg.in/macaron.v1#Context_SetSecureCookie)
* [`*macaron.Context.GetSecureCookie`](https://gowalker.org/gopkg.in/macaron.v1#Context_GetSecureCookie)

These two methods uses default secret string you set globally to encode and decode values.

To use them:

```go
// ...
m.SetDefaultCookieSecret("macaron")
m.Get("/set", func(ctx *macaron.Context) {
    ctx.SetSecureCookie("user", "Unknwon", 1)
})

m.Get("/get", func(ctx *macaron.Context) string {
    name, _ := ctx.GetSecureCookie("user")
    return name
})
// ...
```

For people who wants even more secure cookies that change secret string every time, just use:

* [`*macaron.Context.SetSuperSecureCookie`](https://gowalker.org/gopkg.in/macaron.v1#Context_SetSuperSecureCookie)
* [`*macaron.Context.GetSuperSecureCookie`](https://gowalker.org/gopkg.in/macaron.v1#Context_GetSuperSecureCookie)

To use them:

```go
// ...
m.Get("/set", func(ctx *macaron.Context) {
    ctx.SetSuperSecureCookie("macaron", "user", "Unknwon", 1)
})

m.Get("/get", func(ctx *macaron.Context) string {
    name, _ := ctx.GetSuperSecureCookie("macaron", "user")
    return name
})
// ...
```

### Other Helper methods

* To set/get URL parameters: [`ctx.SetParams`](https://gowalker.org/gopkg.in/macaron.v1#Context_SetParams) / [`ctx.Params`](https://gowalker.org/gopkg.in/macaron.v1#Context_Params), [`ctx.ParamsEscape`](https://gowalker.org/gopkg.in/macaron.v1#Context_ParamsEscape), [`ctx.ParamsInt`](https://gowalker.org/gopkg.in/macaron.v1#Context_ParamsInt), [`ctx.ParamsInt64`](https://gowalker.org/gopkg.in/macaron.v1#Context_ParamsInt64), [`ctx.ParamsFloat64`](https://gowalker.org/gopkg.in/macaron.v1#Context_ParamsFloat64)
* To get query parameters: [`ctx.Query`](https://gowalker.org/gopkg.in/macaron.v1#Context_ctx.Query), [`ctx.QueryEscape`](https://gowalker.org/gopkg.in/macaron.v1#Context_ctx.QueryEscape), [`ctx.QueryInt`](https://gowalker.org/gopkg.in/macaron.v1#Context_ctx.QueryInt), [`ctx.QueryInt64`](https://gowalker.org/gopkg.in/macaron.v1#Context_ctx.QueryInt64), [`ctx.QueryFloat64`](https://gowalker.org/gopkg.in/macaron.v1#Context_ctx.QueryFloat64), [`ctx.QueryStrings`](https://gowalker.org/gopkg.in/macaron.v1#Context_ctx.QueryStrings), [`ctx.QueryTrim`](https://gowalker.org/gopkg.in/macaron.v1#Context_QueryTrim)
* To serve content or file: [`ctx.ServeContent`](https://gowalker.org/gopkg.in/macaron.v1#Context_ServeContent), [`ctx.ServeFile`](https://gowalker.org/gopkg.in/macaron.v1#Context_ServeFile), [`ctx.ServeFileContent`](https://gowalker.org/gopkg.in/macaron.v1#Context_ServeFileContent)
* To get remote IP address: [`ctx.RemoteAddr`](https://gowalker.org/gopkg.in/macaron.v1#Context_RemoteAddr)

## Router Logger

This service can be injected by function [`macaron.Logger`](https://gowalker.org/gopkg.in/macaron.v1#Logger). It is responsible for your application routing log.

To use it:

```go
package main

import "gopkg.in/macaron.v1"

func main() {
    m := macaron.New()
    m.Use(macaron.Logger())
    // ...
}
```

{% hint style="info" %}
This service is injected automatically when you use [`macaron.Classic`](https://gowalker.org/gopkg.in/macaron.v1#Classic).
{% endhint %}

Sample output take from [Peach Docs](https://peachdocs.org):

```
[Macaron] Started GET /docs/middlewares/core.html for [::1]
[Macaron] Completed /docs/middlewares/core.html 200 OK in 2.114956ms
```

## Panic Recovery

This service can be injected by function [`macaron.Recovery`](https://gowalker.org/gopkg.in/macaron.v1#Recovery). It is responsible for recovering your application when panic happens.

To use it:

```go
package main

import "gopkg.in/macaron.v1"

func main() {
    m := macaron.New()
    m.Use(macaron.Recovery())
    // ...
}
```

{% hint style="info" %}
This service is injected automatically when you use [`macaron.Classic`](https://gowalker.org/gopkg.in/macaron.v1#Classic).
{% endhint %}

## Static Files

This service can be injected by function [`macaron.Static`](https://gowalker.org/gopkg.in/macaron.v1#Static). It is responsible for serving static resources of your application, it can be injected as many times as you want if you have multiple static directories.

To use it:

```go
package main

import "gopkg.in/macaron.v1"

func main() {
    m := macaron.New()
    m.Use(macaron.Static("public"))
    m.Use(macaron.Static("assets"))
    // ...
}
```

{% hint style="info" %}
This service is injected automatically with directory `public` when you use [`macaron.Classic`](https://gowalker.org/gopkg.in/macaron.v1#Classic).
{% endhint %}

By default, when you try to request a directory, this service will not list directory files. Instead, it tries to find the `index.html` file.

Sample output take from [Peach Docs](https://peachdocs.org):

```
[Macaron] Started GET /css/prettify.css for [::1]
[Macaron] [Static] Serving /css/prettify.css
[Macaron] Completed /css/prettify.css 304 Not Modified in 97.584us
[Macaron] Started GET /imgs/macaron.png for [::1]
[Macaron] [Static] Serving /imgs/macaron.png
[Macaron] Completed /imgs/macaron.png 304 Not Modified in 123.211us
[Macaron] Started GET /js/gogsweb.min.js for [::1]
[Macaron] [Static] Serving /js/gogsweb.min.js
[Macaron] Completed /js/gogsweb.min.js 304 Not Modified in 47.653us
[Macaron] Started GET /css/main.css for [::1]
[Macaron] [Static] Serving /css/main.css
[Macaron] Completed /css/main.css 304 Not Modified in 42.58us
```

### Example

Suppose you have following directory structure:

```
public/
    |__ html
            |__ index.html
    |__ css/
            |__ main.css
```

Results:

| Request URL       | Match File |
| ----------------- | ---------- |
| `/html/main.html` | None       |
| `/html/`          | index.html |
| `/css/main.css`   | main.css   |

### Options

This service also accepts second argument for custom options([`macaron.StaticOptions`](https://gowalker.org/gopkg.in/macaron.v1#StaticOptions)):

```go
package main

import "gopkg.in/macaron.v1"

func main() {
    m := macaron.New()
    m.Use(macaron.Static("public",
        macaron.StaticOptions{
            // Prefix is the optional prefix used to serve the static directory content. Default is empty string.
            Prefix: "public",
            // SkipLogging will disable [Static] log messages when a static file is served. Default is false.
            SkipLogging: true,
            // IndexFile defines which file to serve as index if it exists. Default is "index.html".
            IndexFile: "index.html",
            // Expires defines which user-defined function to use for producing a HTTP Expires Header. Default is nil.
            // https://developers.google.com/speed/docs/insights/LeverageBrowserCaching
            Expires: func() string { 
                return time.Now().Add(24 * 60 * time.Minute).UTC().Format("Mon, 02 Jan 2006 15:04:05 GMT")
            },
        }))
    // ...
}
```

### Multiple Static Handlers

In case you have multiple static directories, there is one helper function [`macaron.Statics`](https://gowalker.org/gopkg.in/macaron.v1#Statics) to make your life easier.

To use it:

```go
// ...
m.Use(macaron.Statics(macaron.StaticOptions{}, "public", "views"))
// ...
```

This will register both `public` and `views` as static directories.

## Others Services

### Global Logger

This service is represented by type [`*log.Logger`](http://gowalker.org/log#Logger). It is optional to use, but for convenience when you do not have a global level logger.

To use it:

```go
package main

import (
    "log"

    "gopkg.in/macaron.v1"
)

func main() {
    m := macaron.Classic()
    m.Get("/", myHandler)
    m.Run()
}

func myHandler(ctx *macaron.Context, logger *log.Logger) string {
    logger.Println("the request path is: " + ctx.Req.RequestURI)
    return "the request path is: " + ctx.Req.RequestURI
}
```

{% hint style="info" %}
This service is injected automatically for every Macaron [Instance](/core_concepts#instances).
{% endhint %}

### Response Stream

This service is represented by type [`http.ResponseWriter`](http://gowalker.org/net/http#ResponseWriter). It is optional to use, normally, you should use `*macaron.Context.Resp`.

To use it:

```go
package main

import (
    "gopkg.in/macaron.v1"
)

func main() {
    m := macaron.Classic()
    m.Get("/", myHandler)
    m.Run()
}

func myHandler(ctx *macaron.Context) {
    ctx.Resp.Write([]byte("the request path is: " + ctx.Req.RequestURI))
}
```

{% hint style="info" %}
This service is injected automatically for every Macaron [Instance](/core_concepts#instances).
{% endhint %}

### Request Object

This service is represented by type [`*http.Request`](http://gowalker.org/net/http#Request). It is optional to use, normally, you should use `*macaron.Context.Req`.

Besides, this service provides three methods to help you easily retrieve request body:

* [`*macaron.Context.Req.Body().String()`](https://gowalker.org/gopkg.in/macaron.v1#RequestBody_String): get request body in `string` type
* [`*macaron.Context.Req.Body().Bytes()`](https://gowalker.org/gopkg.in/macaron.v1#RequestBody_Bytes): get request body in `[]byte` type
* [`*macaron.Context.Req.Body().ReadCloser()`](https://gowalker.org/gopkg.in/macaron.v1#RequestBody_ReadCloser): get request body in `io.ReadCloser` type

To use them:

```go
package main

import (
    "gopkg.in/macaron.v1"
)

func main() {
    m := macaron.Classic()
    m.Get("/body1", func(ctx *macaron.Context) {
        reader, err := ctx.Req.Body().ReadCloser()
        // ...
    })
    m.Get("/body2", func(ctx *macaron.Context) {
        data, err := ctx.Req.Body().Bytes()
        // ...
    })
    m.Get("/body3", func(ctx *macaron.Context) {
        data, err := ctx.Req.Body().String()
        // ...
    })
    m.Run()
}
```

Notice that request body can only be read once.

Sometimes you need to pass type [`*http.Request`](http://gowalker.org/net/http#Request) as an argument, you should use `*macaron.Context.Req.Request`.

{% hint style="info" %}
This service is injected automatically for every Macaron [Instance](/core_concepts#instances).
{% endhint %}


# Custom Services

Services are objects that are available to be injected into a handler's argument list. You can map a service on a **Global** or **Request** level.

## Global Mapping

A Macaron instance implements the [`inject.Injector`](https://gowalker.org/github.com/go-macaron/inject#Injector) interface, so mapping a service is easy:

```go
db := &MyDatabase{}
m := macaron.Classic()
m.Map(db) // Service will be available to all handlers as *MyDatabase
m.Get("/", func(db *MyDatabase) {
    // Operations with db.
})
m.Run()
```

## Request-Level Mapping

Mapping on the request level can be done in a handler via [`*macaron.Context`](https://gowalker.org/github.com/go-macaron/macaron#Context):

```go
func MyCustomLoggerHandler(ctx *macaron.Context) {
    logger := &MyCustomLogger{ctx.Req}
    ctx.Map(logger) // mapped as *MyCustomLogger
}

func main() {
    //...
    m.Get("/", MyCustomLoggerHandler, func(logger *MyCustomLogger) {
        // Operations with logger.
    })
    m.Get("/panic", func(logger *MyCustomLogger) {
        // This will panic because no logger service maps to this request.
    })
    //...
}
```

## Mapping values to Interfaces

One of the most powerful parts about services is the ability to map a service to an interface. For instance, if you wanted to override the [`http.ResponseWriter`](http://gowalker.org/net/http#ResponseWriter) with an object that wrapped it and performed extra operations, you can write the following handler:

```go
func WrapResponseWriter(ctx *macaron.Context) {
    rw := NewSpecialResponseWriter(ctx.Resp)
    // override ResponseWriter with our wrapper ResponseWriter
    ctx.MapTo(rw, (*http.ResponseWriter)(nil)) 
}
```

In this way, your code can enjoy new custom service feature without any change. Plus, allow more custom implementations of same type of services.


# Middlewares

Middlewares and helper modules allow you easily plugin/unplugin features for your Macaron applications.

There are already many [middlewares and modules](https://github.com/go-macaron) to simplify your work:

* [auth](https://github.com/go-macaron/auth) - HTTP Basic authentication
* [authz](https://github.com/go-macaron/authz) - ACL, RBAC and ABAC authorization based on [Casbin](https://github.com/casbin/casbin)
* [bindata](/middlewares/bindata) - Embed binary data as static and template files
* [binding](/middlewares/binding) - Request data binding and validation
* [cache](/middlewares/cache) - Cache manager
* [captcha](/middlewares/captcha) - Captcha service
* [csrf](/middlewares/csrf) - Generates and validates CSRF tokens
* [gzip](/middlewares/gzip) - Gzip compression to all responses
* [i18n](/middlewares/i18n) - Internationalization and Localization
* [inject](https://github.com/go-macaron/inject) - Map and inject dependencies
* [jade](https://github.com/go-macaron/jade) - Jade templating engine
* [method](https://github.com/go-macaron/method) - HTTP method override
* [oauth2](https://github.com/go-macaron/oauth2) - OAuth 2.0 backend client
* [permissions2](https://github.com/xyproto/permissions2) - Cookies, users and permissions
* [pongo2](https://github.com/go-macaron/pongo2) - Pongo2 template engine support
* [renders](https://github.com/go-macaron/renders) - Beego-like render engine (Macaron has built-in template engine, this is another option)
* [session](/middlewares/session) - Session manager
* [sockets](https://github.com/go-macaron/sockets) - WebSockets channels binding
* [switcher](/middlewares/switcher) - Multiple-site support
* [toolbox](https://github.com/go-macaron/toolbox) - Health check, pprof, profile and statistic services

## Best register order for middlewares

Some middlewares depends on others, here is a list for best ordering:

1. `macaron.Logger()`
2. `macaron.Recovery()`
3. `gzip.Gziper()`
4. `macaron.Static()`
5. `macaron.Renderer()`/`pongo2.Pongoer()`
6. `i18n.I18n()`
7. `cache.Cacher()`
8. `captcha.Captchaer()`
9. `session.Sessioner()`
10. `csrf.Csrfer()`
11. `toolbox.Toolboxer()`


# Routing

In Macaron, a route is an HTTP method paired with a URL-matching pattern. Each route can take one or more handler methods:

```go
m.Get("/", func() {
    // show something
})

m.Patch("/", func() {
    // update something
})

m.Post("/", func() {
    // create something
})

m.Put("/", func() {
    // replace something
})

m.Delete("/", func() {
    // destroy something
})

m.Options("/", func() {
    // http options
})

m.Any("/", func() {
    // do anything
})

m.Route("/", "GET,POST", func() {
    // combine something
})

m.Combo("/").
    Get(func() string { return "GET" }).
    Patch(func() string { return "PATCH" }).
    Post(func() string { return "POST" }).
    Put(func() string { return "PUT" }).
    Delete(func() string { return "DELETE" }).
    Options(func() string { return "OPTIONS" }).
    Head(func() string { return "HEAD" })

m.NotFound(func() {
    // Custom handle for 404
})
```

Notes:

* Routes are matched in the order they are defined,
* ...but, narrow range routes have higher priority than wider range routes(see below: **Matching Priority**)
* The first route that matches the request is invoked.

In some cases, HEAD method is also used wherever GET method is registered. To reduce redundant code, there is a method called [`SetAutoHead`](https://gowalker.org/gopkg.in/macaron.v1#Router_SetAutoHead) can help you automatically register it:

```go
m := New()
m.SetAutoHead(true)
m.Get("/", func() string {
    return "GET"
}) // HEAD method for path "/" is now registered as well.
```

If you want to use suburl without having a huge group indent, use `m.SetURLPrefix(suburl)`.

## Named Parameters

Route patterns may include named parameters, accessible via the method [`*Context.Params`](https://gowalker.org/gopkg.in/macaron.v1#Context_Params):

### Placeholders

Use a specific name to represent a route part:

```go
m.Get("/hello/:name", func(ctx *macaron.Context) string {
    return "Hello " + ctx.Params(":name")
})

m.Get("/date/:year/:month/:day", func(ctx *macaron.Context) string {
    return fmt.Sprintf("Date: %s/%s/%s", ctx.Params(":year"), ctx.Params(":month"), ctx.Params(":day"))
})
```

Of course, `:` seems noising sometimes, take it out when you feels like:

```go
m.Get("/hello/:name", func(ctx *macaron.Context) string {
    return "Hello " + ctx.Params("name")
})

m.Get("/date/:year/:month/:day", func(ctx *macaron.Context) string {
    return fmt.Sprintf("Date: %s/%s/%s", ctx.Params("year"), ctx.Params("month"), ctx.Params("day"))
})
```

### Globs

Routes can be matched with globs:

```go
m.Get("/hello/*", func(ctx *macaron.Context) string {
    return "Hello " + ctx.Params("*")
})
```

What happens when `*` is in the middle?

```go
m.Get("/date/*/*/*/events", func(ctx *macaron.Context) string {
    return fmt.Sprintf("Date: %s/%s/%s", ctx.Params("*0"), ctx.Params("*1"), ctx.Params("*2"))
})
```

### Regular Expressions

Regular expressions can be used as well:

* Regular match:

  ```go
    m.Get("/user/:username([\\w]+)", func(ctx *macaron.Context) string {
        return fmt.Sprintf("Hello %s", ctx.Params(":username"))
    })

    m.Get("/user/:id([0-9]+)", func(ctx *macaron.Context) string {
        return fmt.Sprintf("User ID: %s", ctx.Params(":id"))
    })

    m.Get("/user/*.*", func(ctx *macaron.Context) string {
        return fmt.Sprintf("Last part is: %s, Ext: %s", ctx.Params(":path"), ctx.Params(":ext"))
    })
  ```
* Mixed match:

  ```go
    m.Get("/cms_:id([0-9]+).html", func(ctx *macaron.Context) string {
        return fmt.Sprintf("The ID is %s", ctx.Params(":id"))
    })
  ```
* Optional match:
  * `/user/?:id`, matches both `/user/` and `/user/123`.
* Shortcuts:
  * `/user/:id:int`, `:int` is shortcut for `([0-9]+)`.
  * `/user/:name:string`, `:string` is shortcut for `([\w]+)`.

## Matching Priority

Matching priority of different match patterns from higher to lower:

* Static routes:
  * `/`
  * `/home`
* Regular expression routes:
  * `/(.+).html`
  * `/([0-9]+).css`
* Path-extension routesL
  * `/*.*`
* Placeholder routes:
  * `/:id`
  * `/:name`
* Glob routes:
  * `/*`

Other notes:

* Matching priority of same pattern is first add first match.
* More detailed pattern gets higher matching priority:
  * `/*/*/events` > `/*`

### Building URLs

You can build URLs with named parameters, to do this, you should use [`*Route.Name`](https://gowalker.org/gopkg.in/macaron.v1#Route_Name) method give route a name:

```go
// ...
m.Get("/users/:id([0-9]+)/:name:string.profile", handler).Name("user_profile")
m.Combo("/api/:user/:repo").Get(handler).Post(handler).Name("user_repo")
// ...
```

Then use [`*Router.URLFor`](https://gowalker.org/gopkg.in/macaron.v1#Router_URLFor) to build URLs with route of given name:

```go
// ...
func handler(ctx *macaron.Context) {
    // /users/12/unknwon.profile
    userProfile := ctx.URLFor("user_profile", ":id", "12", ":name", "unknwon")
    // /api/unknwon/macaron
    userRepo := ctx.URLFor("user_repo", ":user", "unknwon", ":repo", "macaron")
}
// ...
```

#### Using it in Go templating engine

```go
// ...
m.Use(macaron.Renderer(macaron.RenderOptions{
    Funcs:      []template.FuncMap{map[string]interface{}{
        "URLFor": m.URLFor,    
    }},
}))
// ...
```

#### Using it in Pongo2 templating engine

```go
// ...
ctx.Data["URLFor"] = ctx.URLFor
ctx.HTML(200, "home")
// ...
```

## Advanced Routing

Route handlers can be stacked on top of each other, which is useful for things like authentication and authorization:

```go
m.Get("/secret", authorize, func() {
    // this will execute as long as authorize doesn't write a response
})
```

Let's see an extreme example:

```go
package main

import (
    "fmt"

    "gopkg.in/macaron.v1"
)

func main() {
    m := macaron.Classic()
    m.Get("/",
        func(ctx *macaron.Context) {
            ctx.Data["Count"] = 1
        },
        func(ctx *macaron.Context) {
            ctx.Data["Count"] = ctx.Data["Count"].(int) + 1
        },
        func(ctx *macaron.Context) {
            ctx.Data["Count"] = ctx.Data["Count"].(int) + 1
        },
        func(ctx *macaron.Context) {
            ctx.Data["Count"] = ctx.Data["Count"].(int) + 1
        },
        func(ctx *macaron.Context) {
            ctx.Data["Count"] = ctx.Data["Count"].(int) + 1
        },
        func(ctx *macaron.Context) string {
            return fmt.Sprintf("There are %d handlers before this", ctx.Data["Count"])
        },
    )
    m.Run()
}
```

Guess what's output will be? Yes, `There are 5 handlers before this`. There are no hard limitation of how many handlers you can have for a route, but you may wonder how does Macaron know when to stop calling next handler?

To answer this question, please consider the following example:

```go
package main

import (
    "fmt"

    "gopkg.in/macaron.v1"
)

func main() {
    m := macaron.Classic()
    m.Get("/",
        func(ctx *macaron.Context) {
            ctx.Data["Count"] = 1
        },
        func(ctx *macaron.Context) {
            ctx.Data["Count"] = ctx.Data["Count"].(int) + 1
        },
        func(ctx *macaron.Context) {
            ctx.Data["Count"] = ctx.Data["Count"].(int) + 1
        },
        func(ctx *macaron.Context) {
            ctx.Data["Count"] = ctx.Data["Count"].(int) + 1
        },
        func(ctx *macaron.Context) string {
            return fmt.Sprintf("There are %d handlers before this", ctx.Data["Count"])
        },
        func(ctx *macaron.Context) string {
            return fmt.Sprintf("There are %d handlers before this", ctx.Data["Count"])
        },
    )
    m.Run()
}
```

In this case, the output will always be `There are 4 handlers before this`, and the last handler never gets chance to call. Why? Because we write response in 5th handler. Thus, once any handler writes anything to the response stream, Macaron will stop calling next handler.

### Group Routing

Route groups can be added too using the [`macaron.Group`](https://gowalker.org/gopkg.in/macaron.v1#Router_Group) method:

```go
m.Group("/books", func() {
    m.Get("/:id", GetBooks)
    m.Post("/new", NewBook)
    m.Put("/update/:id", UpdateBook)
    m.Delete("/delete/:id", DeleteBook)

    m.Group("/chapters", func() {
        m.Get("/:id", GetBooks)
        m.Post("/new", NewBook)
        m.Put("/update/:id", UpdateBook)
        m.Delete("/delete/:id", DeleteBook)
    })
})
```

Just like you can pass middlewares to a handler you can pass middlewares to groups:

```go
m.Group("/books", func() {
    m.Get("/:id", GetBooks)
    m.Post("/new", NewBook)
    m.Put("/update/:id", UpdateBook)
    m.Delete("/delete/:id", DeleteBook)

    m.Group("/chapters", func() {
        m.Get("/:id", GetBooks)
        m.Post("/new", NewBook)
        m.Put("/update/:id", UpdateBook)
        m.Delete("/delete/:id", DeleteBook)
    }, MyMiddleware3, MyMiddleware4)
}, MyMiddleware1, MyMiddleware2)
```

Still, no hard limitation of how many nested group routes and group level handlers(middlewares) you can have.


# Templating

There are two official middlewares built for templating for your Macaron application currently, which are [`macaron.Renderer`](https://gowalker.org/gopkg.in/macaron.v1#Renderer) and [`pongo2.Pongoer`](https://gowalker.org/github.com/go-macaron/pongo2#Pongoer).

You're free to choose one of them to use, and one Macaron [Instance](/core_concepts#instances) only uses one templating engine.

Common behaviors:

* Both of them are supporting render XML, JSON and raw content as response, the only difference between them is the way to render HTML.
* Both of them use `templates` as default template file directory.
* Both of them use `.tmpl` and `.html` as default template file extensions.
* Both of them use [Macaron Env](https://github.com/go-macaron/docs/tree/c242bc20d97be80b903f76aa7ed48a4aa1b3d41d/intro/core_concepts/README.md#macaron-env) to determine whether to cache template files(when `macaron.Env == macaron.PROD`) or not.

## Render HTML

### Go Templating Engine

This service can be injected by function [`macaron.Renderer`](https://gowalker.org/gopkg.in/macaron.v1#Renderer) and is represented by type [`macaron.Render`](https://gowalker.org/gopkg.in/macaron.v1#Render). It is optional to use, normally, you should use `*macaron.Context.Render`.This service uses Go built-in templating engine to render your HTML. If you want to know about details of how it works, please see [`html/template` documentation](https://gowalker.org/html/template).

#### Example

Suppose you have following directory structure:

```
main/
    |__ main.go
    |__ templates/
            |__ hello.tmpl
```

hello.tmpl:

```markup
<h1>Hello {{.Name}}</h1>
```

main.go:

```go
package main

import "gopkg.in/macaron.v1"

func main() {
    m := macaron.Classic()
    m.Use(macaron.Renderer())

    m.Get("/", func(ctx *macaron.Context) {
        ctx.Data["Name"] = "jeremy"
        ctx.HTML(200, "hello") // 200 is the response code.
    })

    m.Run()
}
```

#### Options

This service also accepts one argument for custom options([`macaron.RenderOptions`](https://gowalker.org/gopkg.in/macaron.v1#RenderOptions)):

```go
package main

import "gopkg.in/macaron.v1"

func main() {
    m := macaron.Classic()
    m.Use(macaron.Renderer(macaron.RenderOptions{
        // Directory to load templates. Default is "templates".
        Directory: "templates",
        // Extensions to parse template files from. Defaults are [".tmpl", ".html"].
        Extensions: []string{".tmpl", ".html"},
        // Funcs is a slice of FuncMaps to apply to the template upon compilation. Default is [].
        Funcs: []template.FuncMap{map[string]interface{}{
            "AppName": func() string {
                return "Macaron"
            },
            "AppVer": func() string {
                return "1.0.0"
            },
        }},
        // Delims sets the action delimiters to the specified strings. Defaults are ["{{", "}}"].
        Delims: macaron.Delims{"{{", "}}"},
        // Appends the given charset to the Content-Type header. Default is "UTF-8".
        Charset: "UTF-8",
        // Outputs human readable JSON. Default is false.
        IndentJSON: true,
        // Outputs human readable XML. Default is false.
        IndentXML: true,
        // Prefixes the JSON output with the given bytes. Default is no prefix.
        PrefixJSON: []byte("macaron"),
        // Prefixes the XML output with the given bytes. Default is no prefix.
        PrefixXML: []byte("macaron"),
        // Allows changing of output to XHTML instead of HTML. Default is "text/html".
        HTMLContentType: "text/html",
    }))        
    // ...
}
```

### Pongo2 Templating Engine

This service can be injected by function [`pongo2.Pongoer`](https://gowalker.org/github.com/go-macaron/pongo2#Pongoer) and is represented by type [`macaron.Render`](https://gowalker.org/gopkg.in/macaron.v1#Render). It is optional to use, normally, you should use `*macaron.Context.Render`.This service uses Pongo2 **v3** templating engine to render your HTML. If you want to know about details of how it works, please see [pongo2 documentation](https://github.com/flosch/pongo2).

#### Example

Suppose you have following directory structure:

```
main/
    |__ main.go
    |__ templates/
            |__ hello.tmpl
```

hello.tmpl:

```markup
<h1>Hello {{Name}}</h1>
```

main.go:

```go
package main

import (
    "github.com/go-macaron/pongo2"
    "gopkg.in/macaron.v1"
)

func main() {
    m := macaron.Classic()
    m.Use(pongo2.Pongoer())

    m.Get("/", func(ctx *macaron.Context) {
        ctx.Data["Name"] = "jeremy"
        ctx.HTML(200, "hello") // 200 is the response code.
    })

    m.Run()
}
```

#### Options

This service also accepts one argument for custom options([`pongo2.Options`](https://gowalker.org/github.com/go-macaron/pongo2#Options)):

```go
package main

import (
    "github.com/go-macaron/pongo2"
    "gopkg.in/macaron.v1"
)

func main() {
    m := macaron.Classic()
    m.Use(pongo2.Pongoer(pongo2.Options{
        // Directory to load templates. Default is "templates".
        Directory: "templates",
        // Extensions to parse template files from. Defaults are [".tmpl", ".html"].
        Extensions: []string{".tmpl", ".html"},
        // Appends the given charset to the Content-Type header. Default is "UTF-8".
        Charset: "UTF-8",
        // Outputs human readable JSON. Default is false.
        IndentJSON: true,
        // Outputs human readable XML. Default is false.
        IndentXML: true,
        // Allows changing of output to XHTML instead of HTML. Default is "text/html".
        HTMLContentType: "text/html",
    }))        
    // ...
}
```

### Template Sets

When you have more than one type of template files, you should use template sets, which allows you decide which one to render dynamically at runtime.

To use it in Go templating engine:

```go
// ...
m.Use(macaron.Renderers(macaron.RenderOptions{
    Directory: "templates/default",
}, "theme1:templates/theme1", "theme2:templates/theme2"))

m.Get("/foobar", func(ctx *macaron.Context) {
    ctx.HTML(200, "hello")
})

m.Get("/foobar1", func(ctx *macaron.Context) {
    ctx.HTMLSet(200, "theme1", "hello")
})

m.Get("/foobar2", func(ctx *macaron.Context) {
    ctx.HTMLSet(200, "theme2", "hello")
})
// ...
```

To use it in Pongo2 templating engine:

```go
// ...
m.Use(pongo2.Pongoers(pongo2.Options{
    Directory: "templates/default",
}, "theme1:templates/theme1", "theme2:templates/theme2"))

m.Get("/foobar", func(ctx *macaron.Context) {
    ctx.HTML(200, "hello")
})

m.Get("/foobar1", func(ctx *macaron.Context) {
    ctx.HTMLSet(200, "theme1", "hello")
})

m.Get("/foobar2", func(ctx *macaron.Context) {
    ctx.HTMLSet(200, "theme2", "hello")
})
// ...
```

As you can see, the only difference here is two functions [`macaron.Renderers`](https://gowalker.org/gopkg.in/macaron.v1#Renderers) and [`pongo2.Pongoers`](https://gowalker.org/github.com/go-macaron/pongo2#Pongoers).

The option argument is aiming for defualt template set and settings, and a list of name-directory pairs separate by `:`.

If the last part of template directory is same as your template set name, you can omit it as follows:

```go
// ...
m.Use(macaron.Renderers(RenderOptions{
    Directory: "templates/default",
}, "templates/theme1", "templates/theme2"))

m.Get("/foobar", func(ctx *macaron.Context) {
    ctx.HTML(200, "hello")
})

m.Get("/foobar1", func(ctx *macaron.Context) {
    ctx.HTMLSet(200, "theme1", "hello")
})

m.Get("/foobar2", func(ctx *macaron.Context) {
    ctx.HTMLSet(200, "theme2", "hello")
})
// ...
```

#### Helper methods for template sets

To check if given template set exists:

```go
// ...
m.Get("/foobar", func(ctx *macaron.Context) {
    ok := ctx.HasTemplateSet("theme2")
    // ...
})
// ...
```

To change template set directory:

```go
// ...
m.Get("/foobar", func(ctx *macaron.Context) {
    ctx.SetTemplatePath("theme2", "templates/new/theme2")
    // ...
})
// ...
```

### Quick summary on rendering HTML

As you can see, the only difference between two templating engines to render HTML is the syntax of template files, in the code level, they are exactly the same.

If you just want to get results of rendered HTML, call method `*macaron.Context.Render.HTMLString`:

```go
package main

import "gopkg.in/macaron.v1"

func main() {
    m := macaron.Classic()
    m.Use(macaron.Renderer())

    m.Get("/", func(ctx *macaron.Context) {
        ctx.Data["Name"] = "jeremy"
        output, err := ctx.HTMLString("hello")
        // Do other operations
    })

    m.Run()
}
```

## Render XML, JSON and raw content

It is fairly easy to render XML, JSON and raw content compare to HTML.

```go
package main

import "gopkg.in/macaron.v1"

type Person struct {
    Name string
    Age  int
    Sex  string
}

func main() {
    m := macaron.Classic()
    m.Use(macaron.Renderer())

    m.Get("/xml", func(ctx *macaron.Context) {
        p := Person{"Unknwon", 21, "male"}
        ctx.XML(200, &p)
    })
    m.Get("/json", func(ctx *macaron.Context) {
        p := Person{"Unknwon", 21, "male"}
        ctx.JSON(200, &p)
    })
    m.Get("/raw", func(ctx *macaron.Context) {
        ctx.RawData(200, []byte("raw data goes here"))
    })
    m.Get("/text", func(ctx *macaron.Context) {
        ctx.PlainText(200, []byte("plain text goes here"))
    })

    m.Run()
}
```

## Response status, error and redirect

To response status, error and redirect:

```go
package main

import "gopkg.in/macaron.v1"

func main() {
    m := macaron.Classic()
    m.Use(macaron.Renderer())

    m.Get("/status", func(ctx *macaron.Context) {
        ctx.Status(403)
    })
    m.Get("/error", func(ctx *macaron.Context) {
        ctx.Error(500, "Internal Server Error")
    })
    m.Get("/redirect", func(ctx *macaron.Context) {
        ctx.Redirect("/") // The second argument is response code, default is 302.
    })

    m.Run()
}
```

## Change template path at runtime

In case you want to change your template path at runtime, call method `*macaron.Context.SetTemplatePath`. Note that this operation applies to global, not just current request.

### Example

Suppose you have following directory structure:

```
main/
    |__ main.go
    |__ templates/
            |__ hello.tmpl
    |__ templates2/
            |__ hello.tmpl
```

templates/hello.tmpl:

```markup
<h1>Hello {{.Name}}</h1>
```

templates2/hello.tmpl:

```markup
<h1>What's up, {{.Name}}</h1>
```

main.go:

```go
package main

import "gopkg.in/macaron.v1"

func main() {
    m := macaron.Classic()
    m.Use(macaron.Renderer())

    m.Get("/old", func(ctx *macaron.Context) {
        ctx.Data["Name"] = "Unknwon"
        ctx.HTML(200, "hello")
        // Empty string refers to default template set.
        ctx.SetTemplatePath("", "templates2")
    })
    m.Get("/new", func(ctx *macaron.Context) {
        ctx.Data["Name"] = "Unknwon"
        ctx.HTML(200, "hello")
    })

    m.Run()
}
```

When you first request `/old`, the response will be `<h1>Hello Unknwon</h1>`, right after response, the template path has been changed to `template2`. So when you request `/new`, the response will be `<h1>What's up, Unknwon</h1>`.


# Gzip

Middleware gzip provides compress to responses for Macaron [Instances](/core_concepts#instances). Make sure to register it before other middlewares that write content to response.

* [GitHub](https://github.com/go-macaron/gzip)
* [API Reference](https://gowalker.org/github.com/go-macaron/gzip)

## Installation

```bash
go get github.com/go-macaron/gzip
```

## Usage

```go
package main

import (
    "github.com/go-macaron/gzip"
    "gopkg.in/macaron.v1"
)

func main() {
    m := macaron.Classic()
    m.Use(gzip.Gziper())
    // Register routers.
    m.Run()
}
```

In this case, the static files will not be compressed by Gzip, to compress them:

```go
package main

import (
    "github.com/go-macaron/gzip"
    "gopkg.in/macaron.v1"
)

func main() {
    m := macaron.New()
    m.Use(macaron.Logger())
    m.Use(macaron.Recovery())
    m.Use(gzip.Gziper())
    m.Use(macaron.Static("public"))
    // Register routers.
    m.Run()
}
```

Or you can choose to only compress a group of routes' responses:

```go
// ...

func main() {
    m := macaron.Classic()
    m.Group("/gzip", func() {
        // ...
    }, gzip.Gziper())
    // ...
    m.Run()
}
```

## Options

This service comes with a variety of configuration options([`gzip.Options`](https://gowalker.org/github.com/go-macaron/gzip#Options)):

```go
// ...
m.Use(gzip.Gziper(gzip.Options{
    // Compression level. Can be DefaultCompression(-1), ConstantCompression(-2)
    // or any integer value between BestSpeed(1) and BestCompression(9) inclusive.
    // Default is 4.
    CompressionLevel: 4,
}))
// ...
```


# Localization

Middleware i18n provides app Internationalization and Localization for Macaron [Instances](/core_concepts#instances).

* [GitHub](https://github.com/go-macaron/i18n)
* [API Reference](https://gowalker.org/github.com/go-macaron/i18n)

## Installation

```bash
go get github.com/go-macaron/i18n
```

## Usage

```go
// main.go
import (
    "github.com/go-macaron/i18n"
    "gopkg.in/macaron.v1"
)

func main() {
      m := macaron.Classic()
      m.Use(i18n.I18n(i18n.Options{
        Langs:    []string{"en-US", "zh-CN"},
        Names:    []string{"English", "简体中文"},
    }))

    m.Get("/", func(locale i18n.Locale) string {
        return "current language is" + locale.Lang
    })

    // Use in handler.
    m.Get("/trans", func(ctx *macaron.Context) string {
        return ctx.Tr("hello %s", "world")
    })

    m.Run()
}
```

```markup
<!-- templates/hello.tmpl -->
<h2>{{.i18n.Tr "hello %s" "world"}}!</h2>
```

### Pongo2

To use i18n feature in [pongo2](https://github.com/flosch/pongo2) with [middleware pongo2](https://github.com/go-macaron/pongo2):

```markup
<!-- templates/hello.tmpl -->
<h2>{{Tr(Lang,"hello %s","world")}}!</h2>
```

## Options

`i18n.I18n` comes with a variety of configuration options([`i18n.Options`](https://gowalker.org/github.com/go-macaron/i18n#Options)):

```go
// ...
m.Use(i18n.I18n(i18n.Options{
    // Directory to load locale files. Default is "conf/locale".
    Directory:    "conf/locale",
    // Languages that will be supported, order is meaningful.
    Langs:        []string{"en-US", "zh-CN"},
    // Human friendly names corresponding to Langs list.
    Names:        []string{"English", "简体中文"},
    // Locale file naming style. Default is "locale_%s.ini".
    Format:        "locale_%s.ini",
    // Name of language parameter name in URL. Default is "lang".
    Parameter:    "lang",
    // Redirect when user uses get parameter to specify language. Default is false.
    Redirect:    false,
    // Name that maps into template variable. Default is "i18n".
    TmplName:    "i18n",
}))
// ...
```

## Loading Locale Files

By default, locale files should be put in `conf/locale`:

```
conf/
  |
  |__ locale/
        |
        |__ locale_en-US.ini
        |
        |__ locale_zh-CN.ini
```

## Others

* See [unknwon/i18n](https://github.com/unknwon/i18n) for specification of translation.
* See [Peach Docs](https://github.com/peachdocs/peach) as a study example.


# Data Binding and Validation

Middlware binding provides request data binding and validation for Macaron [Instances](/core_concepts#instances).

* [GitHub](https://github.com/go-macaron/binding)
* [API Reference](https://gowalker.org/github.com/go-macaron/binding)

## Installation

```bash
go get github.com/go-macaron/binding
```

## Usage

### Getting form data from a request

Suppose you have a contact form on your site where at least name and message are required. We'll need a struct to receive the data:

```go
type ContactForm struct {
    Name           string `form:"name" binding:"Required"`
    Email          string `form:"email"`
    Message        string `form:"message" binding:"Required"`
    MailingAddress string `form:"mailing_address"`
}
```

Then we simply add our route in Macaron:

```go
m.Post("/contact/submit", binding.Bind(ContactForm{}), func(contact ContactForm) string {
    return fmt.Sprintf("Name: %s\nEmail: %s\nMessage: %s\nMailing Address: %v",
        contact.Name, contact.Email, contact.Message, contact.MailingAddress)
})
```

That's it! The [`binding.Bind`](https://gowalker.org/github.com/go-macaron/binding#Bind) function takes care of validating required fields.

By default, if there are any errors (like a required field is empty), binding middleware will return an error to the client and your app won't even see the request. To prevent this behavior, you can use [`binding.BindIgnErr`](https://gowalker.org/github.com/go-macaron/binding#BindIgnErr) instead.

{% hint style="danger" %}
Don't try to bind to embedded struct pointers; it won't work. See [martini-contrib/binding issue 30](https://github.com/martini-contrib/binding/issues/30) if you want to help with this.
{% endhint %}

#### Naming Convention

By default, there is one naming convention for form tag name, which are:

* `Name` -> `name`
* `UnitPrice` -> `unit_price`

For example, previous example can be simplified with following code:

```go
type ContactForm struct {
    Name           string `binding:"Required"`
    Email          string
    Message        string `binding:"Required"`
    MailingAddress string
}
```

Clean and neat, isn't it?

If you want to custom your app naming convention, you can use [`binding.SetNameMapper`](https://gowalker.org/github.com/go-macaron/binding#SetNameMapper) function, which accepts a function that is type of [`binding.NameMapper`](https://gowalker.org/github.com/go-macaron/binding#NameMapper).

### Getting JSON data from a request

To get data from JSON payloads, simply use the `json:` struct tags instead of `form:`.

{% hint style="info" %}
Use [JSON-to-Go](http://mholt.github.io/json-to-go/) to correctly convert JSON to a Go type definition. It's useful if you're new to this or the structure is large/complex.
{% endhint %}

### Binding to interfaces

If you'd like to bind the data to an interface rather than to a concrete struct, you can specify the interface and use it like this:

```go
m.Post("/contact/submit", binding.Bind(ContactForm{}, (*MyInterface)(nil)), func(contact MyInterface) {
    // ... your struct became an interface!
})
```

## Description of Handlers

Each of these middleware handlers are independent and optional, though be aware that some handlers invoke other ones.

### Bind

[`binding.Bind`](https://gowalker.org/github.com/go-macaron/binding#Bind) is a convenient wrapper over the other handlers in this package. It does the following boilerplate for you:

1. Deserializes request data into a struct
2. Performs validation with [`binding.Validate`](https://gowalker.org/github.com/go-macaron/binding#Validate)
3. If your struct doesn't implement [`binding.ErrorHandler`](https://gowalker.org/github.com/go-macaron/binding#ErrorHandler), then default error handling will be applied. Otherwise, calls `ErrorHandler.Error` method to perform custom error handling.

Notes:

* Your application (the final handler) will not even see the request if there are any errors when default error handling is applied.
* Header `Content-Type` will be used to know how to deserialize the requests.

{% hint style="danger" %}
Don't attempt to bind a pointer to a struct. This will cause a panic [to prevent a race condition](https://github.com/codegangsta/martini-contrib/pull/34#issuecomment-29683659) where every request would be pointing to the same struct.
{% endhint %}

### Form

[`binding.Form`](https://gowalker.org/github.com/go-macaron/binding#Form) deserializes form data from the request, whether in the query string or as a `form-urlencoded` payload. It only does these things:

1. Deserializes request data into a struct
2. Performs validation with [`binding.Validate`](https://gowalker.org/github.com/go-macaron/binding#Validate)

Note that it does not handle errors. You may receive a [`binding.Errors`](https://gowalker.org/github.com/go-macaron/binding#Errors) into your own handler if you want to handle errors.

### MultipartForm and File Uploads

Like [`binding.Form`](https://gowalker.org/github.com/go-macaron/binding#Form), [`binding.MultipartForm`](https://gowalker.org/github.com/go-macaron/binding#MultipartForm) deserializes form data from a request into the struct you pass in. Additionally, this will deserialize a POST request that has a form of `enctype="multipart/form-data"`. If the bound struct contains a field of type [`*multipart.FileHeader`](http://gowalker.org/pkg/mime/multipart/#FileHeader) (or `[]*multipart.FileHeader`), you also can read any uploaded files that were part of the form.

This handler does the following:

1. Deserializes request data into a struct
2. Performs validation with [`binding.Validate`](https://gowalker.org/github.com/go-macaron/binding#Validate)

Again, like [`binding.Form`](https://gowalker.org/github.com/go-macaron/binding#Form), no error handling is performed, but you can get the errors in your handler by receiving a [`binding.Errors`](https://gowalker.org/github.com/go-macaron/binding#Errors) type.

#### Example

```go
type UploadForm struct {
    Title      string                `form:"title"`
    TextUpload *multipart.FileHeader `form:"txtUpload"`
}

func main() {
    m := macaron.Classic()
    m.Post("/", binding.MultipartForm(UploadForm{}), uploadHandler(uf UploadForm) string {
        file, err := uf.TextUpload.Open()
        // ... you can now read the uploaded file
    })
    m.Run()
}
```

### Json

[`binding.Json`](https://gowalker.org/github.com/go-macaron/binding#Json) deserializes JSON data in the payload of the request. It does the following things:

1. Deserializes request data into a struct
2. Performs validation with [`binding.Validate`](https://gowalker.org/github.com/go-macaron/binding#Validate)

Similar to [`binding.Form`](https://gowalker.org/github.com/go-macaron/binding#Form), no error handling is performed, but you can get the errors and handle them yourself.

### Validate

[`binding.Validate`](https://gowalker.org/github.com/go-macaron/binding#Validate) receives a populated struct and checks it for errors with basic rules. It will execute the `Validator.Validate()` method on the struct, if it is a [`binding.Validator`](https://gowalker.org/github.com/go-macaron/binding#Validator).

#### Validation Rules

There are some builtin validation rules. To use them, the tag format is `binding:"<Name>"`.

| Name               | Note                                                                                         |
| ------------------ | -------------------------------------------------------------------------------------------- |
| `OmitEmpty`        | Omit rest of validations if value is empty                                                   |
| `Required`         | Must be non-zero value                                                                       |
| `AlphaDash`        | Must be alpha characters or numerics or `-_`                                                 |
| `AlphaDashDot`     | Must be alpha characters or numerics, `-_` or `.`                                            |
| `Size(int)`        | Fixed length                                                                                 |
| `MinSize(int)`     | Minimum length                                                                               |
| `MaxSize(int)`     | Maximum length                                                                               |
| `Range(int,int)`   | Value range(inclusive)                                                                       |
| `Email`            | Must be E-mail address                                                                       |
| `Url`              | Must be HTTP/HTTPS URL address                                                               |
| `In(a,b,c,...)`    | Must be one of element in array                                                              |
| `NotIn(a,b,c,...)` | Must not be one of element in array                                                          |
| `Include(string)`  | Must contain                                                                                 |
| `Exclude(string)`  | Must not contain                                                                             |
| `Default(string)`  | Set default value when field is zero-value(cannot use this when bind with interface wrapper) |

To combine multiple rules: `binding:"Required;MinSize(10)"`.

## Customize Operations

### Custom Validation

If you want additional validation beyond just checking required fields, your struct can implement the [`binding.Validator`](https://gowalker.org/github.com/go-macaron/binding#Validator) interface like so:

```go
func (cf ContactForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
    if strings.Contains(cf.Message, "Go needs generics") {
        errs = append(errors, binding.Error{
            FieldNames:     []string{"message"},
            Classification: "ComplaintError",
            Message:        "Go has generics. They're called interfaces.",
        })
    }
    return errs
}
```

Now, any contact form submissions with "Go needs generics" in the message will return an error explaining your folly.

### Custom Validation Rules

If you need to more validation rules that are applied automatically for you, you can add custom rules by function [`binding.AddParamRule`](https://gowalker.org/github.com/go-macaron/binding#AddParamRule), it accepts type [`binding.ParamRule`](https://gowalker.org/github.com/go-macaron/binding#ParamRule) as argument.

Suppose you want to limit minimum value:

```go
binding.AddParamRule(&binding.ParamRule{
    IsMatch: func(rule string) bool {
        return strings.HasPrefix(rule, "Min(")
    },
    IsValid: func(errs binding.Errors, rule, name string, v interface{}) (bool, binding.Errors) {
        num, ok := v.(int)
        if !ok {
            return false, errs
        }
        min, _ := strconv.Atoi(rule[4 : len(rule)-1])
        if num < min {
            errs.Add([]string{name}, "MinimumValue", "Value is too small")
            return false, errs
        }
        return true, errs
    },
})
```

If your rule is simple, you can also use [`binding.AddRule`](https://gowalker.org/github.com/go-macaron/binding#AddRule), it accepts type [`binding.Rule`](https://gowalker.org/github.com/go-macaron/binding#Rule):

```go
binding.AddRule(&binding.Rule{
    IsMatch: func(rule string) bool {
        return rule == "String"
    },
    IsValid: func(errs binding.Errors, name string, v interface{}) (bool, binding.Errors) {
        _, ok := v.(string)
        return ok, errs
    },
})
```

Custom validation rules are applied after builtin rules.

### Custom Error Handler

If you want to avoid default error handle process but still want binding middleware calls handle function for you, your struct can implement the [`binding.ErrorHandler`](https://gowalker.org/github.com/go-macaron/binding#ErrorHandler) interface like so:

```go
func (cf ContactForm) Error(ctx *macaron.Context, errs binding.Errors) {
    // Custom process to handle error.
}
```

This operation happens after your custom validation.


# Cache

Middleware cache provides cache management for Macaron [Instances](/core_concepts#instances).

* [GitHub](https://github.com/go-macaron/cache)
* [API Reference](https://gowalker.org/github.com/go-macaron/cache)

## Installation

```bash
go get github.com/go-macaron/cache
```

## Usage

```go
import (
    "github.com/go-macaron/cache"
    "gopkg.in/macaron.v1"
)

func main() {
    m := macaron.Classic()
    m.Use(cache.Cacher())

    m.Get("/", func(c cache.Cache) string {
        c.Put("cache", "cache middleware", 120)
        return c.Get("cache")
    })

    m.Run()
}
```

## Options

`cache.Cacher` comes with a variety of configuration options([`cache.Options`](https://gowalker.org/github.com/go-macaron/cache#Options)):

```go
//...
m.Use(cache.Cacher(cache.Options{
    // Name of adapter. Default is "memory".
    Adapter:        "memory",
    // Adapter configuration, it's corresponding to adapter.
    AdapterConfig:  "",
    // GC interval time in seconds. Default is 60.
    Interval:       60,
    // Configuration section name. Default is "cache".
    Section:        "cache",
    }))
//...
```

## Adapters

There are 8 built-in implementations of cache adapter, you have to import adapter driver explicitly except for **memory** and **file** adapters.

Following are some basic usage examples for adapters.

### Memory

```go
//...
m.Use(cache.Cacher())
//...
```

### File

```go
//...
m.Use(cache.Cacher(cache.Options{
    Adapter:       "file",
    AdapterConfig: "data/caches",
}))
//...
```

### Redis

**Notice** Only string and int-type are allowed.

```go
import _ "github.com/go-macaron/cache/redis"

//...
m.Use(cache.Cacher(cache.Options{
    Adapter:       "redis",
    // e.g.: network=tcp,addr=127.0.0.1:6379,password=macaron,db=0,pool_size=100,idle_timeout=180,hset_name=MacaronCache,prefix=cache:
    AdapterConfig: "addr=127.0.0.1:6379,password=macaron",
    OccupyMode:    false,
}))
//...
```

There is a special **occupy mode** for Redis cacher when you want to use entire database selection with large amount of cache data. By setting `OccupyMode` to `true` to enable this mode, then cacher will stop maintaining the index collection of cache data that is used to determine what data are belonging to your app, and helps you reduce CPU and memory usage in such cases.

### Memcache

```go
import _ "github.com/go-macaron/cache/memcache"

//...
m.Use(cache.Cacher(cache.Options{
    Adapter:       "memcache",
    // e.g.: 127.0.0.1:9090;127.0.0.1:9091
    AdapterConfig: "127.0.0.1:11211",
}))
//...
```

### PostgreSQL

Use following SQL to create database:

```sql
CREATE TABLE cache (
    key       CHAR(32) NOT NULL,
    data      BYTEA,
    created   INTEGER NOT NULL,
    expire    INTEGER NOT NULL,
    PRIMARY KEY (key)
);
```

```go
import _ "github.com/go-macaron/cache/postgres"

//...
m.Use(cache.Cacher(cache.Options{
    Adapter:       "postgres",
    AdapterConfig: "user=a password=b host=localhost port=5432 dbname=c sslmode=disable",
}))
//...
```

### MySQL

Use following SQL to create database:

```sql
CREATE TABLE `cache` (
    `key`       CHAR(32) NOT NULL,
    `data`      BLOB,
    `created`   INT(11) UNSIGNED NOT NULL,
    `expire`    INT(11) UNSIGNED NOT NULL,
    PRIMARY KEY (`key`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
```

```go
import _ "github.com/go-macaron/cache/mysql"

//...
m.Use(cache.Cacher(cache.Options{
    Adapter:       "mysql",
    AdapterConfig: "username:password@protocol(address)/dbname?param=value",
}))
//...
```

### Ledis

```go
import _ "github.com/go-macaron/cache/ledis"

//...
m.Use(cache.Cacher(cache.Options{
    Adapter:       "ledis",
    AdapterConfig: "data_dir=./app.db,db=0",
}))
//...
```

### Nodb

```go
import _ "github.com/go-macaron/cache/nodb"

//...
m.Use(cache.Cacher(cache.Options{
    Adapter:       "nodb",
    AdapterConfig: "data/cache.db",
}))
//...
```


# Captcha

Middleware captcha provides captcha service for Macaron [Instances](/core_concepts#instances).

* [GitHub](https://github.com/go-macaron/captcha)
* [API Reference](https://gowalker.org/github.com/go-macaron/captcha)

### Installation

```bash
go get github.com/go-macaron/captcha
```

## Usage

{% hint style="info" %}
To use this middleware, you have to register [cache](/middlewares/cache) first.
{% endhint %}

```go
// main.go
import (
    "github.com/go-macaron/cache"
    "github.com/go-macaron/captcha"
    "gopkg.in/macaron.v1"
)

func main() {
    m := macaron.Classic()
    m.Use(cache.Cacher())
    m.Use(captcha.Captchaer())

    m.Get("/", func(ctx *macaron.Context, cpt *captcha.Captcha) string {
        if cpt.VerifyReq(ctx.Req) {
            return "valid captcha"
        }
        return "invalid captcha"
    })

    m.Run()
}
```

```markup
<!-- templates/hello.tmpl -->
{{.Captcha.CreateHtml}}
```

## Options

`captcha.Captchaer` comes with a variety of configuration options:

```go
// ...
m.Use(captcha.Captchaer(captcha.Options{
    // URL prefix of getting captcha pictures. Default is "/captcha/".
    URLPrefix:            "/captcha/",
    // Hidden input element ID. Default is "captcha_id".
    FieldIdName:        "captcha_id",
    // User input value element name in request form. Default is "captcha".
    FieldCaptchaName:    "captcha",
    // Challenge number. Default is 6.
    ChallengeNums:        6,
    // Captcha image width. Default is 240.
    Width:                240,
    // Captcha image height. Default is 80.
    Height:                80,
    // Captcha expiration time in seconds. Default is 600.
    Expiration:            600,
    // Cache key prefix captcha characters. Default is "captcha_".
    CachePrefix:        "captcha_",
}))
// ...
```


# Session

Middleware session provides session management for Macaron [Instances](/core_concepts#instances).

* [GitHub](https://github.com/go-macaron/session)
* [API Reference](https://gowalker.org/github.com/go-macaron/session)

## Installation

```bash
go get github.com/go-macaron/session
```

## Usage

```go
import (
    "github.com/go-macaron/session"
    "gopkg.in/macaron.v1"
)

func main() {
    m := macaron.Classic()
    m.Use(macaron.Renderer())
    m.Use(session.Sessioner())

    m.Get("/", func(sess session.Store) string {
        sess.Set("session", "session middleware")
        return sess.Get("session").(string)
    })

    m.Get("/signup", func(ctx *macaron.Context, f *session.Flash) {
        f.Success("yes!!!")
        f.Error("opps...")
        f.Info("aha?!")
        f.Warning("Just be careful.")
        ctx.HTML(200, "signup")
    })

    m.Run()
}
```

```markup
<!-- templates/signup.tmpl -->
<h2>{{.Flash.SuccessMsg}}</h2>
<h2>{{.Flash.ErrorMsg}}</h2>
<h2>{{.Flash.InfoMsg}}</h2>
<h2>{{.Flash.WarningMsg}}</h2>
```

### Pongo2

If you're using [pongo2](https://github.com/go-macaron/pongo2) as template engine, you will use flash in HTML as follows:

```markup
<!-- templates/signup.tmpl -->
<h2>{{Flash.SuccessMsg}}</h2>
<h2>{{Flash.ErrorMsg}}</h2>
<h2>{{Flash.InfoMsg}}</h2>
<h2>{{Flash.WarningMsg}}</h2>
```

### Output flash in current response

By default, flash will be only used for the next coming response corresponding to the session, but functions `Success`, `Error`, `Info` and `Warning` are all accept a second argument to indicate whether output flash in current response or not.

```go
// ...
f.Success("yes!!!", true)
f.Error("opps...", true)
f.Info("aha?!", true)
f.Warning("Just be careful.", true)
// ...
```

But remember, flash can only be used once no matter which way you use.

## Options

`session.Sessioner` comes with a variety of configuration options([`session.Options`](https://gowalker.org/github.com/go-macaron/session#Options)):

```go
//...
m.Use(session.Sessioner(session.Options{
    // Name of provider. Default is "memory".
    Provider:       "memory",
    // Provider configuration, it's corresponding to provider.
    ProviderConfig: "",
    // Cookie name to save session ID. Default is "MacaronSession".
    CookieName:     "MacaronSession",
    // Cookie path to store. Default is "/".
    CookiePath:     "/",
    // GC interval time in seconds. Default is 3600.
    Gclifetime:     3600,
    // Max life time in seconds. Default is whatever GC interval time is.
    Maxlifetime:    3600,
    // Use HTTPS only. Default is false.
    Secure:         false,
    // Cookie life time. Default is 0.
    CookieLifeTime: 0,
    // Cookie domain name. Default is empty.
    Domain:         "",
    // Session ID length. Default is 16.
    IDLength:       16,
    // Configuration section name. Default is "session".
    Section:        "session",
}))
//...
```

## Providers

There are 9 built-in implementations of session provider, you have to import provider driver explicitly except for **memory** and **file** providers.

Following are some basic usage examples for providers.

### Memory

```go
//...
m.Use(session.Sessioner())
//...
```

### File

```go
//...
m.Use(session.Sessioner(session.Options{
    Provider:       "file",
    ProviderConfig: "data/sessions",
}))
//...
```

### Redis

```go
import _ "github.com/go-macaron/session/redis"

//...
m.Use(session.Sessioner(session.Options{
    Provider:       "redis",
    // e.g.: network=tcp,addr=127.0.0.1:6379,password=macaron,db=0,pool_size=100,idle_timeout=180,prefix=session:
    ProviderConfig: "addr=127.0.0.1:6379,password=macaron",
}))
//...
```

### Memcache

```go
import _ "github.com/go-macaron/session/memcache"

//...
m.Use(session.Sessioner(session.Options{
    Provider:       "memcache",
    // e.g.: 127.0.0.1:9090;127.0.0.1:9091
    ProviderConfig: "127.0.0.1:9090",
}))
//...
```

### PostgreSQL

Use following SQL to create database(make sure `key` length matches your `Options.IDLength`):

```sql
CREATE TABLE session (
    key       CHAR(16) NOT NULL,
    data      BYTEA,
    expiry    INTEGER NOT NULL,
    PRIMARY KEY (key)
);
```

```go
import _ "github.com/go-macaron/session/postgres"

//...
m.Use(session.Sessioner(session.Options{
    Provider:       "postgres",
    ProviderConfig: "user=a password=b host=localhost port=5432 dbname=c sslmode=disable",
}))
//...
```

### MySQL

Use following SQL to create database:

```sql
CREATE TABLE `session` (
    `key`       CHAR(16) NOT NULL,
    `data`      BLOB,
    `expiry`    INT(11) UNSIGNED NOT NULL,
    PRIMARY KEY (`key`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
```

```go
import _ "github.com/go-macaron/session/mysql"

//...
m.Use(session.Sessioner(session.Options{
    Provider:       "mysql",
    ProviderConfig: "username:password@protocol(address)/dbname?param=value",
}))
//...
```

### Couchbase

```go
import _ "github.com/go-macaron/session/couchbase"

//...
m.Use(session.Sessioner(session.Options{
    Provider:       "couchbase",
    ProviderConfig: "username:password@protocol(address)/dbname?param=value",
}))
//...
```

### Ledis

```go
import _ "github.com/go-macaron/session/ledis"

//...
m.Use(session.Sessioner(session.Options{
    Provider:       "ledis",
    ProviderConfig: "data_dir=./app.db,db=0",
}))
//...
```

### Nodb

```go
import _ "github.com/go-macaron/session/nodb"

//...
m.Use(session.Sessioner(session.Options{
    Provider:       "nodb",
    ProviderConfig: "data/cache.db",
}))
//...
```

## Implement Provider Interface

In case you need to have your own implementation of session storage and provider, you can implement following two interfaces and take **memory** provider as a study example.

```go
// RawStore is the interface that operates the session data.
type RawStore interface {
    // Set sets value to given key in session.
    Set(key, value interface{}) error
    // Get gets value by given key in session.
    Get(key interface{}) interface{}
    // Delete deletes a key from session.
    Delete(key interface{}) error
    // ID returns current session ID.
    ID() string
    // Release releases session resource and save data to provider.
    Release() error
    // Flush deletes all session data.
    Flush() error
}

// Provider is the interface that provides session manipulations.
type Provider interface {
    // Init initializes session provider.
    Init(gclifetime int64, config string) error
    // Read returns raw session store by session ID.
    Read(sid string) (RawStore, error)
    // Exist returns true if session with given ID exists.
    Exist(sid string) bool
    // Destory deletes a session by session ID.
    Destory(sid string) error
    // Regenerate regenerates a session store from old session ID to new one.
    Regenerate(oldsid, sid string) (RawStore, error)
    // Count counts and returns number of sessions.
    Count() int
    // GC calls GC to clean expired sessions.
    GC()
}
```


# Cross-Site Request Forgery

Middleware csrf generates and validates CSRF tokens for Macaron [Instances](/core_concepts#instances).

* [GitHub](https://github.com/go-macaron/csrf)
* [API Reference](https://gowalker.org/github.com/go-macaron/csrf)

## Installation

```bash
go get github.com/go-macaron/csrf
```

## Usage

{% hint style="info" %}
To use this middleware, you have to register [session](https://github.com/go-macaron/docs/tree/233fc2726d3f319753c20c620e3d19d6b22a896b/middlewares/session.md) first.
{% endhint %}

```go
package main

import (
    "github.com/go-macaron/csrf"
    "github.com/go-macaron/session"
    "gopkg.in/macaron.v1"
)

func main() {
    m := macaron.Classic()
    m.Use(macaron.Renderer())
    m.Use(session.Sessioner())
    m.Use(csrf.Csrfer())

    // Simulate the authentication of a session.
    // If uid exists redirect to a form that requires CSRF protection.
    m.Get("/", func(ctx *macaron.Context, sess session.Store) {
        if sess.Get("uid") == nil {
            ctx.Redirect("/login")
            return
        }
        ctx.Redirect("/protected")
    })

    // Set uid for the session.
    m.Get("/login", func(ctx *macaron.Context, sess session.Store) {
        sess.Set("uid", 123456)
        ctx.Redirect("/")
    })

    // Render a protected form. Passing a csrf token by calling x.GetToken()
    m.Get("/protected", func(ctx *macaron.Context, sess session.Store, x csrf.CSRF) {
        if sess.Get("uid") == nil {
            ctx.Redirect("/login", 401)
            return
        }

        // Pass token to the protected template.
        ctx.Data["csrf_token"] = x.GetToken()
        ctx.HTML(200, "protected")
    })

    // Apply CSRF validation to route.
    m.Post("/protected", csrf.Validate, func(ctx *macaron.Context, sess session.Store) {
        if sess.Get("uid") != nil {
            ctx.RenderData(200, []byte("You submitted a valid token"))
            return
        }
        ctx.Redirect("/login", 401)
    })

    m.Run()
}
```

```markup
<!-- templates/protected.tmpl -->
<form action="/protected" method="post">
    <input type="hidden" name="_csrf" value="{{.csrf_token}}">
    <button>Submit</button>
</form>
```

## Options

`csrf.Csrfer` comes with a variety of configuration options:

```go
// ...
m.Use(csrf.Csrfer(csrf.Options{
    // The global secret value used to generate Tokens. Default is a random string.
    Secret:        "mysecret",
    // HTTP header used to set and get token. Default is "X-CSRFToken".
    Header:        "X-CSRFToken",
    // Form value used to set and get token. Default is "_csrf".
    Form:        "_csrf",
    // Cookie value used to set and get token. Default is "_csrf".
    Cookie:        "_csrf",
    // Cookie path. Default is "/".
    CookiePath:    "/",
    // Key used for getting the unique ID per user. Default is "uid".
    SessionKey:    "uid",
    // If true, send token via header. Default is false.
    SetHeader:    false,
    // If true, send token via cookie. Default is false.
    SetCookie:  false,
    // Set the Secure flag to true on the cookie. Default is false.
    Secure:     false,
    // Disallow Origin appear in request header. Default is false.
    Origin:     false,
    // The function called when Validate fails. Default is a simple error print.
    ErrorFunc:  func(w http.ResponseWriter) {
        http.Error(w, "Invalid csrf token.", http.StatusBadRequest)
    },
    }))
// ...
```


# Embed Binary Data

Package bindata is a helper module that allows to use in-memory static and template files for Macaron [Instances](/core_concepts#instances).

* [GitHub](https://github.com/go-macaron/bindata)
* [API Reference](https://gowalker.org/github.com/go-macaron/bindata)

### Installation

```bash
go get github.com/go-macaron/bindata
```

## Usage

Using [go-bindata](https://github.com/go-bindata/go-bindata) convert your template and public directories into individual packages.

Import the packages and use them like the example below.

```go
import (
    "path/to/bindata/public"
    "path/to/bindata/templates"
    "github.com/go-macaron/bindata"
)

m.Use(macaron.Static("public",
    macaron.StaticOptions{
        FileSystem: bindata.Static(bindata.Options{
            Asset:      public.Asset,
            AssetDir:   public.AssetDir,
            AssetNames: public.AssetNames,
            Prefix:     "",
        }),
    },
))

m.Use(macaron.Renderer(macaron.RenderOptions{
    TemplateFileSystem: bindata.Templates(bindata.Options{
        Asset:      templates.Asset,
        AssetDir:   templates.AssetDir,
        AssetNames: templates.AssetNames,
        Prefix:     "",
    }),
}))
```


# Serving Multiple Sites

Module switcher provides host switch functionality for [Macaron](https://github.com/go-macaron/macaron).

* [GitHub](https://github.com/go-macaron/switcher)
* [API Reference](https://gowalker.org/github.com/go-macaron/switcher)

## Installation

```bash
go get github.com/go-macaron/switcher
```

## Usage

If you want to run 2 instances in one program, Host Switcher is the feature you're looking for.

```go
func main() {
    m1 := macaron.Classic()
    // Register m1 middlewares and routers.

    m2 := macaron.Classic()
    // Register m2 middlewares and routers.

    hs := switcher.NewHostSwitcher()
    // Set instance corresponding to host address.
    hs.Set("gowalker.org", m1)
    hs.Set("gogs.io", m2)
    hs.Run()
}
```

By default, this program will listen on ports `4000`(for `m1`) and `4001`(for `m2`) in `macaron.DEV` mode just for convenience. And only listen on `4000` in `macaron.PROD` mode.

### Dynamic match

In case you have different subdomains that need only one Macaron instance:

```go
// ...
m := macaron.Classic()
// Register m middlewares and routers.

hs := macaron.NewHostSwitcher()
// Set instance corresponding to host address.
hs.Set("*.example.com", m)
hs.Run()
// ...
```


# FAQs

## How do I integrate with existing servers?

Every Macaron [instance](/core_concepts#instances) implements [`http.Handler`](https://gowalker.org/net/http#Handler), so it can easily be used to serve subtrees on existing Go servers. For example this is a working Macaron app for Google App Engine:

```go
package hello

import (
    "net/http"

    "gopkg.in/macaron.v1"
)

func init() {
    m := macaron.Classic()
    m.Get("/", func() string {
        return "Hello world!"
    })
    http.Handle("/", m)
}
```

## How do I change the port/host?

Macaron's `Run` function looks for the `PORT` and `HOST` environment variables and uses those. Otherwise Macaron will default to [localhost:4000](http://localhost:4000). To have more flexibility over port and host, use the [`http.ListenAndServe`](https://gowalker.org/net/http#ListenAndServe) function instead.

```go
m := macaron.Classic()
// ...
log.Fatal(http.ListenAndServe(":8080", m))
```

Or following ways:

* `m.Run("0.0.0.0")`, listen on `0.0.0.0:4000`
* `m.Run(8080)`, listen on `0.0.0.0:8080`
* `m.Run("0.0.0.0", 8080)`, listen on `0.0.0.0:8080`

## How do I graceful shutdown?

```go
package main

import (
    ...
    "net/http"

    "gopkg.in/macaron.v1"
    "gopkg.in/tylerb/graceful.v1"
)

func main() {
    m := macaron.Classic()

    ...

    mux := http.NewServeMux()
    mux.Handle("/", m)
    graceful.Run(":4000", 60*time.Second, mux)
}
```

## How do I pass data in request-level other than service inject?

There is a field called `Data` with type `map[string]interface{}` in [`*macaron.Context`](https://gowalker.org/github.com/go-macaron/macaron#Context) where you can store and retrieve any type of data. It comes with [`*macaron.Context`](https://gowalker.org/github.com/go-macaron/macaron#Context) so every request is independent.

See example [here](https://github.com/go-macaron/docs/tree/ef61f3e63eebd43a1fcd2e6e4fdb3bffffb4059d/middlewares/routing.md#advanced-routing).

## What's the idea behind this other than Martini?

* Integrate frequently used middlewares and helper methods with less reflection.
* Replace default router with faster multi-tree router.
* Make it much easier to power [Gogs](https://gogs.io) project.
* Make a deep source study against Martini.

## Why Logo is a dragon?

Shouldn't it be some sort of dessert?

The transliteration of Macaron in Chinese is `Maca Long`, `Long` means dragon, so actually the Logo is a dragon whose name is `Maca`. Hah!

## Live code reload?

[Bra](https://github.com/unknwon/bra) is the prefect fit for live reloading Macaron and other apps.


# 简体中文

Macaron 是一个具有高生产力和模块化设计的 Go Web 框架。框架秉承了 [Martini](https://github.com/go-martini/martini) 的基本思想，并在此基础上做出高级扩展。

{% hint style="info" %}
Go 语言的最低版本要求为 **1.6**。
{% endhint %}

## 尝鲜体验

安装 Macaron：

```
go get gopkg.in/macaron.v1
```

Macaron 的初级用法：

```go
package main

import "gopkg.in/macaron.v1"

func main() {
    m := macaron.Classic()
    m.Get("/", func() string {
        return "Hello world!"
    })
    m.Run()
}
```

## 主要特性

* 支持子路由的强大路由设计
* 支持灵活多变的路由组合
* 支持无限路由组的无限嵌套
* 支持直接集成现有的服务
* 支持运行时动态设置需要渲染的模板集
* 支持使用内存文件作为静态资源和模板文件
* 支持对模块的轻松接入与解除
* 采用 [inject](https://github.com/codegangsta/inject) 提供的便利的依赖注入
* 采用更好的路由层和更少的反射来提升执行速度

## 使用案例

* [Gogs](https://gogs.io): A painless self-hosted Git Service
* [Grafana](http://grafana.org/): The open source analytics & monitoring solution for every database
* [Peach Docs](https://peachdocs.org): A modern documentation web server
* [Go Walker](https://gowalker.org): Go online API documentation
* [Intel Stack](https://intelstack.com/): A 100% free intelligence marketplace

## 快速导航

* 刚开始了解 Macaron 的话，不妨从 [初学者指南](/zh-cn/starter_guide) 看起。
* Macaron 已经拥有许多 [中间件和辅助模块](/zh-cn/middlewares) 来简化您的工作。
* 如果您有任何问题，建议先从 [常见问题](/zh-cn/faqs) 中寻找答案。
* 如果您觉得文档有描述得不够清楚之处，请通过 [提交工单](https://github.com/go-macaron/docs/issues) 告知我们。


# 初学者指南

在我们开始之前，必须明确的一点就是，文档不会教授您任何有关 Go 语言的基础知识。所有对 Macaron 使用的讲解均是基于您已有的知识基础上展开的。

通过执行以下命令来安装 Macaron：

```bash
go get gopkg.in/macaron.v1
```

并且可以在今后使用以下命令来升级 Macaron：

```bash
go get -u gopkg.in/macaron.v1
```

## 最简示例

创建一个名为 `main.go` 的文件，然后输入以下代码：

```go
package main

import "gopkg.in/macaron.v1"

func main() {
    m := macaron.Classic()
    m.Get("/", func() string {
        return "Hello world!"
    })
    m.Run()
}
```

函数 [`macaron.Classic`](https://gowalker.org/gopkg.in/macaron.v1#Classic) 创建并返回一个 [经典 Macaron](/zh-cn/core_concepts#jing-dian-macaron) 实例。

方法 [`m.Get`](https://gowalker.org/gopkg.in/macaron.v1#Router_Get) 是用于注册针对 HTTP GET 请求的路由。在本例中，我们注册了针对根路径 `/` 的路由，并提供了一个 [处理器](/zh-cn/core_concepts#chu-li-qi) 函数来进行简单的处理操作，即返回内容为 `Hello world!` 的字符串作为响应。

您可能会问，为什么处理器函数可以返回一个字符串作为响应？这是由于 [返回值](/zh-cn/core_concepts#fan-hui-zhi) 所带来的特性。换句话说，我们在本例中使用了 Macaron 中处理器的一个特殊语法来将返回值作为响应内容。

最后，我们调用 [`m.Run`](https://gowalker.org/gopkg.in/macaron.v1#Macaron_Run) 方法来让服务器启动。在默认情况下，[Macaron 实例](/zh-cn/core_concepts#macaron-shi-li) 会监听 `0.0.0.0:4000`。

接下来，就可以执行命令 `go run main.go` 运行程序。您应该在程序启动后看到一条日志信息：

```bash
[Macaron] listening on 0.0.0.0:4000 (development)
```

现在，打开您的浏览器然后访问 [localhost:4000](http://localhost:4000)。您会发现，一切是如此的美好！

## 扩展示例

现在，让我们对 `main.go` 做出一些修改，以便进行更多的练习。

```go
package main

import (
    "log"
    "net/http"

    "gopkg.in/macaron.v1"
)

func main() {
    m := macaron.Classic()
    m.Get("/", myHandler)

    log.Println("Server is running...")
    log.Println(http.ListenAndServe("0.0.0.0:4000", m))
}

func myHandler(ctx *macaron.Context) string {
    return "the request path is: " + ctx.Req.RequestURI
}
```

当您再次执行命令 `go run main.go` 运行程序的时候，您会看到屏幕上显示的内容为 `the request path is: /`。

那么，是什么改变了事物原本的样貌？（答：爱情）

首先，我们依旧使用了 [经典 Macaron](/zh-cn/core_concepts#jing-dian-macaron) 来为根路径 `/` 注册针对 HTTP GET 请求的路由。但我们不再使用匿名函数，而是改用名为 `myHandler` 的函数作为处理器。需要注意的是，注册路由时，不需要在函数名称后面加上括号，因为我们不需要在此时调用这个函数。

函数 `myHandler` 接受一个类型为 [`*macaron.Context`](https://github.com/go-macaron/docs/tree/703e4dc1df0a6d4be8f8defbf1bb382fd8c90009/zh-CN/middlewares/core_services.md#qing-qiu-shang-xia-wen-context) 的参数，并返回一个字符串。您可能已经发现我们并没有告诉 Macaron 需要传递什么参数给处理器，而且当您查看 [`m.Get`](https://gowalker.org/gopkg.in/macaron.v1#Router_Get) 方法的声明时会发现，Macaron 实际上将所有的处理器（[`macaron.Handler`](https://gowalker.org/gopkg.in/macaron.v1#Handler)）都当作类型 `interface{}` 来处理。那么，Macaron 又是怎么知道需要传递什么参数来调用处理器并执行逻辑的呢？

这就涉及到 [服务注入](/zh-cn/core_concepts#fu-wu-zhu-ru) 的概念了， [`*macaron.Context`](https://github.com/go-macaron/docs/tree/703e4dc1df0a6d4be8f8defbf1bb382fd8c90009/zh-CN/middlewares/core_services.md#qing-qiu-shang-xia-wen-context) 就是默认注入的服务之一，所以您可以直接使用它作为参数。如果您不明白怎么注入您自己的服务，没关系，反正还不是时候知道这些。

和之前的例子一样，我们需要让服务器监听在某个地址上。这一次，我们使用 Go 标准库的函数 [`http.ListenAndServe`](https://gowalker.org/net/http#ListenAndServe) 来完成这项操作。如此一来，您便可以发现，任一 [Macaron 实例](/zh-cn/core_concepts#macaron-shi-li) 都是和标准库完全兼容的。

## 了解更多

您现在已经知道怎么基于 Macaron 来书写简单的代码，请尝试修改上文中的两个示例，并确保您已经完全理解上文中的所有内容。

当您觉得自己已经原地满血复活后，就可以继续学习之后的内容了。


# 核心概念

## 经典 Macaron

为了更快速的启用 Macaron，[`macaron.Classic`](https://gowalker.org/gopkg.in/macaron.v1#Classic) 提供了一些默认的组件以方便 Web 开发:

```go
m := macaron.Classic()
// ... 可以在这里使用中间件和注册路由
m.Run()
```

下面是 [`macaron.Classic`](https://gowalker.org/gopkg.in/macaron.v1#Classic) 已经包含的功能：

* 请求/响应日志 - [`macaron.Logger`](/zh-cn/core_services#lu-you-ri-zhi)
* 容错恢复 - [`macaron.Recovery`](/zh-cn/core_services#rong-cuo-hui-fu)
* 静态文件服务 - [`macaron.Static`](/zh-cn/core_services#jing-tai-wen-jian)

## Macaron 实例

任何类型为 [`macaron.Macaron`](https://gowalker.org/gopkg.in/macaron.v1#Macaron) 的对象都可以被认为是 Macaron 的实例，您可以在单个程序中使用任意数量的 Macaron 实例。

## 处理器

处理器是 Macaron 的灵魂和核心所在. 一个处理器基本上可以是任何的函数:

```go
m.Get("/", func() string {
    return "hello world"
})
```

如果想要将同一个函数作用于多个路由，则可以使用一个命名函数：

```go
m.Get("/", myHandler)
m.Get("/hello", myHandler)

func myHandler() string {
    return "hello world"
}
```

除此之外，同一个路由还可以注册任意多个处理器：

```go
m.Get("/", myHandler1, myHandler2)

func myHandler1() {
    // ... 处理内容
}

func myHandler2() string {
    return "hello world"
}
```

### 返回值

当一个处理器返回结果的时候, Macaron 将会把返回值作为字符串写入到当前的 [`http.ResponseWriter`](http://gowalker.org/net/http#ResponseWriter) 里面：

```go
m.Get("/", func() string {
    return "hello world" // HTTP 200 : "hello world"
})

m.Get("/", func() *string {
    str := "hello world"
    return &str // HTTP 200 : "hello world"
})

m.Get("/", func() []byte {
    return []byte("hello world") // HTTP 200 : "hello world"
})

m.Get("/", func() error {
    // 返回 nil 则什么都不会发生
    return nil 
}, func() error {
    // ... 得到了错误
    return err // HTTP 500 : <错误消息>
})
```

另外你也可以选择性的返回状态码（仅适用于 `string` 和 `[]byte` 类型）:

```go
m.Get("/", func() (int, string) {
    return 418, "i'm a teapot" // HTTP 418 : "i'm a teapot"
})

m.Get("/", func() (int, *string) {
    str := "i'm a teapot"
    return 418, &str // HTTP 418 : "i'm a teapot"
})

m.Get("/", func() (int, []byte) {
    return 418, []byte("i'm a teapot") // HTTP 418 : "i'm a teapot"
})
```

### 服务注入

处理器是通过反射来调用的，Macaron 通过 [依赖注入](http://baike.baidu.com/view/1486379.htm?from_id=5177233\&type=syn\&fromtitle=%E4%BE%9D%E8%B5%96%E6%B3%A8%E5%85%A5\&fr=aladdin) 来为处理器注入参数列表。 **这样使得 Macaron 与 Go 语言的** [**`http.HandlerFunc`**](https://gowalker.org/net/http#HandlerFunc) **接口完全兼容**。

如果你加入一个参数到你的处理器, Macaron 将会搜索它参数列表中的服务，并且通过类型判断来解决依赖关系：

```go
m.Get("/", func(resp http.ResponseWriter, req *http.Request) {
    // resp 和 req 是由 Macaron 默认注入的服务
    resp.WriteHeader(200) // HTTP 200
})
```

在您的代码中最常用的服务应该是 [`*macaron.Context`](/zh-cn/core_services#qing-qiu-shang-xia-wen-context)：

```go
m.Get("/", func(ctx *macaron.Context) {
    ctx.Resp.WriteHeader(200) // HTTP 200
})
```

下面的这些服务已经被包含在经典 Macaron 中（[`macaron.Classic`](https://gowalker.org/gopkg.in/macaron.v1#Classic)）：

* [`*macaron.Context`](/zh-cn/core_services#qing-qiu-shang-xia-wen-context) - HTTP 请求上下文
* [`*log.Logger`](/zh-cn/core_services#quan-ju-ri-zhi) - Macaron 全局日志器
* [`http.ResponseWriter`](/zh-cn/core_services#xiang-ying-liu) - HTTP 响应流
* [`*http.Request`](/zh-cn/core_services#qing-qiu-dui-xiang) - HTTP 请求对象

### 中间件机制

中间件处理器是工作于请求和路由之间的。本质上来说和 Macaron 其他的处理器没有分别. 您可以使用如下方法来添加一个中间件处理器到队列中:

```go
m.Use(func() {
  // 处理中间件事务
})
```

你可以通过 `Handlers` 函数对中间件队列实现完全的控制. 它将会替换掉之前的任何设置过的处理器:

```go
m.Handlers(
    Middleware1,
    Middleware2,
    Middleware3,
)
```

中间件处理器可以非常好处理一些功能，包括日志记录、授权认证、会话（sessions）处理、错误反馈等其他任何需要在发生在 HTTP 请求之前或者之后的操作:

```go
// 验证一个 API 密钥
m.Use(func(ctx *macaron.Context) {
    if ctx.Req.Header.Get("X-API-KEY") != "secret123" {
        ctx.Resp.WriteHeader(http.StatusUnauthorized)
    }
})
```

## Macaron 环境变量

一些 Macaron 处理器依赖 `macaron.Env` 全局变量为开发模式和部署模式表现出不同的行为，不过更建议使用环境变量 `MACARON_ENV=production` 来指示当前的模式为部署模式。

## 处理器工作流

![](/files/-Lr_s1cEu-hRvvppo-El)


# 核心服务

Macaron 会注入一些默认服务来驱动您的应用，这些服务被称之为 **核心服务**。也就是说，您可以直接使用它们作为处理器参数而不需要任何附加工作。

## 请求上下文（Context）

该服务通过类型 [`*macaron.Context`](https://gowalker.org/gopkg.in/macaron.v1#Context) 来体现。这是 Macaron 最为核心的服务，您的任何操作都是基于它之上。该服务包含了您所需要的请求对象、响应流、模板引擎接口、数据存储和注入与获取其它服务。

使用方法：

```go
package main

import "gopkg.in/macaron.v1"

func Home(ctx *macaron.Context) {
    // ...
}
```

### Next()

方法 [`Context.Next`](https://gowalker.org/gopkg.in/macaron.v1#Context_Next) 是一个可选的功能，它可以用于中间件处理器暂时放弃执行，等待其他的处理器都执行完毕后继续执行。这样就可以很好的处理在 HTTP 请求完成后需要做的操作：

```go
// log before and after a request
m.Use(func(ctx *macaron.Context, log *log.Logger){
    log.Println("before a request")

    ctx.Next()

    log.Println("after a request")
})
```

### Cookie

最基本的 Cookie 用法：

* [`*macaron.Context.SetCookie`](https://gowalker.org/gopkg.in/macaron.v1#Context_SetCookie)
* [`*macaron.Context.GetCookie`](https://gowalker.org/gopkg.in/macaron.v1#Context_GetCookie)、[`*macaron.Context.GetCookieInt`](https://gowalker.org/gopkg.in/macaron.v1#Context_GetCookieInt)、[`*macaron.Context.GetCookieInt64`](https://gowalker.org/gopkg.in/macaron.v1#Context_GetCookieInt64)、[`*macaron.Context.GetCookieFloat64`](https://gowalker.org/gopkg.in/macaron.v1#Context_GetCookieFloat64)

使用方法：

```go
// ...
m.Get("/set", func(ctx *macaron.Context) {
    ctx.SetCookie("user", "Unknwon", 1)
})

m.Get("/get", func(ctx *macaron.Context) string {
    return ctx.GetCookie("user")
})
// ...
```

使用以下顺序的参数来设置更多的属性：`SetCookie(<name>, <value>, <max age>, <path>, <domain>, <secure>, <http only>)`。

因此，设置 Cookie 最完整的用法为：`SetCookie("user", "unknwon", 999, "/", "localhost", true, true)`。

需要注意的是，参数的顺序是固定的。

如果需要更加安全的 Cookie 机制，可以先使用 [`macaron.SetDefaultCookieSecret`](https://gowalker.org/gopkg.in/macaron.v1#Macaron_SetDefaultCookieSecret) 设定密钥，然后使用：

* [`*macaron.Context.SetSecureCookie`](https://gowalker.org/gopkg.in/macaron.v1#Context_SetSecureCookie)
* [`*macaron.Context.GetSecureCookie`](https://gowalker.org/gopkg.in/macaron.v1#Context_GetSecureCookie)

这两个方法将会自动使用您设置的默认密钥进行加密/解密 Cookie 值。

使用方法：

```go
// ...
m.SetDefaultCookieSecret("macaron")
m.Get("/set", func(ctx *macaron.Context) {
    ctx.SetSecureCookie("user", "Unknwon", 1)
})

m.Get("/get", func(ctx *macaron.Context) string {
    name, _ := ctx.GetSecureCookie("user")
    return name
})
// ...
```

对于那些对安全性要求特别高的应用，可以为每次设置 Cookie 使用不同的密钥加密/解密：

* [`*macaron.Context.SetSuperSecureCookie`](https://gowalker.org/gopkg.in/macaron.v1#Context_SetSuperSecureCookie)
* [`*macaron.Context.GetSuperSecureCookie`](https://gowalker.org/gopkg.in/macaron.v1#Context_GetSuperSecureCookie)

使用方法：

```go
// ...
m.Get("/set", func(ctx *macaron.Context) {
    ctx.SetSuperSecureCookie("macaron", "user", "Unknwon", 1)
})

m.Get("/get", func(ctx *macaron.Context) string {
    name, _ := ctx.GetSuperSecureCookie("macaron", "user")
    return name
})
// ...
```

### 其它辅助方法

* 设置/获取 URL 参数：[`ctx.SetParams`](https://gowalker.org/gopkg.in/macaron.v1#Context_SetParams) / [`ctx.Params`](https://gowalker.org/gopkg.in/macaron.v1#Context_Params)、[`ctx.ParamsEscape`](https://gowalker.org/gopkg.in/macaron.v1#Context_ParamsEscape)、[`ctx.ParamsInt`](https://gowalker.org/gopkg.in/macaron.v1#Context_ParamsInt)、[`ctx.ParamsInt64`](https://gowalker.org/gopkg.in/macaron.v1#Context_ParamsInt64)、[`ctx.ParamsFloat64`](https://gowalker.org/gopkg.in/macaron.v1#Context_ParamsFloat64)
* 获取查询参数：[`ctx.Query`](https://gowalker.org/gopkg.in/macaron.v1#Context_ctx.Query)、[`ctx.QueryEscape`](https://gowalker.org/gopkg.in/macaron.v1#Context_ctx.QueryEscape)、[`ctx.QueryInt`](https://gowalker.org/gopkg.in/macaron.v1#Context_ctx.QueryInt)、[`ctx.QueryInt64`](https://gowalker.org/gopkg.in/macaron.v1#Context_ctx.QueryInt64)、[`ctx.QueryFloat64`](https://gowalker.org/gopkg.in/macaron.v1#Context_ctx.QueryFloat64)、[`ctx.QueryStrings`](https://gowalker.org/gopkg.in/macaron.v1#Context_ctx.QueryStrings)、[`ctx.QueryTrim`](https://gowalker.org/gopkg.in/macaron.v1#Context_QueryTrim)
* 服务内容或文件：[`ctx.ServeContent`](https://gowalker.org/gopkg.in/macaron.v1#Context_ServeContent)、[`ctx.ServeFile`](https://gowalker.org/gopkg.in/macaron.v1#Context_ServeFile)、[`ctx.ServeFile`](https://gowalker.org/gopkg.in/macaron.v1#Context_ServeFile), [`ctx.ServeFileContent`](https://gowalker.org/gopkg.in/macaron.v1#Context_ServeFileContent)
* 获取远程 IP 地址：[`ctx.RemoteAddr`](https://gowalker.org/gopkg.in/macaron.v1#Context_RemoteAddr)

## 路由日志

该服务可以通过函数 [`macaron.Logger`](https://gowalker.org/gopkg.in/macaron.v1#Logger) 来注入。该服务主要负责应用的路由日志。

使用方法：

```go
package main

import "gopkg.in/macaron.v1"

func main() {
    m := macaron.New()
    m.Use(macaron.Logger())
    // ...
}
```

{% hint style="info" %}
当您使用 [`macaron.Classic`](https://gowalker.org/gopkg.in/macaron.v1#Classic) 时，该服务会被自动注入。
{% endhint %}

从 [Peach Docs](https://github.com/peachdocs/peach) 项目中提取的样例输出：

```
[Macaron] Started GET /docs/middlewares/core.html for [::1]
[Macaron] Completed /docs/middlewares/core.html 200 OK in 2.114956ms
```

## 容错恢复

该服务可以通过函数 [`macaron.Recovery`](https://gowalker.org/gopkg.in/macaron.v1#Recovery) 来注入。该服务主要负责在应用发生恐慌（panic）时进行恢复。

使用方法：

```go
package main

import "gopkg.in/macaron.v1"

func main() {
    m := macaron.New()
    m.Use(macaron.Recovery())
    // ...
}
```

{% hint style="info" %}
当您使用 [`macaron.Classic`](https://gowalker.org/gopkg.in/macaron.v1#Classic) 时，该服务会被自动注入。
{% endhint %}

## 静态文件

该服务可以通过函数 [`macaron.Static`](https://gowalker.org/gopkg.in/macaron.v1#Static) 来注入。该服务主要负责应用静态资源的服务，当您的应用拥有多个静态目录时，可以对其进行多次注入。

使用方法：

```go
package main

import "gopkg.in/macaron.v1"

func main() {
    m := macaron.New()
    m.Use(macaron.Static("public"))
    m.Use(macaron.Static("assets"))
    // ...
}
```

{% hint style="info" %}
当您使用 [`macaron.Classic`](https://gowalker.org/gopkg.in/macaron.v1#Classic) 时，该服务会以 `public` 为静态目录被自动注入。
{% endhint %}

默认情况下，当您请求一个目录时，该服务不会列出目录下的文件，而是去寻找 `index.html` 文件。

从 [Peach](https://github.com/peachdocs/peach) 项目中提取的样例输出：

```
[Macaron] Started GET /css/prettify.css for [::1]
[Macaron] [Static] Serving /css/prettify.css
[Macaron] Completed /css/prettify.css 304 Not Modified in 97.584us
[Macaron] Started GET /imgs/macaron.png for [::1]
[Macaron] [Static] Serving /imgs/macaron.png
[Macaron] Completed /imgs/macaron.png 304 Not Modified in 123.211us
[Macaron] Started GET /js/gogsweb.min.js for [::1]
[Macaron] [Static] Serving /js/gogsweb.min.js
[Macaron] Completed /js/gogsweb.min.js 304 Not Modified in 47.653us
[Macaron] Started GET /css/main.css for [::1]
[Macaron] [Static] Serving /css/main.css
[Macaron] Completed /css/main.css 304 Not Modified in 42.58us
```

### 使用示例

假设您的应用拥有以下目录结构：

```
public/
    |__ html
            |__ index.html
    |__ css/
            |__ main.css
```

响应结果：

| 请求 URL            | 匹配文件       |
| ----------------- | ---------- |
| `/html/main.html` | 匹配失败       |
| `/html/`          | index.html |
| `/css/main.css`   | main.css   |

### 自定义选项

该服务允许接受第二个参数来进行自定义选项操作（[`macaron.StaticOptions`](https://gowalker.org/gopkg.in/macaron.v1#StaticOptions)）：

```go
package main

import "gopkg.in/macaron.v1"

func main() {
    m := macaron.New()
    m.Use(macaron.Static("public",
        macaron.StaticOptions{
            // 请求静态资源时的 URL 前缀，默认没有前缀
            Prefix: "public",
            // 禁止记录静态资源路由日志，默认为不禁止记录
            SkipLogging: true,
            // 当请求目录时的默认索引文件，默认为 "index.html"
            IndexFile: "index.html",
            // 用于返回自定义过期响应头，默认为不设置
            // https://developers.google.com/speed/docs/insights/LeverageBrowserCaching
            Expires: func() string { 
                return time.Now().Add(24 * 60 * time.Minute).UTC().Format("Mon, 02 Jan 2006 15:04:05 GMT")
            },
        }))
    // ...
}
```

### 注册多个静态处理器

如果您需要一次注册多个静态处理器，可以使用方法 [`macaron.Statics`](https://gowalker.org/gopkg.in/macaron.v1#Statics) 来简化您的工作。

使用方法：

```go
// ...
m.Use(macaron.Statics(macaron.StaticOptions{}, "public", "views"))
// ...
```

这样，就可以同时注册 `public` 和 `views` 为静态目录了。

## 其它服务

### 全局日志

该服务通过类型 [`*log.Logger`](http://gowalker.org/log#Logger) 来体现。该服务为可选，只是为没有日志器的应用提供一定的便利。

使用方法：

```go
package main

import (
    "log"

    "gopkg.in/macaron.v1"
)

func main() {
    m := macaron.Classic()
    m.Get("/", myHandler)
    m.Run()
}

func myHandler(ctx *macaron.Context, logger *log.Logger) string {
    logger.Println("the request path is: " + ctx.Req.RequestURI)
    return "the request path is: " + ctx.Req.RequestURI
}
```

{% hint style="info" %}
所有 [Macaron 实例](/zh-cn/core_concepts#macaron-shi-li) 都会自动注册该服务。
{% endhint %}

### 响应流

该服务通过类型 [`http.ResponseWriter`](http://gowalker.org/net/http#ResponseWriter) 来体现。该服务为可选，一般情况下可直接使用 `*macaron.Context.Resp`。

使用方法：

```go
package main

import (
    "gopkg.in/macaron.v1"
)

func main() {
    m := macaron.Classic()
    m.Get("/", myHandler)
    m.Run()
}

func myHandler(ctx *macaron.Context) {
    ctx.Resp.Write([]byte("the request path is: " + ctx.Req.RequestURI))
}
```

{% hint style="info" %}
所有 [Macaron 实例](/zh-cn/core_concepts#macaron-shi-li) 都会自动注册该服务。
{% endhint %}

### 请求对象

该服务通过类型 [`*http.Request`](http://gowalker.org/net/http#Request) 来体现。该服务为可选，一般情况下可直接使用 `*macaron.Context.Req`。

除此之外，该服务还提供了 3 个便利的方法来获取请求体：

* [`*macaron.Context.Req.Body().String()`](https://gowalker.org/gopkg.in/macaron.v1#RequestBody_String)：获取 `string` 类型的请求体
* [`*macaron.Context.Req.Body().Bytes()`](https://gowalker.org/gopkg.in/macaron.v1#RequestBody_Bytes)：获取 `[]byte` 类型的请求体
* [`*macaron.Context.Req.Body().ReadCloser()`](https://gowalker.org/gopkg.in/macaron.v1#RequestBody_ReadCloser)：获取 `io.ReadCloser` 类型的请求体

使用方法：

```go
package main

import (
    "gopkg.in/macaron.v1"
)

func main() {
    m := macaron.Classic()
    m.Get("/body1", func(ctx *macaron.Context) {
        reader, err := ctx.Req.Body().ReadCloser()
        // ...
    })
    m.Get("/body2", func(ctx *macaron.Context) {
        data, err := ctx.Req.Body().Bytes()
        // ...
    })
    m.Get("/body3", func(ctx *macaron.Context) {
        data, err := ctx.Req.Body().String()
        // ...
    })
    m.Run()
}
```

需要注意的是，请求体在每个请求中只能被读取一次。

有时您需要传递类型为 [`*http.Request`](http://gowalker.org/net/http#Request) 的参数，则应该使用 `*macaron.Context.Req.Request`。

{% hint style="info" %}
所有 [Macaron 实例](/zh-cn/core_concepts#macaron-shi-li) 都会自动注册该服务。
{% endhint %}


# 自定义服务

服务即是被注入到处理器中的参数. 你可以映射一个服务到 **全局** 或者 **请求** 的级别.

## 全局映射

因为 Macaron 实现了 [`inject.Injector`](https://gowalker.org/github.com/go-macaron/macaron/inject#Injector) 的接口, 那么映射一个服务就变得非常简单:

```go
db := &MyDatabase{}
m := macaron.Classic()
m.Map(db) // Service will be available to all handlers as *MyDatabase
m.Get("/", func(db *MyDatabase) {
    // Operations with db.
})
m.Run()
```

## 请求级别的映射

映射在请求级别的服务可以通过 [`*macaron.Context`](https://gowalker.org/github.com/go-macaron/macaron#Context) 来完成:

```go
func MyCustomLoggerHandler(ctx *macaron.Context) {
    logger := &MyCustomLogger{ctx.Req}
    ctx.Map(logger) // mapped as *MyCustomLogger
}

func main() {
    //...
    m.Get("/", MyCustomLoggerHandler, func(logger *MyCustomLogger) {
        // Operations with logger.
    })
    m.Get("/panic", func(logger *MyCustomLogger) {
        // This will panic because no logger service maps to this request.
    })
    //...
}
```

## 映射值到接口

关于服务最强悍的地方之一就是它能够映射服务到接口. 例如说, 假设你想要覆盖 [`http.ResponseWriter`](http://gowalker.org/net/http#ResponseWriter) 成为一个对象, 那么你可以封装它并包含你自己的额外操作, 你可以如下这样来编写你的处理器:

```go
func WrapResponseWriter(ctx *macaron.Context) {
    rw := NewSpecialResponseWriter(ctx.Resp)
    // override ResponseWriter with our wrapper ResponseWriter
    ctx.MapTo(rw, (*http.ResponseWriter)(nil)) 
}
```

如此一来，您不仅可以修改自定义的实现而不对客户代码做任何修改，还可以允许对于相同类型的服务使用多种实现。


# 中间件和辅助模块

中间件及辅助模块允许您轻易地对模块的进行接入到您的 Macaron 应用中。

现在已经有许多 [中间件和辅助模块](https://github.com/go-macaron) 来简化您的工作：

* [auth](https://github.com/go-macaron/auth) - HTTP 基本认证
* [authz](https://github.com/go-macaron/authz) - ACL、RBAC 和 ABAC 的权限管理，基于 [Casbin](https://github.com/casbin/casbin)
* [bindata](/zh-cn/middlewares/bindata) - 嵌入二进制数据作为静态资源和模板文件
* [binding](/zh-cn/middlewares/binding) - 请求数据绑定和校验
* [cache](/zh-cn/middlewares/cache) - Cache 管理器
* [captcha](/zh-cn/middlewares/captcha) - 验证码服务
* [csrf](/zh-cn/middlewares/csrf) - 生成和验证 CSRF 令牌
* [gzip](/zh-cn/middlewares/gzip) - Gzip 压缩所有响应
* [i18n](/zh-cn/middlewares/i18n) - 国际化与本地化
* [inject](https://github.com/go-macaron/inject) - 映射和注入依赖
* [jade](https://github.com/go-macaron/jade) - Jade 模板引擎
* [method](https://github.com/go-macaron/method) - HTTP 方法覆盖
* [oauth2](https://github.com/go-macaron/oauth2) - OAuth 2.0 后端客户端
* [permissions2](https://github.com/xyproto/permissions2) - Cookies、多用户和权限管理
* [pongo2](https://github.com/go-macaron/pongo2) - Pongo2 模板引擎
* [renders](https://github.com/go-macaron/renders) - 类 Beego 模板引擎（Macaron 已有内置模板引擎，此为可选）
* [session](/zh-cn/middlewares/session) - Session 管理器
* [sockets](https://github.com/go-macaron/sockets) - WebSockets 管道绑定
* [switcher](/zh-cn/middlewares/switcher) - 多站点支持
* [toolbox](https://github.com/go-macaron/toolbox) - 健康检查、性能调试和路由统计等服务

## 注册中间件的最佳顺序

有些中间件会依赖其它中间件，以下为最佳的注册顺序列表：

1. `macaron.Logger()`
2. `macaron.Recovery()`
3. `gzip.Gziper()`
4. `macaron.Static()`
5. `macaron.Renderer()`/`pongo2.Pongoer()`
6. `i18n.I18n()`
7. `cache.Cacher()`
8. `captcha.Captchaer()`
9. `session.Sessioner()`
10. `csrf.Csrfer()`
11. `toolbox.Toolboxer()`


# 路由模块

在 Macaron 中, 路由是一个 HTTP 方法配对一个 URL 匹配模型. 每一个路由可以对应一个或多个处理器方法:

```go
m.Get("/", func() {
    // show something
})

m.Patch("/", func() {
    // update something
})

m.Post("/", func() {
    // create something
})

m.Put("/", func() {
    // replace something
})

m.Delete("/", func() {
    // destroy something
})

m.Options("/", func() {
    // http options
})

m.Any("/", func() {
    // do anything
})

m.Route("/", "GET,POST", func() {
    // combine something
})

m.Combo("/").
    Get(func() string { return "GET" }).
    Patch(func() string { return "PATCH" }).
    Post(func() string { return "POST" }).
    Put(func() string { return "PUT" }).
    Delete(func() string { return "DELETE" }).
    Options(func() string { return "OPTIONS" }).
    Head(func() string { return "HEAD" })

m.NotFound(func() {
    // 自定义 404 处理逻辑
})
```

几点说明：

* 路由匹配的顺序是按照他们被定义的顺序执行的，
* ...但是，匹配范围较小的路由优先级比匹配范围大的优先级高（详见 **匹配优先级**）。
* 最先被定义的路由将会首先被用户请求匹配并调用。

在一些时候，每当 GET 方法被注册的时候，都会需要注册一个一模一样的 HEAD 方法。为了达到减少代码的目的，您可以使用一个名为 [`SetAutoHead`](https://gowalker.org/gopkg.in/macaron.v1#Router_SetAutoHead) 的方法来协助您自动注册：

```go
m := New()
m.SetAutoHead(true)
m.Get("/", func() string {
    return "GET"
}) // 路径 "/" 的 HEAD 也已经被自动注册
```

如果您想要使用子路径但让路由代码保持简洁，可以调用 `m.SetURLPrefix(suburl)`。

## 命名参数

路由模型可能包含参数列表, 可以通过 [`*Context.Params`](https://gowalker.org/gopkg.in/macaron.v1#Context_Params) 来获取:

### 占位符

使用一个特定的名称来代表路由的某个部分：

```go
m.Get("/hello/:name", func(ctx *macaron.Context) string {
    return "Hello " + ctx.Params(":name")
})

m.Get("/date/:year/:month/:day", func(ctx *macaron.Context) string {
    return fmt.Sprintf("Date: %s/%s/%s", ctx.Params(":year"), ctx.Params(":month"), ctx.Params(":day"))
})
```

当然，想要偷懒的时候可以将 `:` 前缀去掉：

```go
m.Get("/hello/:name", func(ctx *macaron.Context) string {
    return "Hello " + ctx.Params("name")
})

m.Get("/date/:year/:month/:day", func(ctx *macaron.Context) string {
    return fmt.Sprintf("Date: %s/%s/%s", ctx.Params("year"), ctx.Params("month"), ctx.Params("day"))
})
```

### 全局匹配

路由匹配可以通过全局匹配的形式:

```go
m.Get("/hello/*", func(ctx *macaron.Context) string {
    return "Hello " + ctx.Params("*")
})
```

那么，如果将 `*` 放在路由中间会发生什么呢？

```go
m.Get("/date/*/*/*/events", func(ctx *macaron.Context) string {
    return fmt.Sprintf("Date: %s/%s/%s", ctx.Params("*0"), ctx.Params("*1"), ctx.Params("*2"))
})
```

### 正则表达式

您还可以使用正则表达式来书写路由规则：

* 常规匹配：

  ```go
    m.Get("/user/:username([\\w]+)", func(ctx *macaron.Context) string {
        return fmt.Sprintf("Hello %s", ctx.Params(":username"))
    })

    m.Get("/user/:id([0-9]+)", func(ctx *macaron.Context) string {
        return fmt.Sprintf("User ID: %s", ctx.Params(":id"))
    })

    m.Get("/user/*.*", func(ctx *macaron.Context) string {
        return fmt.Sprintf("Last part is: %s, Ext: %s", ctx.Params(":path"), ctx.Params(":ext"))
    })
  ```
* 混合匹配：

  ```go
    m.Get("/cms_:id([0-9]+).html", func(ctx *macaron.Context) string {
        return fmt.Sprintf("The ID is %s", ctx.Params(":id"))
    })
  ```
* 可选匹配：
  * `/user/?:id` 可同时匹配 `/user/` 和 `/user/123`。
* 简写：
  * `/user/:id:int`：`:int` 是 `([0-9]+)` 正则的简写。
  * `/user/:name:string`：`:string` 是 `([\w]+)` 正则的简写。

## 匹配优先级

以下为从高到低的不同模式的匹配优先级：

* 静态路由：
  * `/`
  * `/home`
* 正则表达式路由：
  * `/(.+).html`
  * `/([0-9]+).css`
* 路径-后缀路由：
  * `/*.*`
* 占位符路由：
  * `/:id`
  * `/:name`
* 全局匹配路由：
  * `/*`

其它说明：

* 相同模式的匹配优先级是根据添加的先后顺序决定的。
* 层级相对明确的模式匹配优先级要高于相对模糊的模式：
  * `/*/*/events` > `/*`

### 构建 URL 路径

您可以通过 [`*Route.Name`](https://gowalker.org/gopkg.in/macaron.v1#Route_Name) 方法配合命名参数来构建 URL 路径，不过首先需要为路由命名：

```go
// ...
m.Get("/users/:id([0-9]+)/:name:string.profile", handler).Name("user_profile")
m.Combo("/api/:user/:repo").Get(handler).Post(handler).Name("user_repo")
// ...
```

然后通过 [`*Router.URLFor`](https://gowalker.org/gopkg.in/macaron.v1#Router_URLFor) 方法来为指定名称的路由构建 URL 路径：

```go
// ...
func handler(ctx *macaron.Context) {
    // /users/12/unknwon.profile
    userProfile := ctx.URLFor("user_profile", ":id", "12", ":name", "unknwon")
    // /api/unknwon/macaron
    userRepo := ctx.URLFor("user_repo", ":user", "unknwon", ":repo", "macaron")
}
// ...
```

#### 配合 Go 模板引擎使用

```go
// ...
m.Use(macaron.Renderer(macaron.RenderOptions{
    Funcs:      []template.FuncMap{map[string]interface{}{
        "URLFor": m.URLFor,    
    }},
}))
// ...
```

#### 配合 Pongo2 模板引擎使用

```go
// ...
ctx.Data["URLFor"] = ctx.URLFor
ctx.HTML(200, "home")
// ...
```

## 高级路由定义

路由处理器可以被相互叠加使用, 例如很有用的地方可以是在验证和授权的时候:

```go
m.Get("/secret", authorize, func() {
    // this will execute as long as authorize doesn't write a response
})
```

让我们来看一个比较极端的例子：

```go
package main

import (
    "fmt"

    "gopkg.in/macaron.v1"
)

func main() {
    m := macaron.Classic()
    m.Get("/",
        func(ctx *macaron.Context) {
            ctx.Data["Count"] = 1
        },
        func(ctx *macaron.Context) {
            ctx.Data["Count"] = ctx.Data["Count"].(int) + 1
        },
        func(ctx *macaron.Context) {
            ctx.Data["Count"] = ctx.Data["Count"].(int) + 1
        },
        func(ctx *macaron.Context) {
            ctx.Data["Count"] = ctx.Data["Count"].(int) + 1
        },
        func(ctx *macaron.Context) {
            ctx.Data["Count"] = ctx.Data["Count"].(int) + 1
        },
        func(ctx *macaron.Context) string {
            return fmt.Sprintf("There are %d handlers before this", ctx.Data["Count"])
        },
    )
    m.Run()
}
```

先意淫下结果？没错，输出结果会是 `There are 5 handlers before this`。Macaron 并没有对您可以使用多少个处理器有一个硬性的限制。不过，Macaron 又是怎么知道什么时候停止调用下一个处理器的呢？

想要回答这个问题，我们先来看下下一个例子：

```go
package main

import (
    "fmt"

    "gopkg.in/macaron.v1"
)

func main() {
    m := macaron.Classic()
    m.Get("/",
        func(ctx *macaron.Context) {
            ctx.Data["Count"] = 1
        },
        func(ctx *macaron.Context) {
            ctx.Data["Count"] = ctx.Data["Count"].(int) + 1
        },
        func(ctx *macaron.Context) {
            ctx.Data["Count"] = ctx.Data["Count"].(int) + 1
        },
        func(ctx *macaron.Context) {
            ctx.Data["Count"] = ctx.Data["Count"].(int) + 1
        },
        func(ctx *macaron.Context) string {
            return fmt.Sprintf("There are %d handlers before this", ctx.Data["Count"])
        },
        func(ctx *macaron.Context) string {
            return fmt.Sprintf("There are %d handlers before this", ctx.Data["Count"])
        },
    )
    m.Run()
}
```

在这个例子中，输出结果将会变成 `There are 4 handlers before this`，而最后一个处理器永远也不会被调用。这是为什么呢？因为我们已经在第 5 个处理器中向响应流写入了内容。所以说，一旦任一处理器向响应流写入任何内容，Macaron 将不会再调用下一个处理器。

### 组路由

路由还可以通过 [`macaron.Group`](https://gowalker.org/gopkg.in/macaron.v1#Router_Group) 来注册组路由：

```go
m.Group("/books", func() {
    m.Get("/:id", GetBooks)
    m.Post("/new", NewBook)
    m.Put("/update/:id", UpdateBook)
    m.Delete("/delete/:id", DeleteBook)

    m.Group("/chapters", func() {
        m.Get("/:id", GetBooks)
        m.Post("/new", NewBook)
        m.Put("/update/:id", UpdateBook)
        m.Delete("/delete/:id", DeleteBook)
    })
})
```

同样的，您可以为某一组路由设置集体的中间件：

```go
m.Group("/books", func() {
    m.Get("/:id", GetBooks)
    m.Post("/new", NewBook)
    m.Put("/update/:id", UpdateBook)
    m.Delete("/delete/:id", DeleteBook)

    m.Group("/chapters", func() {
        m.Get("/:id", GetBooks)
        m.Post("/new", NewBook)
        m.Put("/update/:id", UpdateBook)
        m.Delete("/delete/:id", DeleteBook)
    }, MyMiddleware3, MyMiddleware4)
}, MyMiddleware1, MyMiddleware2)
```

同样的，Macaron 不在乎您使用多少层嵌套的组路由，或者多少个组级别处理器（中间件）。


# 模板引擎

目前 Macaron 应用有两款官方模板引擎中间件可供选择，即 [`macaron.Renderer`](https://gowalker.org/gopkg.in/macaron.v1#Renderer) 和 [`pongo2.Pongoer`](https://gowalker.org/github.com/go-macaron/pongo2#Pongoer)。

您可以自由选择使用哪一款模板引擎，并且您只能为一个 [Macaron 实例](/zh-cn/core_concepts#macaron-shi-li) 注册一款模板引擎。

共有特性：

* 均支持 XML、JSON 和原始数据格式的响应，它们之间的不同只体现在 HTML 渲染上。
* 均使用 `templates` 作为默认模板文件目录。
* 均使用 `.tmpl` 和 `.html` 作为默认模板文件后缀。
* 均支持通过 [Macaron 环境变量](/zh-cn/core_concepts#macaron-huan-jing-bian-liang) 来判断是否缓存模板文件（当 `macaron.Env == macaron.PROD` 时）。

## 渲染 HTML

### Go 模板引擎

该服务可以通过函数 [`macaron.Renderer`](https://gowalker.org/gopkg.in/macaron.v1#Renderer) 来注入，并通过类型 [`macaron.Render`](https://gowalker.org/gopkg.in/macaron.v1#Render) 来体现。该服务为可选，一般情况下可直接使用 `*macaron.Context.Render`。该服务使用 Go 语言内置的模板引擎来渲染 HTML。如果想要了解更多有关使用方面的信息，请参见 [官方文档](https://gowalker.org/html/template)。

#### 使用示例

假设您的应用拥有以下目录结构：

```
main/
    |__ main.go
    |__ templates/
            |__ hello.tmpl
```

hello.tmpl：

```markup
<h1>Hello {{.Name}}</h1>
```

main.go：

```go
package main

import "gopkg.in/macaron.v1"

func main() {
    m := macaron.Classic()
    m.Use(macaron.Renderer())

    m.Get("/", func(ctx *macaron.Context) {
        ctx.Data["Name"] = "jeremy"
        ctx.HTML(200, "hello") // 200 为响应码
    })

    m.Run()
}
```

#### 自定义选项

该服务允许接受一个参数来进行自定义选项（[`macaron.RenderOptions`](https://gowalker.org/gopkg.in/macaron.v1#RenderOptions)）：

```go
package main

import "gopkg.in/macaron.v1"

func main() {
    m := macaron.Classic()
    m.Use(macaron.Renderer(macaron.RenderOptions{
        // 模板文件目录，默认为 "templates"
        Directory: "templates",
        // 模板文件后缀，默认为 [".tmpl", ".html"]
        Extensions: []string{".tmpl", ".html"},
        // 模板函数，默认为 []
        Funcs: []template.FuncMap{map[string]interface{}{
            "AppName": func() string {
                return "Macaron"
            },
            "AppVer": func() string {
                return "1.0.0"
            },
        }},
        // 模板语法分隔符，默认为 ["{{", "}}"]
        Delims: macaron.Delims{"{{", "}}"},
        // 追加的 Content-Type 头信息，默认为 "UTF-8"
        Charset: "UTF-8",
        // 渲染具有缩进格式的 JSON，默认为不缩进
        IndentJSON: true,
        // 渲染具有缩进格式的 XML，默认为不缩进
        IndentXML: true,
        // 渲染具有前缀的 JSON，默认为无前缀
        PrefixJSON: []byte("macaron"),
        // 渲染具有前缀的 XML，默认为无前缀
        PrefixXML: []byte("macaron"),
        // 允许输出格式为 XHTML 而不是 HTML，默认为 "text/html"
        HTMLContentType: "text/html",
    }))        
    // ...
}
```

### Pongo2 模板引擎

该服务可以通过函数 [`pongo2.Pongoer`](https://gowalker.org/github.com/go-macaron/pongo2#Pongoer) 来注入，并通过类型 [`macaron.Render`](https://gowalker.org/gopkg.in/macaron.v1#Render)来体现。该服务为可选，一般情况下可直接使用 `*macaron.Context.Render`。该服务使用 Pongo2 **v3** 模板引擎来渲染 HTML。如果想要了解更多有关使用方面的信息，请参见 [官方文档](https://github.com/flosch/pongo2)。

#### 使用示例

假设您的应用拥有以下目录结构：

```
main/
    |__ main.go
    |__ templates/
            |__ hello.tmpl
```

hello.tmpl：

```markup
<h1>Hello {{Name}}</h1>
```

main.go：

```go
package main

import (
    "github.com/go-macaron/pongo2"
    "gopkg.in/macaron.v1"
)

func main() {
    m := macaron.Classic()
    m.Use(pongo2.Pongoer())

    m.Get("/", func(ctx *macaron.Context) {
        ctx.Data["Name"] = "jeremy"
        ctx.HTML(200, "hello") // 200 is the response code.
    })

    m.Run()
}
```

#### 自定义选项

该服务允许接受一个参数来进行自定义选项（[`pongo2.Options`](https://gowalker.org/github.com/go-macaron/pongo2#Options)）：

```go
package main

import (
    "github.com/go-macaron/pongo2"
    "gopkg.in/macaron.v1"
)

func main() {
    m := macaron.Classic()
    m.Use(pongo2.Pongoer(pongo2.Options{
        // 模板文件目录，默认为 "templates"
        Directory: "templates",
        // 模板文件后缀，默认为 [".tmpl", ".html"]
        Extensions: []string{".tmpl", ".html"},
        // 追加的 Content-Type 头信息，默认为 "UTF-8"
        Charset: "UTF-8",
        // 渲染具有缩进格式的 JSON，默认为不缩进
        IndentJSON: true,
        // 渲染具有缩进格式的 XML，默认为不缩进
        IndentXML: true,
        // 允许输出格式为 XHTML 而不是 HTML，默认为 "text/html"
        HTMLContentType: "text/html",
    }))        
    // ...
}
```

### 模板集

当您的应用存在多套模板时，就需要使用模板集来实现运行时动态设置需要渲染的模板。

Go 模板引擎的使用方法：

```go
// ...
m.Use(macaron.Renderers(macaron.RenderOptions{
    Directory: "templates/default",
}, "theme1:templates/theme1", "theme2:templates/theme2"))

m.Get("/foobar", func(ctx *macaron.Context) {
    ctx.HTML(200, "hello")
})

m.Get("/foobar1", func(ctx *macaron.Context) {
    ctx.HTMLSet(200, "theme1", "hello")
})

m.Get("/foobar2", func(ctx *macaron.Context) {
    ctx.HTMLSet(200, "theme2", "hello")
})
// ...
```

Pongo2 模板引擎的使用方法：

```go
// ...
m.Use(pongo2.Pongoers(pongo2.Options{
    Directory: "templates/default",
}, "theme1:templates/theme1", "theme2:templates/theme2"))

m.Get("/foobar", func(ctx *macaron.Context) {
    ctx.HTML(200, "hello")
})

m.Get("/foobar1", func(ctx *macaron.Context) {
    ctx.HTMLSet(200, "theme1", "hello")
})

m.Get("/foobar2", func(ctx *macaron.Context) {
    ctx.HTMLSet(200, "theme2", "hello")
})
// ...
```

正如您所看到的那样，其实就是 2 个方法的不同：[`macaron.Renderers`](https://gowalker.org/gopkg.in/macaron.v1#Renderers) 和 [`pongo2.Pongoers`](https://gowalker.org/github.com/go-macaron/pongo2#Pongoers)。

第一个配置参数用于指定默认的模板集和配置选项，之后则是一个模板集名称和目录（通过 `:` 分隔）的列表。

如果您的模板集名称和模板集路径的最后一部分相同，则可以省略名称：

```go
// ...
m.Use(macaron.Renderers(RenderOptions{
    Directory: "templates/default",
}, "templates/theme1", "templates/theme2"))

m.Get("/foobar", func(ctx *macaron.Context) {
    ctx.HTML(200, "hello")
})

m.Get("/foobar1", func(ctx *macaron.Context) {
    ctx.HTMLSet(200, "theme1", "hello")
})

m.Get("/foobar2", func(ctx *macaron.Context) {
    ctx.HTMLSet(200, "theme2", "hello")
})
// ...
```

#### 模板集辅助方法

检查某个模板集是否存在：

```go
// ...
m.Get("/foobar", func(ctx *macaron.Context) {
    ok := ctx.HasTemplateSet("theme2")
    // ...
})
// ...
```

修改模板集的目录：

```go
// ...
m.Get("/foobar", func(ctx *macaron.Context) {
    ctx.SetTemplatePath("theme2", "templates/new/theme2")
    // ...
})
// ...
```

### 小结

也许您已经发现，除了在 HTML 语法上的不同之外，两款引擎在代码层面的用法是完全一样的。

如果您只是想要得到 HTML 渲染后的结果，则可以调用方法 `*macaron.Context.Render.HTMLString`：

```go
package main

import "gopkg.in/macaron.v1"

func main() {
    m := macaron.Classic()
    m.Use(macaron.Renderer())

    m.Get("/", func(ctx *macaron.Context) {
        ctx.Data["Name"] = "jeremy"
        output, err := ctx.HTMLString("hello")
        // 进行其它操作
    })

    m.Run()
}
```

## 渲染 XML、JSON 和原始数据

相对于渲染 HTML 而言，渲染 XML、JSON 和原始数据的工作要简单的多。

```go
package main

import "gopkg.in/macaron.v1"

type Person struct {
    Name string
    Age  int
    Sex  string
}

func main() {
    m := macaron.Classic()
    m.Use(macaron.Renderer())

    m.Get("/xml", func(ctx *macaron.Context) {
        p := Person{"Unknwon", 21, "male"}
        ctx.XML(200, &p)
    })
    m.Get("/json", func(ctx *macaron.Context) {
        p := Person{"Unknwon", 21, "male"}
        ctx.JSON(200, &p)
    })
    m.Get("/raw", func(ctx *macaron.Context) {
        ctx.RawData(200, []byte("raw data goes here"))
    })
    m.Get("/text", func(ctx *macaron.Context) {
        ctx.PlainText(200, []byte("plain text goes here"))
    })

    m.Run()
}
```

## 响应状态码、错误和重定向

如果您希望响应指定状态码、错误和重定向操作，则可以参照以下代码：

```go
package main

import "gopkg.in/macaron.v1"

func main() {
    m := macaron.Classic()
    m.Use(macaron.Renderer())

    m.Get("/status", func(ctx *macaron.Context) {
        ctx.Status(403)
    })
    m.Get("/error", func(ctx *macaron.Context) {
        ctx.Error(500, "Internal Server Error")
    })
    m.Get("/redirect", func(ctx *macaron.Context) {
        ctx.Redirect("/") // 第二个参数为响应码，默认为 302
    })

    m.Run()
}
```

## 运行时修改模板路径

如果您希望在运行时修改应用的模板路径，则可以调用方法 `*macaron.Context.SetTemplatePath`。需要注意的是，修改操作是全局生效的，而不只是针对当前请求。

### 使用示例

假设您的应用拥有以下目录结构：

```
main/
    |__ main.go
    |__ templates/
            |__ hello.tmpl
    |__ templates2/
            |__ hello.tmpl
```

templates/hello.tmpl：

```markup
<h1>Hello {{.Name}}</h1>
```

templates2/hello.tmpl：

```markup
<h1>What's up, {{.Name}}</h1>
```

main.go：

```go
package main

import "gopkg.in/macaron.v1"

func main() {
    m := macaron.Classic()
    m.Use(macaron.Renderer())

    m.Get("/old", func(ctx *macaron.Context) {
        ctx.Data["Name"] = "Unknwon"
        ctx.HTML(200, "hello")
        // 空字符串表示操作默认模板集
        ctx.SetTemplatePath("", "templates2")
    })
    m.Get("/new", func(ctx *macaron.Context) {
        ctx.Data["Name"] = "Unknwon"
        ctx.HTML(200, "hello")
    })

    m.Run()
}
```

当您首次请求 `/old` 页面时，响应结果为 `<h1>Hello Unknwon</h1>`，然后便执行了修改模板路径为 `template2`。此时，当您请求 `/new` 页面时，响应结果会变成 `<h1>What's up, Unknwon</h1>`。


# Gzip 压缩

中间件 gzip 为 [Macaron 实例](/zh-cn/core_concepts#macaron-shi-li) 的响应内容提供 Gzip 压缩。请确保在其它会向响应流写入内容的中间件之前注册该服务。

* [GitHub](https://github.com/go-macaron/gzip)
* [API 文档](https://gowalker.org/github.com/go-macaron/gzip)

## 下载安装

```bash
go get github.com/go-macaron/gzip
```

## 使用示例

```go
package main

import (
    "github.com/go-macaron/gzip"
    "gopkg.in/macaron.v1"
)

func main() {
    m := macaron.Classic()
    m.Use(gzip.Gziper())
    // 注册路由
    m.Run()
}
```

在这个例子中，静态资源不会被 Gzip 压缩，如果想压缩它们，则可以使用以下方法：

```go
package main

import (
    "github.com/go-macaron/gzip"
    "gopkg.in/macaron.v1"
)

func main() {
    m := macaron.New()
    m.Use(macaron.Logger())
    m.Use(macaron.Recovery())
    m.Use(gzip.Gziper())
    m.Use(macaron.Static("public"))
    // 注册路由
    m.Run()
}
```

或者选择只压缩某一组路由的响应内容：

```go
// ...

func main() {
    m := macaron.Classic()
    m.Group("/gzip", func() {
        // ...
    }, gzip.Gziper())
    // ...
    m.Run()
}
```

## 自定义选项

该服务允许接受一个参数来进行自定义选项（[`gzip.Options`](https://gowalker.org/github.com/go-macaron/gzip#Options)）：

```go
// ...
m.Use(gzip.Gziper(gzip.Options{
    // 压缩级别，可以是 DefaultCompression（-1）、ConstantCompression（-2）
    // 或介于包括 BestSpeed（1） 和 BestCompression（9） 在内，这两者之间的任意整数。
    // 默认为 4
    CompressionLevel: 4,
}))
```


# 应用本地化

中间件 i18n 为 [Macaron 实例](/zh-cn/core_concepts#macaron-shi-li) 提供了国际化和本地化应用的功能。

* [GitHub](https://github.com/go-macaron/i18n)
* [API 文档](https://gowalker.org/github.com/go-macaron/i18n)

## 下载安装

```bash
go get github.com/go-macaron/i18n
```

## 使用示例

```go
// main.go
import (
    "github.com/go-macaron/i18n"
    "gopkg.in/macaron.v1"
)

func main() {
      m := macaron.Classic()
      m.Use(i18n.I18n(i18n.Options{
        Langs:    []string{"en-US", "zh-CN"},
        Names:    []string{"English", "简体中文"},
    }))

    m.Get("/", func(locale i18n.Locale) string {
        return "current language is" + locale.Lang
    })

    // 在处理器中使用
    m.Get("/trans", func(ctx *macaron.Context) string {
        return ctx.Tr("hello %s", "world")
    })

    m.Run()
}
```

```markup
<!-- templates/hello.tmpl -->
<h2>{{i18n.Tr "hello %s" "world"}}!</h2>
```

### Pongo2 模板引擎

在 [pongo2](https://github.com/flosch/pongo2) 模板引擎中使用 i18n 中间件：

```markup
<!-- templates/hello.tmpl -->
<h2>{{Tr(Lang,"hello %s","world")}}!</h2>
```

## 自定义选项

该服务允许接受一个参数来进行自定义选项（[`i18n.Options`](https://gowalker.org/github.com/go-macaron/i18n#Options)）：

```go
// ...
m.Use(i18n.I18n(i18n.Options{
    // 存放本地化文件的目录，默认为 "conf/locale"
    Directory:    "conf/locale",
    // 支持的语言，顺序是有意义的
    Langs:        []string{"en-US", "zh-CN"},
    // 语言的本地化名称
    Names:        []string{"English", "简体中文"},
    // 本地化文件命名风格，默认为 "locale_%s.ini"
    Format:        "locale_%s.ini",
    // 指示当前语言的 URL 参数名，默认为 "lang"
    Parameter:    "lang",
    // 当通过 URL 参数指定语言时是否重定向，默认为 false
    Redirect:    false,
    // 存放在模板中的本地化对象变量名称，默认为 "i18n"
    TmplName:    "i18n",
}))
// ...
```

## 加载本地化文件

默认情况下，本地化文件应当存放在相对当前目录的 `conf/locale` 文件夹下：

```
conf/
  |
  |__ locale/
        |
        |__ locale_en-US.ini
        |
        |__ locale_zh-CN.ini
```

## 其它说明

* 请查看 [unknwon/i18n](https://github.com/Unknwon/i18n) 包来了解本地化使用规范。
* 您可以将 [Peach Docs](https://github.com/peachdocs/peach) 作为学习案例。


# 数据绑定与验证

中间件 binding 为 [Macaron 实例](/zh-cn/core_concepts#macaron-shi-li) 提供了请求数据绑定与验证的功能。

* [GitHub](https://github.com/go-macaron/binding)
* [API 文档](https://gowalker.org/github.com/go-macaron/binding)

## 下载安装

```bash
go get github.com/go-macaron/binding
```

## 使用示例

### 获取表单数据

假设您有一个联系人信息的表单，其中姓名和信息为必填字段，则我们可以使用如下结构来进行表示：

```go
type ContactForm struct {
    Name           string `form:"name" binding:"Required"`
    Email          string `form:"email"`
    Message        string `form:"message" binding:"Required"`
    MailingAddress string `form:"mailing_address"`
}
```

然后通过 Macaron 增加如下路由：

```go
m.Post("/contact/submit", binding.Bind(ContactForm{}), func(contact ContactForm) string {
    return fmt.Sprintf("Name: %s\nEmail: %s\nMessage: %s\nMailing Address: %v",
        contact.Name, contact.Email, contact.Message, contact.MailingAddress)
})
```

搞定！函数 [`binding.Bind`](https://gowalker.org/github.com/go-macaron/binding#Bind) 会帮助您完成对必选字段的数据验证。

默认情况下，如果在验证过程中发生任何错误（例如：必填字段的值为空），binding 中间件就会直接向客户端返回错误信息，提前终止请求的处理。如果您不希望 binding 中间件自动终止请求的处理，则可以使用 [`binding.BindIgnErr`](https://gowalker.org/github.com/go-macaron/binding#BindIgnErr) 函数来忽略对错误的自动处理。

{% hint style="danger" %}
请不要使用类型为指针的嵌入结构，这会导致错误。请查看 [martini-contrib/binding issue 30](https://github.com/martini-contrib/binding/issues/30) 上的相关讨论获取完整信息。
{% endhint %}

#### 命名约定

默认情况下，`form` 标签的名称使用以下命名约定：

* `Name` -> `name`
* `UnitPrice` -> `unit_price`

也就是说，上面例子中的结构定义可以简化为如下代码：

```go
type ContactForm struct {
    Name           string `binding:"Required"`
    Email          string
    Message        string `binding:"Required"`
    MailingAddress string
}
```

超赞！有木有？

如果您想要自定义命名约定，可以通过 [`binding.SetNameMapper`](https://gowalker.org/github.com/go-macaron/binding#SetNameMapper) 函数来设置。该函数接受一个类型为 [`binding.NameMapper`](https://gowalker.org/github.com/go-macaron/binding#NameMapper) 的值作为参数。

### 获取 JSON 数据

将指定 `form` 标签的地方替换为 `json`，就可以完成对 JSON 数据的绑定。

{% hint style="info" %}
使用 [JSON-to-Go](http://mholt.github.io/json-to-go/) 网站工具可以帮助您更好更快地得根据 JSON 数据生成 Go 语言中对应的结构。
{% endhint %}

### 绑定到接口

如果您希望传递接口而不是一个具体的结构，则可以使用如下方法：

```go
m.Post("/contact/submit", binding.Bind(ContactForm{}, (*MyInterface)(nil)), func(contact MyInterface) {
    // ... 您接收到的值为一个接口
})
```

## 处理器说明

原则上，每个处理器之间是相互独立的，但在特定情况下，它们之间会相互调用。

### Bind

函数 [`binding.Bind`](https://gowalker.org/github.com/go-macaron/binding#Bind) 是一个便利性的高层封装，它能够自动识别表单类型并完成数据绑定与验证。

请求处理流程：

1. 反序列化请求数据到结构
2. 通过 [`binding.Validate`](https://gowalker.org/github.com/go-macaron/binding#Validate) 函数完成数据验证
3. 如果您的结构实现了 [`binding.ErrorHandler`](https://gowalker.org/github.com/go-macaron/binding#ErrorHandler) 接口，则会调用相应的错误处理方法 `ErrorHandler.Error`；否则会使用默认的错误处理机制。

备注：

* 当使用默认的错误处理机制时，您的应用（队列后方的处理器）将根本不会意识到当前请求的存在。
* 头信息 `Content-Type` 是用于决定如何对请求数据进行反序列化的根本条件。

{% hint style="danger" %}
请不要尝试绑定指向某个结构的指针，binding 中间件会直接 panic 并退出程序 [以防止可能发生的数据竞争](https://github.com/codegangsta/martini-contrib/pull/34#issuecomment-29683659)。
{% endhint %}

### Form

函数 [`binding.Form`](https://gowalker.org/github.com/go-macaron/binding#Form) 用于反序列化表单数据，可以是查询或 `form-urlencoded` 类型的请求。

请求处理流程：

1. 反序列化请求数据到结构
2. 通过 [`binding.Validate`](https://gowalker.org/github.com/go-macaron/binding#Validate) 函数完成数据验证

需要注意的是，该函数不具有默认错误处理机制。您可以通过获取类型为 [`binding.Errors`](https://gowalker.org/github.com/go-macaron/binding#Errors) 的参数来完成自定义错误处理。

### MultipartForm 和文件上传

类似 [`binding.Form`](https://gowalker.org/github.com/go-macaron/binding#Form)，函数 [`binding.MultipartForm`](https://gowalker.org/github.com/go-macaron/binding#MultipartForm) 同样是反序列化表单数据到结构。除此之外，它还能处理 `enctype="multipart/form-data"` 类型的 POST 请求。如果结构中包含类型为 [`*multipart.FileHeader`](http://gowalker.org/pkg/mime/multipart/#FileHeader)（或 `[]*multipart.FileHeader`）的字段，您可以直接从该字段读取客户端上传的文件。

请求处理流程：

1. 反序列化请求数据到结构
2. 通过 [`binding.Validate`](https://gowalker.org/github.com/go-macaron/binding#Validate) 函数完成数据验证

同样的，和函数 [`binding.Form`](https://gowalker.org/github.com/go-macaron/binding#Form) 一样，该函数不具有默认错误处理机制，但您可以通过获取类型为 [`binding.Errors`](https://gowalker.org/github.com/go-macaron/binding#Errors) 的参数来完成自定义错误处理。

#### 使用示例

```go
type UploadForm struct {
    Title      string                `form:"title"`
    TextUpload *multipart.FileHeader `form:"txtUpload"`
}

func main() {
    m := macaron.Classic()
    m.Post("/", binding.MultipartForm(UploadForm{}), uploadHandler(uf UploadForm) string {
        file, err := uf.TextUpload.Open()
        // ... 您可以在这里读取上传的文件内容
    })
    m.Run()
}
```

### Json

函数 [`binding.Json`](https://gowalker.org/github.com/go-macaron/binding#Json) 反序列化 JSON 数据。

请求处理流程：

1. 反序列化请求数据到结构
2. 通过 [`binding.Validate`](https://gowalker.org/github.com/go-macaron/binding#Validate) 函数完成数据验证

与函数 [`binding.Form`](https://gowalker.org/github.com/go-macaron/binding#Form)，该函数不具有默认错误处理机制，但您可以通过获取类型为 [`binding.Errors`](https://gowalker.org/github.com/go-macaron/binding#Errors) 的参数来完成自定义错误处理。

### Validate

函数 [`binding.Validate`](https://gowalker.org/github.com/go-macaron/binding#Validate) 接受一个结构并对它进行基本数据验证。如果该结构实现了 [`binding.Validator`](https://gowalker.org/github.com/go-macaron/binding#Validator) 接口，则会调用 `Validator.Validate()` 方法完成后续的数据验证。

#### 验证规则

目前有一些内置的验证规则，通过格式为 `binding:"<Name>"` 的标签使用。

| 名称                 | 说明                            |
| ------------------ | ----------------------------- |
| `OmitEmpty`        | 值为空时忽略后续验证                    |
| `Required`         | 必须为相同类型的非零值                   |
| `AlphaDash`        | 必须为半角英文字母、阿拉伯数字或 `-_`         |
| `AlphaDashDot`     | 必须为半角英文字母、阿拉伯数字、`-_` 或 `.`    |
| `Size(int)`        | 固定长度                          |
| `MinSize(int)`     | 最小长度                          |
| `MaxSize(int)`     | 最大长度                          |
| `Range(int,int)`   | 取值范围（包含边界值）                   |
| `Email`            | 必须为邮箱地址                       |
| `Url`              | 必须为 HTTP/HTTPS URL 地址         |
| `In(a,b,c,...)`    | 必须为数组的一个元素                    |
| `NotIn(a,b,c,...)` | 必须不是数组的元素                     |
| `Include(string)`  | 必须包含                          |
| `Exclude(string)`  | 必须不包含                         |
| `Default(string)`  | 当字段为零值时设置默认值（当使用接口绑定时不能设置该规则） |

当需要使用多条规则时： `binding:"Required;MinSize(10)"`。

## 自定义操作

### 自定义验证

如果您想要进行自定义的附加验证操作，您的结构可以通过实现接口 [`binding.Validator`](https://gowalker.org/github.com/go-macaron/binding#Validator) 来完成：

```go
func (cf ContactForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
    if strings.Contains(cf.Message, "Go needs generics") {
        errs = append(errors, binding.Error{
            FieldNames:     []string{"message"},
            Classification: "ComplaintError",
            Message:        "Go has generics. They're called interfaces.",
        })
    }
    return errs
}
```

现在，任何包含信息 "Go needs generics" 的联系人表单都会报错。

### 自定义验证规则

当您觉得内置的验证规则不够时，可以通过函数 [`binding.AddParamRule`](https://gowalker.org/github.com/go-macaron/binding#AddParamRule) 来增加自定义验证规则。该函数接受一个类型为 [`binding.ParamRule`](https://gowalker.org/github.com/go-macaron/binding#ParamRule) 的参数。

假设您需要验证字段的最小值：

```go
binding.AddParamRule(&binding.ParamRule{
    IsMatch: func(rule string) bool {
        return strings.HasPrefix(rule, "Min(")
    },
    IsValid: func(errs binding.Errors, rule, name string, v interface{}) (bool, binding.Errors) {
        num, ok := v.(int)
        if !ok {
            return false, errs
        }
        min, _ := strconv.Atoi(rule[4 : len(rule)-1])
        if num < min {
            errs.Add([]string{name}, "MinimumValue", "Value is too small")
            return false, errs
        }
        return true, errs
    },
})
```

如果您的规则非常简单，也可以使用 [`binding.AddRule`](https://gowalker.org/github.com/go-macaron/binding#AddRule)，它接受类型为 [`binding.Rule`](https://gowalker.org/github.com/go-macaron/binding#Rule) 的参数：

```go
binding.AddRule(&binding.Rule{
    IsMatch: func(rule string) bool {
        return rule == "String"
    },
    IsValid: func(errs binding.Errors, name string, v interface{}) (bool, binding.Errors) {
        _, ok := v.(string)
        return ok, errs
    },
})
```

自定义规则的应用发生在内置规则之后。

### 自定义错误处理

如果您即不想使用默认的错误处理机制，又希望 binding 中间件自动化地调用您的自定义错误处理，则可以通过实现接口 [`binding.ErrorHandler`](https://gowalker.org/github.com/go-macaron/binding#ErrorHandler) 来完成：

```go
func (cf ContactForm) Error(ctx *macaron.Context, errs binding.Errors) {
    // 自定义错误处理过程
}
```

该操作发生在自定义验证规则被应用之后。


# 缓存管理（Cache）

中间件 cache 为 [Macaron 实例](/zh-cn/core_concepts#macaron-shi-li) 提供了缓存管理的功能。

* [GitHub](https://github.com/go-macaron/cache)
* [API Reference](https://gowalker.org/github.com/go-macaron/cache)

## 下载安装

```bash
go get github.com/go-macaron/cache
```

## 使用示例

```go
import (
    "github.com/go-macaron/cache"
    "gopkg.in/macaron.v1"
)

func main() {
    m := macaron.Classic()
    m.Use(cache.Cacher())

    m.Get("/", func(c cache.Cache) string {
        c.Put("cache", "cache middleware", 120)
        return c.Get("cache")
    })

    m.Run()
}
```

## 自定义选项

该服务允许接受一个参数来进行自定义选项（[`cache.Options`](https://gowalker.org/github.com/go-macaron/cache#Options)）：

```go
//...
m.Use(cache.Cacher(cache.Options{
    // 适配器的名称，默认为 "memory".
    Adapter:        "memory",
    // 适配器的配置，根据适配器而不同
    AdapterConfig:  "",
    // GC 执行时间间隔，默认为 60 秒
    Interval:       60,
    // 配置分区名称，默认为 "cache"
    Section:        "cache",
    }))
//...
```

## 适配器

目前有 8 款内置的适配器，除了 **内存** 和 **文件** 提供器外，您都必须显式导入其它适配器的驱动。

以下为适配器的基本用法：

### 内存

```go
//...
m.Use(cache.Cacher())
//...
```

### 文件

```go
//...
m.Use(cache.Cacher(cache.Options{
    Adapter:       "file",
    AdapterConfig: "data/caches",
}))
//...
```

### Redis

**特别注意** 只能存取 string 和 int 相关类型。

```go
import _ "github.com/go-macaron/cache/redis"

//...
m.Use(cache.Cacher(cache.Options{
    Adapter:       "redis",
    // e.g.: network=tcp,addr=127.0.0.1:6379,password=macaron,db=0,pool_size=100,idle_timeout=180,hset_name=MacaronCache,prefix=cache:
    AdapterConfig: "addr=127.0.0.1:6379,password=macaron",
    OccupyMode:    false,
}))
//...
```

当您使用 Redis 作为缓存器时，可以通过将 `OccupyMode` 的值设置为 `true` 来启用独占模式。在该模式下，缓存器将直接占用所选用的整个数据库，而不是通过维护一个索引集合来判断哪些数据是属于您的应用的。当您的缓存数据非常巨大时，该模式可以有效降低应用的 CPU 和内存使用率。

### Memcache

```go
import _ "github.com/go-macaron/cache/memcache"

//...
m.Use(cache.Cacher(cache.Options{
    Adapter:       "memcache",
    // e.g.: 127.0.0.1:9090;127.0.0.1:9091
    AdapterConfig: "127.0.0.1:11211",
}))
//...
```

### PostgreSQL

可以使用以下 SQL 语句创建数据库：

```sql
CREATE TABLE cache (
    key       CHAR(32) NOT NULL,
    data      BYTEA,
    created   INTEGER NOT NULL,
    expire    INTEGER NOT NULL,
    PRIMARY KEY (key)
);
```

```go
import _ "github.com/go-macaron/cache/postgres"

//...
m.Use(cache.Cacher(cache.Options{
    Adapter:       "postgres",
    AdapterConfig: "user=a password=b host=localhost port=5432 dbname=c sslmode=disable",
}))
//...
```

### MySQL

可以使用以下 SQL 语句创建数据库：

```sql
CREATE TABLE `cache` (
    `key`       CHAR(32) NOT NULL,
    `data`      BLOB,
    `created`   INT(11) UNSIGNED NOT NULL,
    `expire`    INT(11) UNSIGNED NOT NULL,
    PRIMARY KEY (`key`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
```

```go
import _ "github.com/go-macaron/cache/mysql"

//...
m.Use(cache.Cacher(cache.Options{
    Adapter:       "mysql",
    AdapterConfig: "username:password@protocol(address)/dbname?param=value",
}))
//...
```

### Ledis

```go
import _ "github.com/go-macaron/cache/ledis"

//...
m.Use(cache.Cacher(cache.Options{
    Adapter:       "ledis",
    AdapterConfig: "data_dir=./app.db,db=0",
}))
//...
```

### Nodb

```go
import _ "github.com/go-macaron/cache/nodb"

//...
m.Use(cache.Cacher(cache.Options{
    Adapter:       "nodb",
    AdapterConfig: "data/cache.db",
}))
//...
```


# 验证码服务

中间件 captcha 用于为 [Macaron 实例](/zh-cn/core_concepts#macaron-shi-li) 提供验证码服务。

* [GitHub](https://github.com/go-macaron/captcha)
* [API 文档](https://gowalker.org/github.com/go-macaron/captcha)

### 下载安装

```bash
go get github.com/go-macaron/captcha
```

## 使用示例

想要使用该中间件，您必须同时使用 [cache](https://github.com/go-macaron/docs/tree/f112eb7b968ef7236b6731b5b4561f3f56c9194c/zh-CN/middlewares/cache/README.md) 中间件。

```go
// main.go
import (
    "github.com/go-macaron/cache"
    "github.com/go-macaron/captcha"
    "gopkg.in/macaron.v1"
)

func main() {
    m := macaron.Classic()
    m.Use(cache.Cacher())
    m.Use(captcha.Captchaer())

    m.Get("/", func(ctx *macaron.Context, cpt *captcha.Captcha) string {
        if cpt.VerifyReq(ctx.Req) {
            return "valid captcha"
        }
        return "invalid captcha"
    })

    m.Run()
}
```

```markup
<!-- templates/hello.tmpl -->
{{.Captcha.CreateHtml}}
```

## 自定义选项

该服务允许接受一个参数来进行自定义选项（[`captcha.Options`](https://gowalker.org/github.com/go-macaron/captcha#Options)）：

```go
// ...
m.Use(captcha.Captchaer(captcha.Options{
    // 获取验证码图片的 URL 前缀，默认为 "/captcha/"
    URLPrefix:            "/captcha/",
    // 表单隐藏元素的 ID 名称，默认为 "captcha_id"
    FieldIdName:        "captcha_id",
    // 用户输入验证码值的元素 ID，默认为 "captcha"
    FieldCaptchaName:    "captcha",
    // 验证字符的个数，默认为 6
    ChallengeNums:        6,
    // 验证码图片的宽度，默认为 240 像素
    Width:                240,
    // 验证码图片的高度，默认为 80 像素
    Height:                80,
    // 验证码过期时间，默认为 600 秒
    Expiration:            600,
    // 用于存储验证码正确值的 Cache 键名，默认为 "captcha_"
    CachePrefix:        "captcha_",
}))
// ...
```


# 会话管理（Session）

中间件 session 为 [Macaron 实例](/zh-cn/core_concepts#macaron-shi-li) 提供了会话管理的功能。

* [GitHub](https://github.com/go-macaron/session)
* [API 文档](https://gowalker.org/github.com/go-macaron/session)

## 下载安装

```bash
go get github.com/go-macaron/session
```

## 使用示例

```go
import (
    "github.com/go-macaron/session"
    "gopkg.in/macaron.v1"
)

func main() {
    m := macaron.Classic()
    m.Use(macaron.Renderer())
    m.Use(session.Sessioner())

    m.Get("/", func(sess session.Store) string {
        sess.Set("session", "session middleware")
        return sess.Get("session").(string)
    })

    m.Get("/signup", func(ctx *macaron.Context, f *session.Flash) {
        f.Success("yes!!!")
        f.Error("opps...")
        f.Info("aha?!")
        f.Warning("Just be careful.")
        ctx.HTML(200, "signup")
    })

    m.Run()
}
```

```markup
<!-- templates/signup.tmpl -->
<h2>{{.Flash.SuccessMsg}}</h2>
<h2>{{.Flash.ErrorMsg}}</h2>
<h2>{{.Flash.InfoMsg}}</h2>
<h2>{{.Flash.WarningMsg}}</h2>
```

### Pongo2

如果您正在使用 [pongo2](https://github.com/go-macaron/pongo2) 作为应用的模板引擎，则需要对 HTML 进行如下修改：

```markup
<!-- templates/signup.tmpl -->
<h2>{{Flash.SuccessMsg}}</h2>
<h2>{{Flash.ErrorMsg}}</h2>
<h2>{{Flash.InfoMsg}}</h2>
<h2>{{Flash.WarningMsg}}</h2>
```

### 将 Flash 输出到当前响应

默认情况下，Flash 的数据只会在相对应会话的下一个响应中使用，但函数 `Success`、`Error`、`Info` 和 `Warning` 均接受第二个参数来指示是否在当前响应输出数据：

```go
// ...
f.Success("yes!!!", true)
f.Error("opps...", true)
f.Info("aha?!", true)
f.Warning("Just be careful.", true)
// ...
```

但是请注意，不管您选择什么时候输出 Flash 的数据，它都只能够被使用一次。

## 自定义选项

该服务允许接受一个参数来进行自定义选项（[`session.Options`](https://gowalker.org/github.com/go-macaron/session#Options)）：

```go
//...
m.Use(session.Sessioner(session.Options{
    // 提供器的名称，默认为 "memory"
    Provider:       "memory",
    // 提供器的配置，根据提供器而不同
    ProviderConfig: "",
    // 用于存放会话 ID 的 Cookie 名称，默认为 "MacaronSession"
    CookieName:     "MacaronSession",
    // Cookie 储存路径，默认为 "/"
    CookiePath:     "/",
    // GC 执行时间间隔，默认为 3600 秒
    Gclifetime:     3600,
    // 最大生存时间，默认和 GC 执行时间间隔相同
    Maxlifetime:    3600,
    // 仅限使用 HTTPS，默认为 false
    Secure:         false,
    // Cookie 生存时间，默认为 0 秒
    CookieLifeTime: 0,
    // Cookie 储存域名，默认为空
    Domain:         "",
    // 会话 ID 长度，默认为 16 位
    IDLength:       16,
    // 配置分区名称，默认为 "session"
    Section:        "session",
}))
//...
```

## 提供器

目前有 9 款内置的提供器，除了 **内存** 和 **文件** 提供器外，您都必须显式导入其它提供器的驱动。

以下为提供器的基本用法：

### 内存

```go
//...
m.Use(session.Sessioner())
//...
```

### 文件

```go
//...
m.Use(session.Sessioner(session.Options{
    Provider:       "file",
    ProviderConfig: "data/sessions",
}))
//...
```

### Redis

```go
import _ "github.com/go-macaron/session/redis"

//...
m.Use(session.Sessioner(session.Options{
    Provider:       "redis",
    // e.g.: network=tcp,addr=127.0.0.1:6379,password=macaron,db=0,pool_size=100,idle_timeout=180,prefix=session:
    ProviderConfig: "addr=127.0.0.1:6379,password=macaron",
}))
//...
```

### Memcache

```go
import _ "github.com/go-macaron/session/memcache"

//...
m.Use(session.Sessioner(session.Options{
    Provider:       "memcache",
    // e.g.: 127.0.0.1:9090;127.0.0.1:9091
    ProviderConfig: "127.0.0.1:9090",
}))
//...
```

### PostgreSQL

可以使用以下 SQL 语句创建数据库（请确保 `key` 的长度和您设置的 `Options.IDLength` 一致）：

```sql
CREATE TABLE session (
    key       CHAR(16) NOT NULL,
    data      BYTEA,
    expiry    INTEGER NOT NULL,
    PRIMARY KEY (key)
);
```

```go
import _ "github.com/go-macaron/session/postgres"

//...
m.Use(session.Sessioner(session.Options{
    Provider:       "postgres",
    ProviderConfig: "user=a password=b dbname=c sslmode=disable",
}))
//...
```

### MySQL

可以使用以下 SQL 语句创建数据库：

```sql
CREATE TABLE `session` (
    `key`       CHAR(16) NOT NULL,
    `data`      BLOB,
    `expiry`    INT(11) UNSIGNED NOT NULL,
    PRIMARY KEY (`key`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
```

```go
import _ "github.com/go-macaron/session/mysql"

//...
m.Use(session.Sessioner(session.Options{
    Provider:       "mysql",
    ProviderConfig: "username:password@protocol(address)/dbname?param=value",
}))
//...
```

### Couchbase

```go
import _ "github.com/go-macaron/session/couchbase"

//...
m.Use(session.Sessioner(session.Options{
    Provider:       "couchbase",
    ProviderConfig: "username:password@protocol(address)/dbname?param=value",
}))
//...
```

### Ledis

```go
import _ "github.com/go-macaron/session/ledis"

//...
m.Use(session.Sessioner(session.Options{
    Provider:       "ledis",
    ProviderConfig: "data_dir=./app.db,db=0",
}))
//...
```

### Nodb

```go
import _ "github.com/go-macaron/session/nodb"

//...
m.Use(session.Sessioner(session.Options{
    Provider:       "nodb",
    ProviderConfig: "data/cache.db",
}))
//...
```

## 实现提供器接口

如果您需要实现自己的会话存储和提供器，可以通过实现下面两个接口实现，同时还可以将 **内存** 提供器作为学习案例。

```go
// RawStore is the interface that operates the session data.
type RawStore interface {
    // Set sets value to given key in session.
    Set(key, value interface{}) error
    // Get gets value by given key in session.
    Get(key interface{}) interface{}
    // Delete deletes a key from session.
    Delete(key interface{}) error
    // ID returns current session ID.
    ID() string
    // Release releases session resource and save data to provider.
    Release() error
    // Flush deletes all session data.
    Flush() error
}

// Provider is the interface that provides session manipulations.
type Provider interface {
    // Init initializes session provider.
    Init(gclifetime int64, config string) error
    // Read returns raw session store by session ID.
    Read(sid string) (RawStore, error)
    // Exist returns true if session with given ID exists.
    Exist(sid string) bool
    // Destory deletes a session by session ID.
    Destory(sid string) error
    // Regenerate regenerates a session store from old session ID to new one.
    Regenerate(oldsid, sid string) (RawStore, error)
    // Count counts and returns number of sessions.
    Count() int
    // GC calls GC to clean expired sessions.
    GC()
}
```


# 跨域请求攻击（CSRF）

中间件 csrf 用于为 [Macaron 实例](/zh-cn/core_concepts#macaron-shi-li) 生成和验证 CSRF 令牌。

* [GitHub](https://github.com/go-macaron/csrf)
* [API 文档](https://gowalker.org/github.com/go-macaron/csrf)

## 下载安装

```bash
go get github.com/go-macaron/csrf
```

## 使用示例

想要使用该中间件，您必须同时使用 [session](https://github.com/go-macaron/docs/tree/f112eb7b968ef7236b6731b5b4561f3f56c9194c/zh-CN/middlewares/session/README.md) 中间件。

```go
package main

import (
    "github.com/go-macaron/csrf"
    "github.com/go-macaron/session"
    "gopkg.in/macaron.v1"
)

func main() {
    m := macaron.Classic()
    m.Use(macaron.Renderer())
    m.Use(session.Sessioner())
    m.Use(csrf.Csrfer())

    // 模拟验证过程，判断 session 中是否存在 uid 数据。
    // 若不存在，则跳转到一个生成 CSRF 的页面。
    m.Get("/", func(ctx *macaron.Context, sess session.Store) {
        if sess.Get("uid") == nil {
            ctx.Redirect("/login")
            return
        }
        ctx.Redirect("/protected")
    })

    // 设置 session 中的 uid 数据。
    m.Get("/login", func(ctx *macaron.Context, sess session.Store) {
        sess.Set("uid", 123456)
        ctx.Redirect("/")
    })

    // 渲染一个需要验证的表单，并传递 CSRF 令牌到表单中。
    m.Get("/protected", func(ctx *macaron.Context, sess session.Store, x csrf.CSRF) {
        if sess.Get("uid") == nil {
            ctx.Redirect("/login", 401)
            return
        }

        ctx.Data["csrf_token"] = x.GetToken()
        ctx.HTML(200, "protected")
    })

    // 验证 CSRF 令牌。
    m.Post("/protected", csrf.Validate, func(ctx *macaron.Context, sess session.Store) {
        if sess.Get("uid") != nil {
            ctx.RenderData(200, []byte("You submitted a valid token"))
            return
        }
        ctx.Redirect("/login", 401)
    })

    m.Run()
}
```

```markup
<!-- templates/protected.tmpl -->
<form action="/protected" method="post">
    <input type="hidden" name="_csrf" value="{{.csrf_token}}">
    <button>提交</button>
</form>
```

## 自定义选项

该服务允许接受一个参数来进行自定义选项（[`csrf.Options`](https://gowalker.org/github.com/go-macaron/csrf#Options)）：

```go
// ...
m.Use(csrf.Csrfer(csrf.Options{
    // 用于生成令牌的全局秘钥，默认为随机字符串
    Secret:        "mysecret",
    // 用于传递令牌的 HTTP 请求头信息字段，默认为 "X-CSRFToken"
    Header:        "X-CSRFToken",
    // 用于传递令牌的表单字段名，默认为 "_csrf"
    Form:        "_csrf",
    // 用于传递令牌的 Cookie 名称，默认为 "_csrf"
    Cookie:        "_csrf",
    // Cookie 设置路径，默认为 "/"
    CookiePath:    "/",
    // 用于保存用户 ID 的 session 名称，默认为 "uid"
    SessionKey:    "uid",
    // 用于指定是否将令牌设置到响应的头信息中，默认为 false
    SetHeader:    false,
    // 用于指定是否将令牌设置到响应的 Cookie 中，默认为 false
    SetCookie:  false,
    // 用于指定是否要求只有使用 HTTPS 时才设置 Cookie，默认为 false
    Secure:     false,
    // 用于禁止请求头信息中包括 Origin 字段，默认为 false
    Origin:     false,
    // 错误处理函数，默认为简单的错误输出
    ErrorFunc:  func(w http.ResponseWriter) {
        http.Error(w, "Invalid csrf token.", http.StatusBadRequest)
    },
    }))
// ...
```


# 嵌入二进制数据

模块 bindata 用于为 [Macaron 实例](/zh-cn/core_concepts#macaron-shi-li) 提供支持内存的静态文件服务和模板文件系统。

* [GitHub](https://github.com/go-macaron/bindata)
* [API 文档](https://gowalker.org/github.com/go-macaron/bindata)

### 下载安装

```bash
go get github.com/go-macaron/bindata
```

## 使用示例

使用 [go-bindata](https://github.com/go-bindata/go-bindata) 将相应的静态文件和模板文件转换成单独的包。

导入相应的包并通过如下方法实现支持：

```go
import (
    "path/to/bindata/public"
    "path/to/bindata/templates"
    "github.com/go-macaron/bindata"
)

m.Use(macaron.Static("public",
    macaron.StaticOptions{
        FileSystem: bindata.Static(bindata.Options{
            Asset:      public.Asset,
            AssetDir:   public.AssetDir,
            AssetNames: public.AssetNames,
            Prefix:     "",
        }),
    },
))

m.Use(macaron.Renderer(macaron.RenderOptions{
    TemplateFileSystem: bindata.Templates(bindata.Options{
        Asset:      templates.Asset,
        AssetDir:   templates.AssetDir,
        AssetNames: templates.AssetNames,
        Prefix:     "",
    }),
}))
```


# 服务多个站点

辅助模块 switcher 为您的应用提供多个 [Macaron 实例](/zh-cn/core_concepts#macaron-shi-li) 的支持。

* [GitHub](https://github.com/go-macaron/switcher)
* [API 文档](https://gowalker.org/github.com/go-macaron/switcher)

## 下载安装

```bash
go get github.com/go-macaron/switcher
```

## 使用示例

如果您想要运行 2 个或 2 个以上的 [Macaron 实例](/zh-cn/core_concepts#macaron-shi-li) 在一个程序中，该辅助模块便可为此类需求提供便利：

```go
func main() {
    m1 := macaron.Classic()
    // 注册 m1 实例的中间件和路由

    m2 := macaron.Classic()
    // 注册 m2 实例的中间件和路由

    hs := switcher.NewHostSwitcher()
    // 设置实例所对应的主机地址
    hs.Set("gowalker.org", m1)
    hs.Set("gogs.io", m2)
    hs.Run()
}
```

默认情况下，即 `macaron.DEV` 模式，出于对调试的便利性，该程序会监听多个端口，包括 `4000`（用于实例 `m1`）和 `4001`（用于实例 `m2`）。而当模式为 `macaron.PROD` 时，则只会监听一个端口，即 `4000`。

### 动态匹配

如果您有多个子域名需要使用一个 Macaron 实例来处理，则可以通过以下方式来动态匹配：

```go
// ...
m := macaron.Classic()
// 注册 m 实例的中间件和路由

hs := macaron.NewHostSwitcher()
// 设置实例所对应的主机地址
hs.Set("*.example.com", m)
hs.Run()
// ...
```


# 常见问题

## 如何集成到我已有的服务中？

每个 [Macaron 实例](/zh-cn/core_concepts#macaron-shi-li) 都实现了 [`http.Handler`](https://gowalker.org/net/http#Handler) 接口，因此可以很容易地将它们以子集的形式集成到已有服务中。例如，您可以将 Macaron 应用集成到 GAE 中：

```go
package hello

import (
    "net/http"

    "gopkg.in/macaron.v1"
)

func init() {
    m := macaron.Classic()
    m.Get("/", func() string {
        return "Hello world!"
    })
    http.Handle("/", m)
}
```

## 如何修改监听地址和端口？

Macaron 的 `Run` 函数会首先根据环境变量 `PORT` 和 `HOST` 来确定监听地址和端口。如果未找到相应设置，则会默认使用 [localhost:4000](http://localhost:4000)。如果您想要更加灵活便利的方案，可以使用 [`http.ListenAndServe`](https://gowalker.org/net/http#ListenAndServe) 函数来实现。

```go
m := macaron.Classic()
// ...
log.Fatal(http.ListenAndServe(":8080", m))
```

或者以下方式：

* `m.Run("0.0.0.0")`，监听在 `0.0.0.0:4000`
* `m.Run(8080)`，监听在 `0.0.0.0:8080`
* `m.Run("0.0.0.0", 8080)`，监听在 `0.0.0.0:8080`

## 如何优雅地终止程序（Graceful Shutdown）？

```go
package main

import (
    ...
    "net/http"

    "gopkg.in/macaron.v1"
    "gopkg.in/tylerb/graceful.v1"
)

func main() {
    m := macaron.Classic()

    ...

    mux := http.NewServeMux()
    mux.Handle("/", m)
    graceful.Run(":4000", 60*time.Second, mux)
}
```

## 除了注入服务以外，如何在同一个请求内传递数据？

对象 [`*macaron.Context`](https://gowalker.org/github.com/go-macaron/macaron#Context) 中包含一个类型为 `map[string]interface{}` 的字段 `Data` 可供您在同个请求的不同处理器之间传递数据。

可以到 [这里](/zh-cn/middlewares/routing#gao-ji-lu-you-ding-yi) 查看使用方法。

## 为什么不直接使用 Martini 而要另外创建一个框架？

* 集成常用组件和方法来减少反射次数。
* 使用速度更快的多叉树路由替换原本的路由层。
* 更好地驱动 [Gogs](https://gogs.io) 项目。
* 对 Martini 源码进行一次深度学习。

## 为什么 Logo 是一条龙？

不应该是一种甜品吗？

正所谓 `马卡龙`，此龙乃是名为 `马卡` 的龙，哈哈！

## 有代码实时编译运行工具吗？

[Bra](https://github.com/unknwon/bra) 可以作为 Macaron 及其它应用的实时编译运行工具。


