Documentation ¶
Overview ¶
rest является простенькой библиотекой для быстрого создания web API проектов.
Основные ее достоинства в том, что она достаточно компактная, облегчает некоторые часто используемые вещи, поддерживает параметры в пути, совместима со стандартной библиотекой http и позволяет описывать обработчики в таком виде, как удобно мне.
Вообще, библиотека написана исключительно для внутреннего использования и нет никаких гарантий, что она не будет время от времени серьезно изменяться. Поэтому, если вы хотите использовать ее в своих проектах, то делайте fork и вперед.
Example ¶
package main import ( "net/http" "os" "github.com/geotrace/rest" ) func main() { var mux rest.ServeMux // инициализируем обработчик запросов // добавляем описание обработчиков, задавая пути, методы и функции их обработки mux.Handles(rest.Paths{ // при задании путей можно использовать именованные параметры с ':' "/user/:id": { "GET": func(c *rest.Context) error { // можно быстро сформировать ответ в JSON return c.Send(rest.JSON{"user": c.Param("id")}) }, // для одного пути можно сразу задать все обработчики для разных методов "POST": func(c *rest.Context) error { var data = make(rest.JSON) // можно быстро десериализовать JSON, переданный в запросе, в объект if err := c.Bind(&data); err != nil { // возвращать ошибки тоже удобно return err } return c.Send(rest.JSON{"user": c.Param("id"), "data": data}) }, }, // можно одновременно описать сразу несколько путей в одном месте "/message/:text": { "GET": func(c *rest.Context) error { // параметры пути получаются простым запросом return c.Send(rest.JSON{"message": c.Param("text")}) }, }, "/file/:name": { "GET": func(c *rest.Context) error { // поддерживает отдачу разного типа данных, в том числе и файлов file, err := os.Open(c.Param("name") + ".html") if err != nil { return err } defer file.Close() // можно получать не только именованные элементы пути, но // параметры, используемые в запросе if c.Param("format") == "raw" { c.ContentType = `text/plain; charset="utf-8"` } else { c.ContentType = `text/html; charset="utf-8"` } return c.Send(file) // отдаем содержимое файла }, }, "/favicon.ico": { // для работы со статическими файлами определена специальная функция "GET": rest.File("./favicon.ico"), }, }) // можно сразу задать базовый путь для всех URL, используемых в обработчиках mux.BasePath = "/api/v1" // можно задать глобальные заголовки для всех ответов mux.Headers = map[string]string{ "X-Powered-By": "My Server", } // т.к. поддерживается интерфейс http.Handler, то можно использовать // с любыми стандартными библиотеками http http.ListenAndServe(":8080", mux) }
Output:
Index ¶
- Variables
- func SetLogger(out io.Writer)
- type Coder
- type Context
- func (c *Context) Bind(obj interface{}) error
- func (c *Context) Data(key interface{}) interface{}
- func (c *Context) Error(code int, msg string) error
- func (c *Context) Flush()
- func (c *Context) Header() http.Header
- func (c *Context) Param(key string) string
- func (c *Context) Redirect(url string) error
- func (c *Context) Send(data interface{}) (err error)
- func (c *Context) SetData(key, value interface{})
- func (c *Context) Status(code int) *Context
- func (c *Context) Write(data []byte) (int, error)
- func (c *Context) WriteHeader(code int)
- type Handler
- type JSON
- type JSONCoder
- type Methods
- type Paths
- type ServeMux
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ( // Взведенный флаг Debug указывает, что описания ошибок возвращаются как // есть. В противном случае всегда возвращается только стандартное описание // статуса HTTP, сформированное на базе этой ошибки. Debug bool = false // Флаг Compress разрешает сжатие данных. Чтобы запретить сжимать данные, // установите значение данного флага в false. При инициализации сжатия // проверяется, что оно уже не включено, например, на уровне глобального // обработчика запросов и, в этом случае, сжатие не будет включено, даже // если флаг установлен. Compress bool = true // Encoder описывает функции, используемые для разбора запроса и кодирования // ответа. MaxBody задает максимальный размер поддерживаемого запроса. // Если размер превышает указанный, то возвращается ошибка. Если не хочется // ограничений, то можно установить значение 0, тогда проверка производиться // не будет. Encoder Coder = NewJSONCoder(1 << 15) // 32 мегабайта // EncodeError управляет форматом вывода ошибок: если флаг не взведен, то // ошибки отдаются как текст. В противном случае описание ошибок // кодируются с помощью Encoder и содержат статус и описание ошибки. EncodeError bool = true )
var ( ErrDataAlreadySent = errors.New("data already sent") ErrBadRequest = errors.New("bad request") // 400 ErrForbidden = errors.New("forbidden") // 403 ErrNotFound = errors.New("not found") // 404 ErrLengthRequired = errors.New("length required") // 411 ErrRequestEntityTooLarge = errors.New("request entity too large") // 413 ErrUnsupportedMediaType = errors.New("unsupported media type") // 415 ErrInternalServerError = errors.New("internal server error") // 500 ErrNotImplemented = errors.New("not implemented") // 501 )
Эти ошибки обрабатываются при передаче их в метод Context.Send и устанавливают соответствующий статус ответа.
Кроме указанных здесь ошибок, так же проверяется, что ошибка отвечает на os.IsNotExist (в этом случае статус станет 404) или os.IsPermission (статус 403). Все остальные ошибки устанавливают статус 500.
Functions ¶
Types ¶
type Context ¶
type Context struct { *http.Request // HTTP запрос в разобранном виде ContentType string // тип информации в ответе // contains filtered or unexported fields }
Context содержит контекстную информацию HTTP-запроса и методы формирования ответа на них. Т.к. http.Request импортируется в Context напрямую, то можно использовать все его свойства и методы, как родные свойства и методы самого контекста.
Context скрывает http.ResponseWriter от прямого использования и, вместо этого, предоставляет свои методы для формирования ответа. Это позволяет обойти некоторые скользкие моменты и, иногда, несколько упростить код.
При отдаче ответа анализируются первые байты данных и на основании них устанавливается тип ответа. Если вы хотите определить тип ответа самостоятельно, то проще всего установить значение ContentType строкой с описанием нужного типа.
Есть два пути отослать в ответ ошибку: послать ее через Context.Send или вернуть ее из обработчика. Результат, в конечном счете, будет приблизительно одинаковый: разница только в том, что при возврате ошибки из обработчика, она будет записана в лог, а в случае посылки ее через Send — нет. Чтобы не путаться и не выбирать наилучший способ, просто рассматривайте возврат ошибки из обработчика, как мягкий вариант паники (panic).
func (*Context) Bind ¶
Bind разбирает данные запроса и заполняет ими указанный в параметре объект. Разбор осуществляется с помощью Encoder.
Example ¶
package main import ( "github.com/geotrace/rest" ) var c = new(rest.Context) func main() error { // инициализируем формат данных для разбора data := make(map[string]interface{}) // читаем запрос и получаем данные в разобранном виде if err := c.Bind(&data); err != nil { return err } // возвращаем эти же данные в ответ return c.Send(data) }
Output:
func (*Context) Data ¶
func (c *Context) Data(key interface{}) interface{}
Data возвращает пользовательские данные, сохраненные в контексте запроса с указанным ключем. Обычно эти данные используются, когда необходимо передать их между несколькими обработчиками.
func (*Context) Error ¶
Error отправляет указанный текст как описание ошибки. В зависимости от флага EncodeError, данный текст будет отдан как описание или как JSON с кодом статуса. В отличии от обычных ошибок, на данный текст не распространяется правило отладки и текст будет отдан в неизменном виде, в не зависимости от установленного значения Debug.
func (*Context) Flush ¶
func (c *Context) Flush()
Flush отдает накопленный буфер с ответом. Используется для поддержки интерфейса http.Flusher.
func (*Context) Header ¶
Header возвращает HTTP-заголовки ответа. Используется для поддержки интерфейса http.ResponseWriter.
func (*Context) Param ¶
Param возвращает значение именованного параметра. Если параметр с таким именем не найден, то возвращается значение параметра из URL с тем же именем.
Разобранные параметры запроса пути сохраняются внутри Context и повторного его разбора уже не требует. Но это происходит только при первом к ним обращении.
func (*Context) Redirect ¶
Redirect отсылает ответ с требованием временного перехода по указанному URL. Ошибка никогда не возвращается.
func (*Context) Send ¶
Send отсылает переданные данные как ответ на запрос. В зависимости от типа данных, используются разные форматы ответов. Поддерживаются данные в формате string, error, []byte, io.Reader и nil. Все остальные типы данных приводятся к формату JSON.
Данный метод можно использовать только один раз: после того, как ответ отправлен, повторный вызов данного метода сразу возвращает ошибку.
Example (File) ¶
package main import ( "os" "github.com/geotrace/rest" ) var c = new(rest.Context) func main() error { // открываем файл file, err := os.Open("README.md") if err != nil { return err } defer file.Close() // устанавливаем тип отдаваемых данных c.ContentType = "text/markdown; charset=UTF-8" // отдаем содержимое файла в качестве ответа return c.Send(file) }
Output:
Example (Json) ¶
package main import ( "github.com/geotrace/rest" ) var c = new(rest.Context) func main() error { // отдаем ответ в формате JSON, беря идентификатор пользователя // из параметров пути или запроса return c.Send(rest.JSON{"user": c.Param("id")}) }
Output:
func (*Context) SetData ¶
func (c *Context) SetData(key, value interface{})
SetData сохраняет пользовательские данные в контексте запроса с указанным ключем.
Рекомендуется в качестве ключа использовать какой-нибудь приватный тип и его значение, чтобы избежать случайного затирания данных другими обработчиками: это гарантированно обезопасит от случайного доступа к ним. Но строки тоже поддерживаются. :)
Example ¶
package main import ( "fmt" "github.com/geotrace/rest" ) var c = new(rest.Context) func main() { type myKeyType byte // определяем собственный тип данных var myKey myKeyType = 1 // генерируем уникальный ключ данных // сохраняем данные в контексте, используя уникальный ключ c.SetData(myKey, "Test data") // читаем данные с помощью ключа str := c.Data(myKey).(string) fmt.Println(str) }
Output:
func (*Context) Status ¶
Status устанавливает код HTTP-ответа, который будет отправлен сервером. Вызов данного метода не приводит к немедленной отправке ответа, а только устанавливает внутренний статус. Статус должен быть в диапазоне от 200 до 599, в противном случае статус не изменяется.
Метод возвращает ссылку на основной контекст, чтобы можно было использовать его в последовательности выполнения команд. Например, можно сразу установить код ответа и тут же опубликовать данные.
Example ¶
package main import ( "github.com/geotrace/rest" ) var c = new(rest.Context) func main() error { // возвращаем 201 код окончания return c.Status(201).Send(nil) }
Output:
func (*Context) Write ¶
Write записывает данные в качестве ответа сервера. Может вызываться несколько раз. Используется для поддержки интерфейса http.ResponseWriter.
При первом вызове (может быть не явный) автоматически устанавливается статус ответа. Если статус ответа был не задан, то будет использован статус 200 (ОК). Так же, если не был задан ContentType, то он будет определен автоматически на основании анализа первых байт данных.
func (*Context) WriteHeader ¶
WriteHeader записывает заголовок ответа. Вызов метода автоматически взводит внутренний флаг, что отправка ответа начата. После его вызова отсылка каких-либо данных другим способом, кроме Write, уже не поддерживается. Используется для поддержки интерфейса http.ResponseWriter.
type Handler ¶
Handler может являться любая функция, которая принимает Context и может возвращать ошибку. Возвращаемая ошибка может быть записана в лог и, если сервер еще не отсылал никакого ответа, то будет возвращена вместе с ошибкой в качестве ответа.
func Data ¶
Data постоянно отдает указанные в параметрах данные в виде ответа на запрос.
Example ¶
package main import ( "github.com/geotrace/rest" ) var mux rest.ServeMux func main() { mux.Handle("GET", "/static/", rest.Data("OK", "")) mux.Handle("GET", "/bin/", rest.Data([]byte{0x1, 0x2, 0x3, 0x4}, "application/octet-stream")) }
Output:
func File ¶
File отдает на запрос содержимое файла с указанным именем.
Example ¶
package main import ( "github.com/geotrace/rest" ) var mux rest.ServeMux func main() { mux.Handle("GET", "/favicon.ico", rest.File("./favicon.ico")) }
Output:
func Files ¶
Files отдает файлы по имени из указанного каталога. Имя файла задается в пути в виде последнего именованного параметра.
Example ¶
package main import ( "github.com/geotrace/rest" ) var mux rest.ServeMux func main() { mux.Handle("GET", "/files/:name", rest.Files("./tmp/")) }
Output:
func Handlers ¶
Handlers объединяет несколько обработчиков запросов в очередь. Они будут выполняться в той последовательности, в которой были добавлены одна за другой, пока не будут выполнены все или пока не вернется первая ошибка, которая прерывает процес дальнейшей обработки. С помощью этой функции можно объединять несколько обработчиков в один.
func Redirect ¶
Redirect возвращает Handler, который осуществляет постоянное перенаправление на указанный в параметрах URL.
Example ¶
package main import ( "github.com/geotrace/rest" ) var mux rest.ServeMux func main() { mux.Handle("GET", "/redirect/", rest.Redirect("/json/")) }
Output:
func (Handler) ServeHTTP ¶
func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request)
ServeHTTP поддерживает интерфейс http.Handler для Handler, что позволяет использовать его с любыми совместимыми с http.Handler библиотеками.
Example ¶
package main import ( "net/http" "time" "github.com/geotrace/rest" ) func main() { http.ListenAndServe(":8080", rest.Handler(func(c *rest.Context) error { return c.Send(rest.JSON{ "user": "name", "date": time.Now().UTC(), }) })) }
Output:
type JSON ¶
type JSON map[string]interface{}
JSON позволяет быстро описать данные в одноименном формате.
type JSONCoder ¶
type JSONCoder struct {
MaxBody int64 // максимально допустимый размер запроса
}
JSONCoder осуществляет разбор запроса и кодирование ответа в формате JSON.
func NewJSONCoder ¶
NewJSONCoder возвращает новый инициализированный Coder, поддерживающий формат JSON.
func (JSONCoder) Bind ¶
Bind разбирает данные запроса в формате JSON и заполняет ими указанный в параметре объект.
Если Content-Type запроса не соответствует "application/json", то возвращается ошибка ErrUnsupportedMediaType. Так же может возвращать ошибку ErrLengthRequired, если не указана длина запроса, ErrRequestEntityTooLarge — если запрос превышает значение MaxBody, и ErrBadRequest — если не смогли разобрать запрос и поместить результат разбора в объект obj. Все эти ошибки поддерживаются методом Send и отдают соответствующий статус ответа на запрос.
type Paths ¶
Paths позволяет описать сразу несколько обработчиков для разных путей и методов: ключем для данного словаря как раз являются пути запросов. Используется в качестве аргумента при вызове метода ServeMux.Handles.
type ServeMux ¶
type ServeMux struct { // Позволяет задать базовый путь для всех запросов. BasePath string // Описывает дополнительные заголовки HTTP-ответа, которые будут добавлены // ко всем ответам, возвращаемым данным обработчиком Headers map[string]string // contains filtered or unexported fields }
ServeMux описывает список обработчиков, ассоциированных с путями запроса и методами.
func (*ServeMux) Handle ¶
Handle регистрирует обработчик для указанного метода и пути. В описании пути можно использовать именованные параметры (начинаются с символа ':') и завершающий именованный параметр (начинается с '*'), который указывает, что path может быть длиннее. В последнем случае вся остальная часть пути будет включена в данный параметр. Параметр со звездочкой, если указан, должен быть самым последним параметром пути.
Если количество элементов пути в path больше 32768 или параметр со звездочкой используется не в самом последнем элементе пути, то возникает panic.
Example ¶
package main import ( "time" "github.com/geotrace/rest" ) var mux rest.ServeMux func main() { mux.Handle("GET", "/json/", func(c *rest.Context) error { return c.Send(rest.JSON{ "user": "name", "date": time.Now().UTC(), }) }) }
Output:
func (ServeMux) Handler ¶
Handler отвечает за подбор обработчика и его выполнение.
Если обработчик для данного пути и метода не найден, но есть обработчики для других методов, то возвращается статус http.StatusMethodNotAllowed и в заголовке передается список методов, которые можно применить к данному пути. В противном случае возвращается статус http.StatusNotFound.
func (*ServeMux) Handles ¶
Handles добавляет сразу список обработчиков для нескольких путей и методов. Это, по сути, просто удобный способ сразу определить большое количество обработчиков, не вызывая каждый раз ServeMux.Handle.
Example ¶
package main import ( "net/http" "github.com/geotrace/rest" ) type User struct{} func (User) get(*rest.Context) error { return nil } func (User) post(*rest.Context) error { return nil } func secure(h rest.Handler) rest.Handler { return h } var ( user User getMessage = func(*rest.Context) error { return nil } getFile = getMessage ) func main() { var mux rest.ServeMux mux.Handles(rest.Paths{ "/user/:id": { "GET": user.get, "POST": user.post, }, "/message/:text": {"GET": getMessage}, "/file/:name": {"GET": secure(getFile)}, }) // т.к. поддерживается интерфейс http.Handler, то можно использовать // с любыми стандартными библиотеками http.ListenAndServe(":8080", mux) }
Output:
func (ServeMux) ServeHTTP ¶
func (m ServeMux) ServeHTTP(w http.ResponseWriter, r *http.Request)
ServeHTTP обеспечивает поддержку интерфейса http.Handler. Таким образом, данный ServeMux можно использовать как стандартный обработчик HTTP-запросов.
В процессе обработки запроса отслеживаются возвращаемые ошибки и перехватываются возможные вызовы panic. Если ответ на запрос еще не отправлялся, то в этих случаях в ответ будет отправлена ошибка.
Example ¶
package main import ( "net/http" "time" "github.com/geotrace/rest" ) var mux rest.ServeMux func main() { mux.Handles(rest.Paths{ "/user/:id": { "GET": func(c *rest.Context) error { return c.Send(rest.JSON{ "user": c.Param("id"), "date": time.Now().UTC(), }) }, "POST": rest.Data("OK", "text/plain"), }, "/favicon.ico": { "GET": rest.File("./favicon.ico"), }, }) http.ListenAndServe(":8080", mux) }
Output: