diff --git a/content/ru/_index.html b/content/ru/_index.html new file mode 100644 index 000000000..85c54d4f2 --- /dev/null +++ b/content/ru/_index.html @@ -0,0 +1,77 @@ ++++ +title = "Gin Web Framework" +linkTitle = "Gin Web Framework" ++++ + +{{< blocks/cover title="Gin Web Framework" image_anchor="top" height="full" color="orange" >}} +
+ }}"> + Подробнее + + + Скачать + +

Самый быстрый полнофункциональный веб-фреймворк для Go. Кристально чистый.

+
+ {{< blocks/link-down color="info" >}} +
+{{< /blocks/cover >}} + + +{{% blocks/lead color="white" %}} + +**Что такое Gin?** + +Gin - это веб-фреймворк, написанный на языке Golang. + +В нем реализован API, похожий на Martini, но производительность в 40 раз выше, чем у Martini. + +Если вам нужна производительность и продуктивность, Gin вам понравится. + +{{% /blocks/lead %}} + +{{< blocks/section color="light" >}} + +{{% blocks/feature icon="fa fa-tachometer-alt" title="Fast" %}} +Маршрутизация на основе радиксного дерева, малый объем памяти. Никаких отражений. Предсказуемая производительность API. +{{% /blocks/feature %}} + +{{% blocks/feature icon="fa fa-bars" title="Middleware support" %}} +Входящий HTTP-запрос может быть обработан цепочкой промежуточного ПО (Middleware) и конечным действием. +Например: Логгер, авторизация, GZIP и, наконец, публикация сообщения в БД. +{{% /blocks/feature %}} + +{{% blocks/feature icon="fa fa-life-ring" title="Crash-free" %}} +Gin может поймать панику, возникшую во время HTTP-запроса, и восстановить ее. Таким образом, ваш сервер будет всегда доступен. Также можно сообщить об этой панике, например, в Sentry! +{{% /blocks/feature %}} + +{{< /blocks/section >}} + +{{< blocks/section color="white" >}} + +{{% blocks/feature icon="fa fa-check-circle" title="JSON validation" %}} +Gin может анализировать и проверять JSON-запросы, например, на наличие необходимых значений. +{{% /blocks/feature %}} + +{{% blocks/feature icon="fa fa-users-cog" title="Routes grouping" %}} +Организуйте свои маршруты лучше. Требуется и не требуется авторизация, разные версии API. Кроме того, группы могут быть вложены бесконечно без снижения производительности. +{{% /blocks/feature %}} + + +{{% blocks/feature icon="fa fa-briefcase" title="Error management" %}} +Gin обеспечивает удобный способ сбора всех ошибок, возникших во время HTTP-запроса. В конечном итоге промежуточное ПО (Middleware) может записывать их в файл журнала, в базу данных и отправлять через сеть. +{{% /blocks/feature %}} + +{{< /blocks/section >}} + +{{< blocks/section color="info" >}} + +{{% blocks/feature icon="fa fa-images" title="Rendering built-in" %}} +Gin предоставляет простой в использовании API для рендеринга JSON, XML и HTML. +{{% /blocks/feature %}} + +{{% blocks/feature icon="fa fa-code" title="Extendable" %}} +Создать новое промежуточное программное обеспечение очень просто, просто посмотрите пример кода. +{{% /blocks/feature %}} + +{{< /blocks/section >}} diff --git a/content/ru/blog/_index.md b/content/ru/blog/_index.md new file mode 100644 index 000000000..bfe08e93d --- /dev/null +++ b/content/ru/blog/_index.md @@ -0,0 +1,13 @@ +--- +title: "Blog" +linkTitle: "Blog" +menu: + main: + weight: 30 +--- + + +This is the **blog** section. It has two categories: News and Releases. + +Files in these directories will be listed in reverse chronological order. + diff --git a/content/ru/blog/news/_index.md b/content/ru/blog/news/_index.md new file mode 100644 index 000000000..3263a4f27 --- /dev/null +++ b/content/ru/blog/news/_index.md @@ -0,0 +1,8 @@ + +--- +title: "Новости" +linkTitle: "News" +weight: 20 +--- + + diff --git a/content/ru/blog/news/how-to-build-one-effective-middleware.md b/content/ru/blog/news/how-to-build-one-effective-middleware.md new file mode 100644 index 000000000..03b4fb4cc --- /dev/null +++ b/content/ru/blog/news/how-to-build-one-effective-middleware.md @@ -0,0 +1,143 @@ +--- +title: "Как создать эффективное промежуточное программное обеспечение? (Middleware)" +linkTitle: "How to build one effective middleware?" +date: 2019-02-26 +--- + +## Constituent parts + +Промежуточное ПО (Middleware) состоит из двух частей: + + - первая часть - это то, что выполняется один раз, когда вы инициализируете промежуточное ПО. В ней вы устанавливаете все глобальные объекты, логики и т. д. Все это происходит один раз за время жизни приложения. + + - Вторая часть - это то, что выполняется при каждом запросе. Например, в промежуточном ПО для работы с базами данных вы просто вводите свой "глобальный" объект базы данных в контекст. Как только он окажется внутри контекста, вы сможете получить его из других промежуточных модулей и из вашей функции-обработчика. + +```go +func funcName(params string) gin.HandlerFunc { + // <--- + // This is part one + // ---> + // The following code is an example + if err := check(params); err != nil { + panic(err) + } + + return func(c *gin.Context) { + // <--- + // This is part two + // ---> + // The following code is an example + c.Set("TestVar", params) + c.Next() + } +} +``` + +## Процесс выполнения + +Во-первых, у нас есть следующий пример кода: + +```go +func main() { + router := gin.Default() + + router.Use(globalMiddleware()) + + router.GET("/rest/n/api/*some", mid1(), mid2(), handler) + + router.Run() +} + +func globalMiddleware() gin.HandlerFunc { + fmt.Println("globalMiddleware...1") + + return func(c *gin.Context) { + fmt.Println("globalMiddleware...2") + c.Next() + fmt.Println("globalMiddleware...3") + } +} + +func handler(c *gin.Context) { + fmt.Println("exec handler.") +} + +func mid1() gin.HandlerFunc { + fmt.Println("mid1...1") + + return func(c *gin.Context) { + + fmt.Println("mid1...2") + c.Next() + fmt.Println("mid1...3") + } +} + +func mid2() gin.HandlerFunc { + fmt.Println("mid2...1") + + return func(c *gin.Context) { + fmt.Println("mid2...2") + c.Next() + fmt.Println("mid2...3") + } +} +``` + +Согласно [Составные части](#Constituent-parts), когда мы запускаем процесс джина, **часть первая** будет выполняться первой и выведет следующую информацию: + +```go +globalMiddleware...1 +mid1...1 +mid2...1 +``` + +And init order is: + +```go +globalMiddleware...1 + | + v +mid1...1 + | + v +mid2...1 +``` + +Когда мы выполним один запрос `curl -v localhost:8080/rest/n/api/some`, **часть вторая** выполнит свое промежуточное ПО и выведет следующую информацию: + +```go +globalMiddleware...2 +mid1...2 +mid2...2 +exec handler. +mid2...3 +mid1...3 +globalMiddleware...3 +``` + +Другими словами, порядок выполнения таков: + +```go +globalMiddleware...2 + | + v +mid1...2 + | + v +mid2...2 + | + v +exec handler. + | + v +mid2...3 + | + v +mid1...3 + | + v +globalMiddleware...3 +``` + + diff --git a/content/ru/blog/releases/_index.md b/content/ru/blog/releases/_index.md new file mode 100644 index 000000000..c045332fe --- /dev/null +++ b/content/ru/blog/releases/_index.md @@ -0,0 +1,8 @@ + +--- +title: "Релизы" +linkTitle: "Releases" +weight: 20 +--- + + diff --git a/content/ru/blog/releases/release13.md b/content/ru/blog/releases/release13.md new file mode 100644 index 000000000..43f4656f4 --- /dev/null +++ b/content/ru/blog/releases/release13.md @@ -0,0 +1,29 @@ +--- +title: "Gin 1.3.0 is released" +linkTitle: "Gin 1.3.0 is released" +date: 2018-08-14 +--- + +### CHANGELOG + +- [NEW] Add [`func (*Context) QueryMap`](https://pkg.go.dev/github.com/gin-gonic/gin#Context.QueryMap), [`func (*Context) GetQueryMap`](https://pkg.go.dev/github.com/gin-gonic/gin#Context.GetQueryMap), [`func (*Context) PostFormMap`](https://pkg.go.dev/github.com/gin-gonic/gin#Context.PostFormMap) and [`func (*Context) GetPostFormMap`](https://pkg.go.dev/github.com/gin-gonic/gin#Context.GetPostFormMap) to support `type map[string]string` as query string or form parameters, see [#1383](https://github.com/gin-gonic/gin/pull/1383) +- [NEW] Add [`func (*Context) AsciiJSON`](https://pkg.go.dev/github.com/gin-gonic/gin#Context.AsciiJSON), see [#1358](https://github.com/gin-gonic/gin/pull/1358) +- [NEW] Add `Pusher()` in [`type ResponseWriter`](https://pkg.go.dev/github.com/gin-gonic/gin#ResponseWriter) for supporting http2 push, see [#1273](https://github.com/gin-gonic/gin/pull/1273) +- [NEW] Add [`func (*Context) DataFromReader`](https://pkg.go.dev/github.com/gin-gonic/gin#Context.DataFromReader) for serving dynamic data, see [#1304](https://github.com/gin-gonic/gin/pull/1304) +- [NEW] Add [`func (*Context) ShouldBindBodyWith`](https://pkg.go.dev/github.com/gin-gonic/gin#Context.ShouldBindBodyWith) allowing to call binding multiple times, see [#1341](https://github.com/gin-gonic/gin/pull/1341) +- [NEW] Support pointers in form binding, see [#1336](https://github.com/gin-gonic/gin/pull/1336) +- [NEW] Add [`func (*Context) JSONP`](https://pkg.go.dev/github.com/gin-gonic/gin#Context.JSONP), see [#1333](https://github.com/gin-gonic/gin/pull/1333) +- [NEW] Support default value in form binding, see [#1138](https://github.com/gin-gonic/gin/pull/1138) +- [NEW] Expose validator engine in [`type StructValidator`](https://pkg.go.dev/github.com/gin-gonic/gin/binding#StructValidator), see [#1277](https://github.com/gin-gonic/gin/pull/1277) +- [NEW] Add [`func (*Context) ShouldBind`](https://pkg.go.dev/github.com/gin-gonic/gin#Context.ShouldBind), [`func (*Context) ShouldBindQuery`](https://pkg.go.dev/github.com/gin-gonic/gin#Context.ShouldBindQuery) and [`func (*Context) ShouldBindJSON`](https://pkg.go.dev/github.com/gin-gonic/gin#Context.ShouldBindJSON), see [#1047](https://github.com/gin-gonic/gin/pull/1047) +- [NEW] Add support for `time.Time` location in form binding, see [#1117](https://github.com/gin-gonic/gin/pull/1117) +- [NEW] Add [`func (*Context) BindQuery`](https://pkg.go.dev/github.com/gin-gonic/gin#Context.BindQuery), see [#1029](https://github.com/gin-gonic/gin/pull/1029) +- [NEW] Make [jsonite](https://github.com/json-iterator/go) optional with build tags, see [#1026](https://github.com/gin-gonic/gin/pull/1026) +- [NEW] Show query string in logger, see [#999](https://github.com/gin-gonic/gin/pull/999) +- [NEW] Add [`func (*Context) SecureJSON`](https://pkg.go.dev/github.com/gin-gonic/gin#Context.SecureJSON), see [#987](https://github.com/gin-gonic/gin/pull/987) and [#993](https://github.com/gin-gonic/gin/pull/993) +- [DEPRECATE] `func (*Context) GetCookie` for [`func (*Context) Cookie`](https://pkg.go.dev/github.com/gin-gonic/gin#Context.Cookie) +- [FIX] Don't display color tags if [`func DisableConsoleColor`](https://pkg.go.dev/github.com/gin-gonic/gin#DisableConsoleColor) called, see [#1072](https://github.com/gin-gonic/gin/pull/1072) +- [FIX] Gin Mode `""` when calling [`func Mode`](https://pkg.go.dev/github.com/gin-gonic/gin#Mode) now returns `const DebugMode`, see [#1250](https://github.com/gin-gonic/gin/pull/1250) +- [FIX] `Flush()` now doesn't overwrite `responseWriter` status code, see [#1460](https://github.com/gin-gonic/gin/pull/1460) + + diff --git a/content/ru/blog/releases/release14.md b/content/ru/blog/releases/release14.md new file mode 100644 index 000000000..23fd64829 --- /dev/null +++ b/content/ru/blog/releases/release14.md @@ -0,0 +1,66 @@ +--- +title: "Gin 1.4.0 is released" +linkTitle: "Gin 1.4.0 is released" +date: 2019-05-08 +--- + +### CHANGELOG + +#### Feature + +- [NEW] Support for [Go Modules](https://github.com/golang/go/wiki/Modules) [#1569](https://github.com/gin-gonic/gin/pull/1569) +- [NEW] Refactor of form mapping multipart request [#1829](https://github.com/gin-gonic/gin/pull/1829) +- [NEW] Supporting file binding [#1264](https://github.com/gin-gonic/gin/pull/1264) +- [NEW] Add support for mapping arrays [#1797](https://github.com/gin-gonic/gin/pull/1797) +- [NEW] Make context.Keys available as LogFormatterParams [#1779](https://github.com/gin-gonic/gin/pull/1779) +- [NEW] Use internal/json for Marshal/Unmarshal [#1791](https://github.com/gin-gonic/gin/pull/1791) +- [NEW] Support mapping time.Duration [#1794](https://github.com/gin-gonic/gin/pull/1794) +- [NEW] Refactor form mappings [#1749](https://github.com/gin-gonic/gin/pull/1749) +- [NEW] Added flag to context.Stream indicates if client disconnected in middle of stream [#1252](https://github.com/gin-gonic/gin/pull/1252) +- [NEW] Extend context.File to allow for the content-dispositon attachments via a new method context.Attachment [#1260](https://github.com/gin-gonic/gin/pull/1260) +- [NEW] Add prefix from X-Forwarded-Prefix in redirectTrailingSlash [#1238](https://github.com/gin-gonic/gin/pull/1238) +- [NEW] Add context.HandlerNames() [#1729](https://github.com/gin-gonic/gin/pull/1729) +- [NEW] Add response size to LogFormatterParams [#1752](https://github.com/gin-gonic/gin/pull/1752) +- [NEW] Allow ignoring field on form mapping [#1733](https://github.com/gin-gonic/gin/pull/1733) +- [NEW] Add a function to force color in console output. [#1724](https://github.com/gin-gonic/gin/pull/1724) +- [NEW] Binding for URL Params [#1694](https://github.com/gin-gonic/gin/pull/1694) +- [NEW] Add LoggerWithFormatter method [#1677](https://github.com/gin-gonic/gin/pull/1677) +- [NEW] RunFd method to run http.Server through a file descriptor [#1609](https://github.com/gin-gonic/gin/pull/1609) +- [NEW] Yaml binding support [#1618](https://github.com/gin-gonic/gin/pull/1618) +- [NEW] Add PureJSON renderer [#694](https://github.com/gin-gonic/gin/pull/694) +- [NEW] Set default time format in form binding [#1487](https://github.com/gin-gonic/gin/pull/1487) +- [NEW] Upgrade dependency libraries [#1491](https://github.com/gin-gonic/gin/pull/1491) + +#### Bug fix + +- [FIX] Truncate Latency precision in long running request [#1830](https://github.com/gin-gonic/gin/pull/1830) +- [FIX] IsTerm flag should not be affected by DisableConsoleColor method. [#1802](https://github.com/gin-gonic/gin/pull/1802) +- [FIX] Readme updates [#1793](https://github.com/gin-gonic/gin/pull/1793) [#1788](https://github.com/gin-gonic/gin/pull/1788) [1789](https://github.com/gin-gonic/gin/pull/1789) +- [FIX] StaticFS: Fixed Logging two log lines on 404. [#1805](https://github.com/gin-gonic/gin/pull/1805), [#1804](https://github.com/gin-gonic/gin/pull/1804) +- [FIX] Moved [examples](https://github.com/gin-gonic/examples) to stand alone Repo [#1775](https://github.com/gin-gonic/gin/pull/1775) +- [FIX] Support HTTP content negotiation wildcards [#1112](https://github.com/gin-gonic/gin/pull/1112) +- [FIX] Pass MaxMultipartMemory when FormFile is called [#1600](https://github.com/gin-gonic/gin/pull/1600) +- [FIX] LoadHTML* tests [#1559](https://github.com/gin-gonic/gin/pull/1559) +- [FIX] Removed use of sync.pool from HandleContext [#1565](https://github.com/gin-gonic/gin/pull/1565) +- [FIX] Format output log to os.Stderr [#1571](https://github.com/gin-gonic/gin/pull/1571) +- [FIX] Make logger use a yellow background and a darkgray text for legibility [#1570](https://github.com/gin-gonic/gin/pull/1570) +- [FIX] Remove sensitive request information from panic log. [#1370](https://github.com/gin-gonic/gin/pull/1370) +- [FIX] log.Println() does not print timestamp [#829](https://github.com/gin-gonic/gin/pull/829) [#1560](https://github.com/gin-gonic/gin/pull/1560) +- [FIX] Add missing copyright and update if/else [#1497](https://github.com/gin-gonic/gin/pull/1497) +- [FIX] Update msgpack usage [#1498](https://github.com/gin-gonic/gin/pull/1498) +- [FIX] Use protobuf on render [#1496](https://github.com/gin-gonic/gin/pull/1496) +- [FIX] Add support for Protobuf format response [#1479](https://github.com/gin-gonic/gin/pull/1479) +- [FIX] Add BindXML and ShouldBindXML [#1485](https://github.com/gin-gonic/gin/pull/1485) +- [FIX] CI testing updates [#1671](https://github.com/gin-gonic/gin/pull/1671) [#1670](https://github.com/gin-gonic/gin/pull/1670) [#1682](https://github.com/gin-gonic/gin/pull/1682) [#1669](https://github.com/gin-gonic/gin/pull/1669) +- [FIX] StaticFS(): Send 404 when path does not exist [#1663](https://github.com/gin-gonic/gin/pull/1663) +- [FIX] Handle nil body for JSON binding [#1638](https://github.com/gin-gonic/gin/pull/1638) +- [FIX] Support bind uri param [#1612](https://github.com/gin-gonic/gin/pull/1612) +- [FIX] recovery: fix issue with syscall import on google app engine [#1640](https://github.com/gin-gonic/gin/pull/1640) +- [FIX] Make sure the debug log contains line breaks [#1650](https://github.com/gin-gonic/gin/pull/1650) +- [FIX] Panic stack trace being printed during recovery of broken pipe [#1089](https://github.com/gin-gonic/gin/pull/1089) [#1259](https://github.com/gin-gonic/gin/pull/1259) +- [FIX] Context.Next() - recheck len of handlers on every iteration. [#1745](https://github.com/gin-gonic/gin/pull/1745) +- [FIX] Fix all errcheck warnings [#1739](https://github.com/gin-gonic/gin/pull/1739) [#1653](https://github.com/gin-gonic/gin/pull/1653) +- [FIX] Change color methods to public in the defaultLogger. [#1771](https://github.com/gin-gonic/gin/pull/1771) +- [FIX] Update writeHeaders method to use http.Header.Set [#1722](https://github.com/gin-gonic/gin/pull/1722) +- [FIX] context.Copy() race condition [#1020](https://github.com/gin-gonic/gin/pull/1020) + diff --git a/content/ru/blog/releases/release15.md b/content/ru/blog/releases/release15.md new file mode 100644 index 000000000..e4593b3ce --- /dev/null +++ b/content/ru/blog/releases/release15.md @@ -0,0 +1,47 @@ +--- +title: "Gin 1.5.0 is released" +linkTitle: "Gin 1.5.0 is released" +date: 2019-11-28 +--- + +### CHANGELOG + +#### Feature + +- [NEW] Now you can parse the inline lowercase start structure [#1893](https://github.com/gin-gonic/gin/pull/1893) +- [NEW] **[Break-Backward]** Hold matched route full path in the Context [#1826](https://github.com/gin-gonic/gin/pull/1826) +- [NEW] Add context param query cache [#1450](https://github.com/gin-gonic/gin/pull/1450) +- [NEW] Add support of multipart multi files [#1949](https://github.com/gin-gonic/gin/pull/1949) +- [NEW] Support bind http header param [#1957](https://github.com/gin-gonic/gin/pull/1957) +- [NEW] Support bind unix time [#1980](https://github.com/gin-gonic/gin/pull/1980) +- [NEW] Support negative Content-Length in DataFromReader [#1981](https://github.com/gin-gonic/gin/pull/1981) +- [NEW] Add DisallowUnknownFields() in gin.Context.BindJSON() [#2028](https://github.com/gin-gonic/gin/pull/2028) +- [NEW] Use specific `net.Listener` with Engine.RunListener() [#2023](https://github.com/gin-gonic/gin/pull/2023) + +#### Bug fix + +- [FIX] Use DefaultWriter and DefaultErrorWriter for debug messages [#1891](https://github.com/gin-gonic/gin/pull/1891) +- [FIX] Some code improvements [#1909](https://github.com/gin-gonic/gin/pull/1909) +- [FIX] Use encode replace json marshal increase json encoder speed [#1546](https://github.com/gin-gonic/gin/pull/1546) +- [FIX] Fix context.Params race condition on Copy() [#1841](https://github.com/gin-gonic/gin/pull/1841) +- [FIX] Improve GetQueryMap performance [#1918](https://github.com/gin-gonic/gin/pull/1918) +- [FIX] Improve get post data [#1920](https://github.com/gin-gonic/gin/pull/1920) +- [FIX] Use context instead of x/net/context [#1922](https://github.com/gin-gonic/gin/pull/1922) +- [FIX] Attempt to fix PostForm cache bug [#1931](https://github.com/gin-gonic/gin/pull/1931) +- [FIX] **[Break-Backward]** Drop support for go1.8 and go1.9 [#1933](https://github.com/gin-gonic/gin/pull/1933) +- [FIX] Bugfix for the FullPath feature [#1919](https://github.com/gin-gonic/gin/pull/1919) +- [FIX] Gin1.5 bytes.Buffer to strings.Builder [#1939](https://github.com/gin-gonic/gin/pull/1939) +- [FIX] Upgrade github.com/ugorji/go/codec [#1969](https://github.com/gin-gonic/gin/pull/1969) +- [FIX] Simplify code [#2004](https://github.com/gin-gonic/gin/pull/2004) +- [FIX] Identify terminal on a RISC-V architecture for auto-colored logs [#2019](https://github.com/gin-gonic/gin/pull/2019) +- [FIX] **[Break-Backward]** Context.JSONP() now expects a semicolon (;) at the end [#2007](https://github.com/gin-gonic/gin/pull/2007) +- [FIX] **[Break-Backward]** Upgrade validator version to v9 [#1015](https://github.com/gin-gonic/gin/pull/1015) +- [FIX] Fix some typo [#2079](https://github.com/gin-gonic/gin/pull/2079) [#2080](https://github.com/gin-gonic/gin/pull/2080) +- [FIX] Relocate binding body tests [#2086](https://github.com/gin-gonic/gin/pull/2086) +- [FIX] Use Writer in Context.Status [#1606](https://github.com/gin-gonic/gin/pull/1606) +- [FIX] `Engine.RunUnix()` now returns the error if it can't change the file mode [#2093](https://github.com/gin-gonic/gin/pull/2093) +- [FIX] `RouterGroup.StaticFS()` leaked files. Now it closes them. [#2118](https://github.com/gin-gonic/gin/pull/2118) +- [FIX] `Context.Request.FormFile` leaked file, now it closes it [#2114](https://github.com/gin-gonic/gin/pull/2114) +- [FIX] Ignore walking on `form:"-"` mapping [#1943](https://github.com/gin-gonic/gin/pull/1943) +- [REFACTOR] **[Break-Backward]** Use encode replace json marshal increase json encoder speed [#1546 ](https://github.com/gin-gonic/gin/pull/1546) + diff --git a/content/ru/blog/releases/release16.md b/content/ru/blog/releases/release16.md new file mode 100644 index 000000000..539997c96 --- /dev/null +++ b/content/ru/blog/releases/release16.md @@ -0,0 +1,60 @@ +--- +title: "Gin 1.6.0 is released" +linkTitle: "Gin 1.6.0 is released" +date: 2020-03-22 +--- + +### CHANGELOG + +#### BREAKING + * chore(performance): Improve performance for adding RemoveExtraSlash flag [#2159](https://github.com/gin-gonic/gin/pull/2159) + * drop support govendor [#2148](https://github.com/gin-gonic/gin/pull/2148) + * Added support for SameSite cookie flag [#1615](https://github.com/gin-gonic/gin/pull/1615) + +#### FEATURES + * add yaml negotitation [#2220](https://github.com/gin-gonic/gin/pull/2220) + * FileFromFS [#2112](https://github.com/gin-gonic/gin/pull/2112) + +#### BUGFIXES + * Unix Socket Handling [#2280](https://github.com/gin-gonic/gin/pull/2280) + * Use json marshall in context json to fix breaking new line issue. Fixes #2209 [#2228](https://github.com/gin-gonic/gin/pull/2228) + * fix accept incoming network connections [#2216](https://github.com/gin-gonic/gin/pull/2216) + * Fixed a bug in the calculation of the maximum number of parameters [#2166](https://github.com/gin-gonic/gin/pull/2166) + * [FIX] allow empty headers on DataFromReader [#2121](https://github.com/gin-gonic/gin/pull/2121) + * Add mutex for protect Context.Keys map [#1391](https://github.com/gin-gonic/gin/pull/1391) + +#### ENHANCEMENTS + * Add mitigation for log injection [#2277](https://github.com/gin-gonic/gin/pull/2277) + * tree: range over nodes values [#2229](https://github.com/gin-gonic/gin/pull/2229) + * tree: remove duplicate assignment [#2222](https://github.com/gin-gonic/gin/pull/2222) + * chore: upgrade go-isatty and json-iterator/go [#2215](https://github.com/gin-gonic/gin/pull/2215) + * path: sync code with httprouter [#2212](https://github.com/gin-gonic/gin/pull/2212) + * Use zero-copy approach to convert types between string and byte slice [#2206](https://github.com/gin-gonic/gin/pull/2206) + * Reuse bytes when cleaning the URL paths [#2179](https://github.com/gin-gonic/gin/pull/2179) + * tree: remove one else statement [#2177](https://github.com/gin-gonic/gin/pull/2177) + * tree: sync httprouter update (#2173) (#2172) [#2171](https://github.com/gin-gonic/gin/pull/2171) + * tree: sync part httprouter codes and reduce if/else [#2163](https://github.com/gin-gonic/gin/pull/2163) + * use http method constant [#2155](https://github.com/gin-gonic/gin/pull/2155) + * upgrade go-validator to v10 [#2149](https://github.com/gin-gonic/gin/pull/2149) + * Refactor redirect request in gin.go [#1970](https://github.com/gin-gonic/gin/pull/1970) + * Add build tag nomsgpack [#1852](https://github.com/gin-gonic/gin/pull/1852) + +#### DOCS + * docs(path): improve comments [#2223](https://github.com/gin-gonic/gin/pull/2223) + * Renew README to fit the modification of SetCookie method [#2217](https://github.com/gin-gonic/gin/pull/2217) + * Fix spelling [#2202](https://github.com/gin-gonic/gin/pull/2202) + * Remove broken link from README. [#2198](https://github.com/gin-gonic/gin/pull/2198) + * Update docs on Context.Done(), Context.Deadline() and Context.Err() [#2196](https://github.com/gin-gonic/gin/pull/2196) + * Update validator to v10 [#2190](https://github.com/gin-gonic/gin/pull/2190) + * upgrade go-validator to v10 for README [#2189](https://github.com/gin-gonic/gin/pull/2189) + * Update to currently output [#2188](https://github.com/gin-gonic/gin/pull/2188) + * Fix "Custom Validators" example [#2186](https://github.com/gin-gonic/gin/pull/2186) + * Add project to README [#2165](https://github.com/gin-gonic/gin/pull/2165) + * docs(benchmarks): for gin v1.5 [#2153](https://github.com/gin-gonic/gin/pull/2153) + * Changed wording for clarity in README.md [#2122](https://github.com/gin-gonic/gin/pull/2122) + +#### MISC + * ci support go1.14 [#2262](https://github.com/gin-gonic/gin/pull/2262) + * chore: upgrade depend version [#2231](https://github.com/gin-gonic/gin/pull/2231) + * Drop support go1.10 [#2147](https://github.com/gin-gonic/gin/pull/2147) + * fix comment in `mode.go` [#2129](https://github.com/gin-gonic/gin/pull/2129) diff --git a/content/ru/docs/_index.md b/content/ru/docs/_index.md new file mode 100644 index 000000000..b9876b7d0 --- /dev/null +++ b/content/ru/docs/_index.md @@ -0,0 +1,24 @@ +--- +название: "Документация" +linkTitle: "Документация" +вес: 20 +menu: + main: + вес: 20 +--- + +## Что такое Gin? + +Gin - это HTTP-веб-фреймворк, написанный на языке Go (Golang). В нем реализован API, похожий на Martini, но с производительностью в 40 раз выше, чем у Martini. Если вам нужна потрясающая производительность, купите себе Gin. + +## Как использовать Gin? + +Мы приводим [примеры] использования API (https://github.com/gin-gonic/examples) и список известных пользователей [Gin](./users). + +## Как внести свой вклад в развитие Gin? + +* Помогайте людям на дискуссионных форумах. +* Расскажите нам о своих успехах в использовании Gin +* Расскажите нам, как мы можем улучшить Gin, и помогите нам в этом. +* Внести вклад в существующую библиотеку + diff --git a/content/ru/docs/benchmarks/_index.md b/content/ru/docs/benchmarks/_index.md new file mode 100644 index 000000000..c512f9258 --- /dev/null +++ b/content/ru/docs/benchmarks/_index.md @@ -0,0 +1,47 @@ +--- +title: "Контрольные показатели" +draft: false +weight: 3 +--- + +Gin использует пользовательскую версию [HttpRouter](https://github.com/julienschmidt/httprouter). + +[Посмотреть все контрольные показатели](https://github.com/gin-gonic/gin/blob/master/BENCHMARKS.md) + +| Benchmark name | (1) | (2) | (3) | (4) | +| ------------------------------ | ---------:| ---------------:| ------------:| ---------------:| +| BenchmarkGin_GithubAll | **43550** | **27364 ns/op** | **0 B/op** | **0 allocs/op** | +| BenchmarkAce_GithubAll | 40543 | 29670 ns/op | 0 B/op | 0 allocs/op | +| BenchmarkAero_GithubAll | 57632 | 20648 ns/op | 0 B/op | 0 allocs/op | +| BenchmarkBear_GithubAll | 9234 | 216179 ns/op | 86448 B/op | 943 allocs/op | +| BenchmarkBeego_GithubAll | 7407 | 243496 ns/op | 71456 B/op | 609 allocs/op | +| BenchmarkBone_GithubAll | 420 | 2922835 ns/op | 720160 B/op | 8620 allocs/op | +| BenchmarkChi_GithubAll | 7620 | 238331 ns/op | 87696 B/op | 609 allocs/op | +| BenchmarkDenco_GithubAll | 18355 | 64494 ns/op | 20224 B/op | 167 allocs/op | +| BenchmarkEcho_GithubAll | 31251 | 38479 ns/op | 0 B/op | 0 allocs/op | +| BenchmarkGocraftWeb_GithubAll | 4117 | 300062 ns/op | 131656 B/op | 1686 allocs/op | +| BenchmarkGoji_GithubAll | 3274 | 416158 ns/op | 56112 B/op | 334 allocs/op | +| BenchmarkGojiv2_GithubAll | 1402 | 870518 ns/op | 352720 B/op | 4321 allocs/op | +| BenchmarkGoJsonRest_GithubAll | 2976 | 401507 ns/op | 134371 B/op | 2737 allocs/op | +| BenchmarkGoRestful_GithubAll | 410 | 2913158 ns/op | 910144 B/op | 2938 allocs/op | +| BenchmarkGorillaMux_GithubAll | 346 | 3384987 ns/op | 251650 B/op | 1994 allocs/op | +| BenchmarkGowwwRouter_GithubAll | 10000 | 143025 ns/op | 72144 B/op | 501 allocs/op | +| BenchmarkHttpRouter_GithubAll | 55938 | 21360 ns/op | 0 B/op | 0 allocs/op | +| BenchmarkHttpTreeMux_GithubAll | 10000 | 153944 ns/op | 65856 B/op | 671 allocs/op | +| BenchmarkKocha_GithubAll | 10000 | 106315 ns/op | 23304 B/op | 843 allocs/op | +| BenchmarkLARS_GithubAll | 47779 | 25084 ns/op | 0 B/op | 0 allocs/op | +| BenchmarkMacaron_GithubAll | 3266 | 371907 ns/op | 149409 B/op | 1624 allocs/op | +| BenchmarkMartini_GithubAll | 331 | 3444706 ns/op | 226551 B/op | 2325 allocs/op | +| BenchmarkPat_GithubAll | 273 | 4381818 ns/op | 1483152 B/op | 26963 allocs/op | +| BenchmarkPossum_GithubAll | 10000 | 164367 ns/op | 84448 B/op | 609 allocs/op | +| BenchmarkR2router_GithubAll | 10000 | 160220 ns/op | 77328 B/op | 979 allocs/op | +| BenchmarkRivet_GithubAll | 14625 | 82453 ns/op | 16272 B/op | 167 allocs/op | +| BenchmarkTango_GithubAll | 6255 | 279611 ns/op | 63826 B/op | 1618 allocs/op | +| BenchmarkTigerTonic_GithubAll | 2008 | 687874 ns/op | 193856 B/op | 4474 allocs/op | +| BenchmarkTraffic_GithubAll | 355 | 3478508 ns/op | 820744 B/op | 14114 allocs/op | +| BenchmarkVulcan_GithubAll | 6885 | 193333 ns/op | 19894 B/op | 609 allocs/op | + +- (1): Общее количество повторений, достигнутое за постоянное время, большее количество означает более уверенный результат +- (2): Длительность одного повторения (ns/op), меньше - лучше +- (3): Память кучи (B/op), меньше - лучше +- (4): Среднее количество выделений за повтор (allocs/op), ниже - лучше diff --git a/content/ru/docs/deployment/_index.md b/content/ru/docs/deployment/_index.md new file mode 100644 index 000000000..ac691d84a --- /dev/null +++ b/content/ru/docs/deployment/_index.md @@ -0,0 +1,31 @@ +--- +title: "Deployment" +draft: false +weight: 6 +--- + +Проекты Gin могут быть легко развернуты на любом облачном провайдере. + +## [Koyeb](https://www.koyeb.com) + +Koyeb - это удобная для разработчиков бессерверная платформа для глобального развертывания приложений с развертыванием на основе git, шифрованием TLS, встроенным автомасштабированием, глобальной пограничной сетью и встроенным сервисом mesh & discovery. + +Следуйте руководству Koyeb [Guide to deploy your Gin projects](https://www.koyeb.com/tutorials/deploy-go-gin-on-koyeb). + +## [Qovery](https://www.qovery.com) + +Qovery предоставляет бесплатный облачный хостинг с базами данных, SSL, глобальной CDN и автоматическим развертыванием с помощью Git. + +Следуйте руководству Qovery, чтобы [развернуть свой проект Gin](https://docs.qovery.com/guides/tutorial/deploy-gin-with-postgresql/). + +## [Render](https://render.com) + +Render - это современная облачная платформа, которая предлагает встроенную поддержку Go, полностью управляемый SSL, базы данных, деплой с нулевым временем простоя, HTTP/2 и поддержку websocket. + +Следуйте рекомендациям Render [руководство по развертыванию проектов Gin](https://render.com/docs/deploy-go-gin). + +## [Google App Engine](https://cloud.google.com/appengine/) + +В GAE есть два способа развертывания Go-приложений. Стандартная среда проще в использовании, но менее настраиваема и не допускает [syscalls](https://github.com/gin-gonic/gin/issues/1639) по соображениям безопасности. В гибком окружении можно запускать любые фреймворки и библиотеки. + +Узнать больше и выбрать предпочтительную среду можно на сайте [Go on Google App Engine](https://cloud.google.com/appengine/docs/go/). diff --git a/content/ru/docs/examples/_index.md b/content/ru/docs/examples/_index.md new file mode 100644 index 000000000..df13529a2 --- /dev/null +++ b/content/ru/docs/examples/_index.md @@ -0,0 +1,7 @@ +--- +название: "Примеры" +черновик: false +вес: 6 +--- + +Раздел содержит список использования api. diff --git a/content/ru/docs/examples/ascii-json.md b/content/ru/docs/examples/ascii-json.md new file mode 100644 index 000000000..9eb014880 --- /dev/null +++ b/content/ru/docs/examples/ascii-json.md @@ -0,0 +1,25 @@ +--- +название: "AsciiJSON" +черновик: false +--- + +Использование AsciiJSON для генерации ASCII-единственного JSON с экранированными не-ASCII символами. + +```go +func main() { + r := gin.Default() + + r.GET("/someJSON", func(c *gin.Context) { + data := map[string]interface{}{ + "lang": "GO语言", + "tag": "
", + } + + // will output : {"lang":"GO\u8bed\u8a00","tag":"\u003cbr\u003e"} + c.AsciiJSON(http.StatusOK, data) + }) + + // Listen and serve on 0.0.0.0:8080 + r.Run(":8080") +} +``` diff --git a/content/ru/docs/examples/bind-body-into-dirrerent-structs.md b/content/ru/docs/examples/bind-body-into-dirrerent-structs.md new file mode 100644 index 000000000..008769777 --- /dev/null +++ b/content/ru/docs/examples/bind-body-into-dirrerent-structs.md @@ -0,0 +1,61 @@ +--- +название: "Попытайтесь связать тело в разных структурах" +draft: false +--- + +Обычные методы для связывания тела запроса (request body) потребляют `c.Request.Body` и их +не могут быть вызваны несколько раз. + +```go +type formA struct { + Foo string `json:"foo" xml:"foo" binding:"required"` +} + +type formB struct { + Bar string `json:"bar" xml:"bar" binding:"required"` +} + +func SomeHandler(c *gin.Context) { + objA := formA{} + objB := formB{} + // This c.ShouldBind consumes c.Request.Body and it cannot be reused. + if errA := c.ShouldBind(&objA); errA == nil { + c.String(http.StatusOK, `the body should be formA`) + // Always an error is occurred by this because c.Request.Body is EOF now. + } else if errB := c.ShouldBind(&objB); errB == nil { + c.String(http.StatusOK, `the body should be formB`) + } else { + ... + } +} +``` + +Для этого можно использовать `c.ShouldBindBodyWith`. + +```go +func SomeHandler(c *gin.Context) { + objA := formA{} + objB := formB{} + // This reads c.Request.Body and stores the result into the context. + if errA := c.ShouldBindBodyWith(&objA, binding.JSON); errA == nil { + c.String(http.StatusOK, `the body should be formA`) + // At this time, it reuses body stored in the context. + } else if errB := c.ShouldBindBodyWith(&objB, binding.JSON); errB == nil { + c.String(http.StatusOK, `the body should be formB JSON`) + // And it can accepts other formats + } else if errB2 := c.ShouldBindBodyWith(&objB, binding.XML); errB2 == nil { + c.String(http.StatusOK, `the body should be formB XML`) + } else { + ... + } +} +``` + +* `c.ShouldBindBodyWith` сохраняет тело в контексте перед привязкой. Это +небольшое влияние на производительность, поэтому не стоит использовать этот метод, если вы +достаточно вызвать привязку сразу. +* Эта возможность необходима только для некоторых форматов - `JSON`, `XML`, `MsgPack`, +`ProtoBuf`. Для других форматов, `Query`, `Form`, `FormPost`, `FormMultipart`, +могут быть вызваны `c.ShouldBind()` многократно без ущерба для +производительности (см. [#1341](https://github.com/gin-gonic/gin/pull/1341)). + diff --git a/content/ru/docs/examples/bind-form-data-request-with-custom-struct.md b/content/ru/docs/examples/bind-form-data-request-with-custom-struct.md new file mode 100644 index 000000000..4567d90ee --- /dev/null +++ b/content/ru/docs/examples/bind-form-data-request-with-custom-struct.md @@ -0,0 +1,77 @@ +--- +название: "Связывание запроса формы-данных с пользовательской структурой" +черновик: false +--- + +Следующий пример с использованием пользовательской структуры: + +```go +type StructA struct { + FieldA string `form:"field_a"` +} + +type StructB struct { + NestedStruct StructA + FieldB string `form:"field_b"` +} + +type StructC struct { + NestedStructPointer *StructA + FieldC string `form:"field_c"` +} + +type StructD struct { + NestedAnonyStruct struct { + FieldX string `form:"field_x"` + } + FieldD string `form:"field_d"` +} + +func GetDataB(c *gin.Context) { + var b StructB + c.Bind(&b) + c.JSON(200, gin.H{ + "a": b.NestedStruct, + "b": b.FieldB, + }) +} + +func GetDataC(c *gin.Context) { + var b StructC + c.Bind(&b) + c.JSON(200, gin.H{ + "a": b.NestedStructPointer, + "c": b.FieldC, + }) +} + +func GetDataD(c *gin.Context) { + var b StructD + c.Bind(&b) + c.JSON(200, gin.H{ + "x": b.NestedAnonyStruct, + "d": b.FieldD, + }) +} + +func main() { + r := gin.Default() + r.GET("/getb", GetDataB) + r.GET("/getc", GetDataC) + r.GET("/getd", GetDataD) + + r.Run() +} +``` + +Результаты при использовании `curl`: + +``` +$ curl "http://localhost:8080/getb?field_a=hello&field_b=world" +{"a":{"FieldA":"hello"},"b":"world"} +$ curl "http://localhost:8080/getc?field_a=hello&field_c=world" +{"a":{"FieldA":"hello"},"c":"world"} +$ curl "http://localhost:8080/getd?field_x=hello&field_d=world" +{"d":"world","x":{"FieldX":"hello"}} +``` + diff --git a/content/ru/docs/examples/bind-html-checkbox.md b/content/ru/docs/examples/bind-html-checkbox.md new file mode 100644 index 000000000..8950b1699 --- /dev/null +++ b/content/ru/docs/examples/bind-html-checkbox.md @@ -0,0 +1,45 @@ +--- +title: "Привязка html-флажков" +draft: false +--- + +Смотрите [подробную информацию](https://github.com/gin-gonic/gin/issues/129#issuecomment-124260092) + +```go +... + +type myForm struct { + Colors []string `form:"colors[]"` +} + +... + +func formHandler(c *gin.Context) { + var fakeForm myForm + c.ShouldBind(&fakeForm) + c.JSON(200, gin.H{"color": fakeForm.Colors}) +} + +... + +``` + +```html +
+

Check some colors

+ + + + + + + +
+``` + +Результат: + +```sh +{"color":["red","green","blue"]} +``` + diff --git a/content/ru/docs/examples/bind-query-or-post.md b/content/ru/docs/examples/bind-query-or-post.md new file mode 100644 index 000000000..c013c89ca --- /dev/null +++ b/content/ru/docs/examples/bind-query-or-post.md @@ -0,0 +1,48 @@ +--- +title: "Связать строку запроса или данные поста" +черновик: false +--- + +Смотрите [подробную информацию](https://github.com/gin-gonic/gin/issues/742#issuecomment-264681292). + +```go +package main + +import ( + "log" + "time" + + "github.com/gin-gonic/gin" +) + +type Person struct { + Name string `form:"name"` + Address string `form:"address"` + Birthday time.Time `form:"birthday" time_format:"2006-01-02" time_utc:"1"` +} + +func main() { + route := gin.Default() + route.GET("/testing", startPage) + route.Run(":8085") +} + +func startPage(c *gin.Context) { + var person Person + // If `GET`, only `Form` binding engine (`query`) used. + // If `POST`, first checks the `content-type` for `JSON` or `XML`, then uses `Form` (`form-data`). + // See more at https://github.com/gin-gonic/gin/blob/master/binding/binding.go#L48 + if c.ShouldBind(&person) == nil { + log.Println(person.Name) + log.Println(person.Address) + log.Println(person.Birthday) + } + + c.String(200, "Success") +} +``` + +Тестирование: +```sh +$ curl -X GET "localhost:8085/testing?name=appleboy&address=xyz&birthday=1992-03-15" +``` diff --git a/content/ru/docs/examples/bind-single-binary-with-template.md b/content/ru/docs/examples/bind-single-binary-with-template.md new file mode 100644 index 000000000..a01a567c7 --- /dev/null +++ b/content/ru/docs/examples/bind-single-binary-with-template.md @@ -0,0 +1,45 @@ +--- +title: "Создание единого бинарного файла с помощью шаблонов" +draft: false +--- +## Использование стороннего пакета + +Вы можете использовать сторонний пакет для сборки сервера в единый бинарник, содержащий шаблоны, используя [go-assets](https://github.com/jessevdk/go-assets). + +```go +func main() { + r := gin.New() + + t, err := loadTemplate() + if err != nil { + panic(err) + } + r.SetHTMLTemplate(t) + + r.GET("/", func(c *gin.Context) { + c.HTML(http.StatusOK, "/html/index.tmpl", nil) + }) + r.Run(":8080") +} + +// loadTemplate loads templates embedded by go-assets-builder +func loadTemplate() (*template.Template, error) { + t := template.New("") + for name, file := range Assets.Files { + if file.IsDir() || !strings.HasSuffix(name, ".tmpl") { + continue + } + h, err := ioutil.ReadAll(file) + if err != nil { + return nil, err + } + t, err = t.New(name).Parse(string(h)) + if err != nil { + return nil, err + } + } + return t, nil +} +``` + +Полный пример находится в каталоге [assets-in-binary/example01](https://github.com/gin-gonic/examples/tree/master/assets-in-binary/example01). diff --git a/content/ru/docs/examples/bind-uri.md b/content/ru/docs/examples/bind-uri.md new file mode 100644 index 000000000..654911691 --- /dev/null +++ b/content/ru/docs/examples/bind-uri.md @@ -0,0 +1,37 @@ +--- +Заголовок: "Связывание Uri" +draft: false +--- + +Смотрите [подробную информацию](https://github.com/gin-gonic/gin/issues/846). + +```go +package main + +import "github.com/gin-gonic/gin" + +type Person struct { + ID string `uri:"id" binding:"required,uuid"` + Name string `uri:"name" binding:"required"` +} + +func main() { + route := gin.Default() + route.GET("/:name/:id", func(c *gin.Context) { + var person Person + if err := c.ShouldBindUri(&person); err != nil { + c.JSON(400, gin.H{"msg": err.Error()}) + return + } + c.JSON(200, gin.H{"name": person.Name, "uuid": person.ID}) + }) + route.Run(":8088") +} +``` + +Тестирование: + +```sh +$ curl -v localhost:8088/thinkerou/987fbc97-4bed-5078-9f07-9141ba07c9f3 +$ curl -v localhost:8088/thinkerou/not-uuid +``` diff --git a/content/ru/docs/examples/binding-and-validation.md b/content/ru/docs/examples/binding-and-validation.md new file mode 100644 index 000000000..2f7d35e62 --- /dev/null +++ b/content/ru/docs/examples/binding-and-validation.md @@ -0,0 +1,118 @@ +--- +Заголовок: "Связывание и проверка моделей" +черновик: false +--- + +Чтобы привязать тело запроса к типу, используйте привязку к модели. В настоящее время мы поддерживаем привязку JSON, XML, YAML и стандартных значений формы (foo=bar&boo=baz). + +Для валидации Gin использует [**go-playground/validator/v10**](https://github.com/go-playground/validator). Ознакомьтесь с полной документацией по использованию тегов [здесь](https://pkg.go.dev/github.com/go-playground/validator/v10#hdr-Baked_In_Validators_and_Tags). + +Обратите внимание, что вам необходимо установить соответствующий тег привязки для всех полей, которые вы хотите привязать. Например, при привязке из JSON установите `json: "fieldname"`. + +Кроме того, Gin предоставляет два набора методов для привязки: +- **Type** - Must bind + - **Методы** - `Bind`, `BindJSON`, `BindXML`, `BindQuery`, `BindYAML` + - **Поведение** - Эти методы используют `MustBindWith` под капотом. Если произошла ошибка связывания, запрос прерывается с помощью `c.AbortWithError(400, err).SetType(ErrorTypeBind)`. При этом код состояния ответа принимает значение 400, а заголовок `Content-Type` устанавливается на `text/plain; charset=utf-8`. Обратите внимание, что если вы попытаетесь установить код ответа после этого, то это приведет к предупреждению `[GIN-debug] [WARNING] Заголовки уже были записаны. Хотелось отменить код статуса 400 на 422`. Если вы хотите получить больший контроль над поведением, используйте эквивалентный метод `ShouldBind`. +- **Тип** - Should bind + - **Методы** - `ShouldBind`, `ShouldBindJSON`, `ShouldBindXML`, `ShouldBindQuery`, `ShouldBindYAML` + - **Поведение** - Эти методы используют `ShouldBindWith` под капотом. Если произошла ошибка связывания, возвращается ошибка, и разработчик обязан обработать запрос и ошибку соответствующим образом. + +При использовании Bind-метода Gin пытается определить связующее звено в зависимости от заголовка Content-Type. Если вы уверены, что именно вы связываете, вы можете использовать `MustBindWith` или `ShouldBindWith`. + +Вы также можете указать, что определенные поля являются обязательными. Если поле украшено `binding: "required"` и при привязке имеет пустое значение, будет возвращена ошибка. + +```go +// Binding from JSON +type Login struct { + User string `form:"user" json:"user" xml:"user" binding:"required"` + Password string `form:"password" json:"password" xml:"password" binding:"required"` +} + +func main() { + router := gin.Default() + + // Example for binding JSON ({"user": "manu", "password": "123"}) + router.POST("/loginJSON", func(c *gin.Context) { + var json Login + if err := c.ShouldBindJSON(&json); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + if json.User != "manu" || json.Password != "123" { + c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"}) + return + } + + c.JSON(http.StatusOK, gin.H{"status": "you are logged in"}) + }) + + // Example for binding XML ( + // + // + // manu + // 123 + // ) + router.POST("/loginXML", func(c *gin.Context) { + var xml Login + if err := c.ShouldBindXML(&xml); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + if xml.User != "manu" || xml.Password != "123" { + c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"}) + return + } + + c.JSON(http.StatusOK, gin.H{"status": "you are logged in"}) + }) + + // Example for binding a HTML form (user=manu&password=123) + router.POST("/loginForm", func(c *gin.Context) { + var form Login + // This will infer what binder to use depending on the content-type header. + if err := c.ShouldBind(&form); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + if form.User != "manu" || form.Password != "123" { + c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"}) + return + } + + c.JSON(http.StatusOK, gin.H{"status": "you are logged in"}) + }) + + // Listen and serve on 0.0.0.0:8080 + router.Run(":8080") +} +``` + +### Пример запроса + +```sh +$ curl -v -X POST \ + http://localhost:8080/loginJSON \ + -H 'content-type: application/json' \ + -d '{ "user": "manu" }' +> POST /loginJSON HTTP/1.1 +> Host: localhost:8080 +> User-Agent: curl/7.51.0 +> Accept: */* +> content-type: application/json +> Content-Length: 18 +> +* upload completely sent off: 18 out of 18 bytes +< HTTP/1.1 400 Bad Request +< Content-Type: application/json; charset=utf-8 +< Date: Fri, 04 Aug 2017 03:51:31 GMT +< Content-Length: 100 +< +{"error":"Key: 'Login.Password' Error:Field validation for 'Password' failed on the 'required' tag"} +``` + +### Пропустить валидацию + +При выполнении приведенного выше примера с помощью указанной выше команды `curl` возвращается ошибка. Потому что в примере используется `привязка: "required"` для `Password`. Если использовать `binding:"-"` для `Password`, то при повторном запуске приведенного выше примера ошибка не возникнет. diff --git a/content/ru/docs/examples/controlling-log-output-coloring.md b/content/ru/docs/examples/controlling-log-output-coloring.md new file mode 100644 index 000000000..248c10818 --- /dev/null +++ b/content/ru/docs/examples/controlling-log-output-coloring.md @@ -0,0 +1,44 @@ +--- +title: "Управление раскраской вывода журнала" +draft: false +--- + +По умолчанию логи, выводимые на консоль, должны быть окрашены в зависимости от обнаруженного TTY. + +Никогда не окрашивать журналы: + +```go +func main() { + // Disable log's color + gin.DisableConsoleColor() + + // Creates a gin router with default middleware: + // logger and recovery (crash-free) middleware + router := gin.Default() + + router.GET("/ping", func(c *gin.Context) { + c.String(200, "pong") + }) + + router.Run(":8080") +} +``` + +Всегда окрашивайте журналы в разные цвета: + +```go +func main() { + // Force log's color + gin.ForceConsoleColor() + + // Creates a gin router with default middleware: + // logger and recovery (crash-free) middleware + router := gin.Default() + + router.GET("/ping", func(c *gin.Context) { + c.String(200, "pong") + }) + + router.Run(":8080") +} +``` diff --git a/content/ru/docs/examples/cookie.md b/content/ru/docs/examples/cookie.md new file mode 100644 index 000000000..7cf85e3ca --- /dev/null +++ b/content/ru/docs/examples/cookie.md @@ -0,0 +1,31 @@ +--- +Название: "Установка и получение cookie" +черновик: false +--- + +```go +import ( + "fmt" + + "github.com/gin-gonic/gin" +) + +func main() { + + router := gin.Default() + + router.GET("/cookie", func(c *gin.Context) { + + cookie, err := c.Cookie("gin_cookie") + + if err != nil { + cookie = "NotSet" + c.SetCookie("gin_cookie", "test", 3600, "/", "localhost", false, true) + } + + fmt.Printf("Cookie value: %s \n", cookie) + }) + + router.Run() +} +``` diff --git a/content/ru/docs/examples/custom-http-config.md b/content/ru/docs/examples/custom-http-config.md new file mode 100644 index 000000000..b6f84b975 --- /dev/null +++ b/content/ru/docs/examples/custom-http-config.md @@ -0,0 +1,33 @@ +--- +Название: "Пользовательская конфигурация HTTP" +черновик: false +--- + +Используйте `http.ListenAndServe()` напрямую, например, так: + +```go +import "net/http" + +func main() { + router := gin.Default() + http.ListenAndServe(":8080", router) +} +``` +or + +```go +import "net/http" + +func main() { + router := gin.Default() + + s := &http.Server{ + Addr: ":8080", + Handler: router, + ReadTimeout: 10 * time.Second, + WriteTimeout: 10 * time.Second, + MaxHeaderBytes: 1 << 20, + } + s.ListenAndServe() +} +``` diff --git a/content/ru/docs/examples/custom-log-format.md b/content/ru/docs/examples/custom-log-format.md new file mode 100644 index 000000000..00613371b --- /dev/null +++ b/content/ru/docs/examples/custom-log-format.md @@ -0,0 +1,38 @@ +--- +Название: "Пользовательский файл журнала" +черновик: false +--- + +Например: + +```go +func main() { + router := gin.New() + // LoggerWithFormatter middleware will write the logs to gin.DefaultWriter + // By default gin.DefaultWriter = os.Stdout + router.Use(gin.LoggerWithFormatter(func(param gin.LogFormatterParams) string { + // your custom format + return fmt.Sprintf("%s - [%s] \"%s %s %s %d %s \"%s\" %s\"\n", + param.ClientIP, + param.TimeStamp.Format(time.RFC1123), + param.Method, + param.Path, + param.Request.Proto, + param.StatusCode, + param.Latency, + param.Request.UserAgent(), + param.ErrorMessage, + ) + })) + router.Use(gin.Recovery()) + router.GET("/ping", func(c *gin.Context) { + c.String(200, "pong") + }) + router.Run(":8080") +} +``` + +**Образец вывода** +``` +::1 - [Fri, 07 Dec 2018 17:04:38 JST] "GET /ping HTTP/1.1 200 122.767µs "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.80 Safari/537.36" " +``` diff --git a/content/ru/docs/examples/custom-middleware.md b/content/ru/docs/examples/custom-middleware.md new file mode 100644 index 000000000..2ae281da3 --- /dev/null +++ b/content/ru/docs/examples/custom-middleware.md @@ -0,0 +1,43 @@ +--- +название: "Custom Middleware" +черновик: false +--- + +```go +func Logger() gin.HandlerFunc { + return func(c *gin.Context) { + t := time.Now() + + // Set example variable + c.Set("example", "12345") + + // before request + + c.Next() + + // after request + latency := time.Since(t) + log.Print(latency) + + // access the status we are sending + status := c.Writer.Status() + log.Println(status) + } +} + +func main() { + r := gin.New() + r.Use(Logger()) + + r.GET("/test", func(c *gin.Context) { + example := c.MustGet("example").(string) + + // it would print: "12345" + log.Println(example) + }) + + // Listen and serve on 0.0.0.0:8080 + r.Run(":8080") +} +``` + diff --git a/content/ru/docs/examples/custom-validators.md b/content/ru/docs/examples/custom-validators.md new file mode 100644 index 000000000..53790bf18 --- /dev/null +++ b/content/ru/docs/examples/custom-validators.md @@ -0,0 +1,67 @@ +--- +название: "Пользовательские валидаторы" +draft: false +--- + +Также можно зарегистрировать пользовательские валидаторы. Смотрите [пример кода](https://github.com/gin-gonic/examples/tree/master/struct-lvl-validations). + +```go +package main + +import ( + "net/http" + "time" + + "github.com/gin-gonic/gin" + "github.com/gin-gonic/gin/binding" + "github.com/go-playground/validator/v10" +) + +// Booking contains binded and validated data. +type Booking struct { + CheckIn time.Time `form:"check_in" binding:"required,bookabledate" time_format:"2006-01-02"` + CheckOut time.Time `form:"check_out" binding:"required,gtfield=CheckIn,bookabledate" time_format:"2006-01-02"` +} + +var bookableDate validator.Func = func(fl validator.FieldLevel) bool { + date, ok := fl.Field().Interface().(time.Time) + if ok { + today := time.Now() + if today.After(date) { + return false + } + } + return true +} + +func main() { + route := gin.Default() + + if v, ok := binding.Validator.Engine().(*validator.Validate); ok { + v.RegisterValidation("bookabledate", bookableDate) + } + + route.GET("/bookable", getBookable) + route.Run(":8085") +} + +func getBookable(c *gin.Context) { + var b Booking + if err := c.ShouldBindWith(&b, binding.Query); err == nil { + c.JSON(http.StatusOK, gin.H{"message": "Booking dates are valid!"}) + } else { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + } +} +``` + +```sh +$ curl "localhost:8085/bookable?check_in=2118-04-16&check_out=2118-04-17" +{"message":"Booking dates are valid!"} + +$ curl "localhost:8085/bookable?check_in=2118-03-10&check_out=2118-03-09" +{"error":"Key: 'Booking.CheckOut' Error:Field validation for 'CheckOut' failed on the 'gtfield' tag"} +``` + +[Валидации уровня структуры](https://github.com/go-playground/validator/releases/tag/v8.7) также могут быть зарегистрированы таким образом. +Смотрите пример [struct-lvl-validation](https://github.com/gin-gonic/examples/tree/master/struct-lvl-validations), чтобы узнать больше. diff --git a/content/ru/docs/examples/define-format-for-the-log-of-routes.md b/content/ru/docs/examples/define-format-for-the-log-of-routes.md new file mode 100644 index 000000000..214f502fa --- /dev/null +++ b/content/ru/docs/examples/define-format-for-the-log-of-routes.md @@ -0,0 +1,44 @@ +--- +Заголовок: "Определить формат для журнала маршрутов". +черновик: false +--- + +По умолчанию журнал маршрутов имеет следующий вид: +``` +[GIN-debug] POST /foo --> main.main.func1 (3 handlers) +[GIN-debug] GET /bar --> main.main.func2 (3 handlers) +[GIN-debug] GET /status --> main.main.func3 (3 handlers) +``` + +Если вы хотите записывать эту информацию в заданном формате (например, JSON, значения ключей или что-то еще), то вы можете определить этот формат с помощью `gin.DebugPrintRouteFunc`. +В примере ниже мы ведем лог всех маршрутов с помощью стандартного пакета лога, но вы можете использовать другие инструменты лога, которые подходят для ваших нужд. +```go +import ( + "log" + "net/http" + + "github.com/gin-gonic/gin" +) + +func main() { + r := gin.Default() + gin.DebugPrintRouteFunc = func(httpMethod, absolutePath, handlerName string, nuHandlers int) { + log.Printf("endpoint %v %v %v %v\n", httpMethod, absolutePath, handlerName, nuHandlers) + } + + r.POST("/foo", func(c *gin.Context) { + c.JSON(http.StatusOK, "foo") + }) + + r.GET("/bar", func(c *gin.Context) { + c.JSON(http.StatusOK, "bar") + }) + + r.GET("/status", func(c *gin.Context) { + c.JSON(http.StatusOK, "ok") + }) + + // Listen and Server in http://0.0.0.0:8080 + r.Run() +} +``` diff --git a/content/ru/docs/examples/goroutines-inside-a-middleware.md b/content/ru/docs/examples/goroutines-inside-a-middleware.md new file mode 100644 index 000000000..da91a0ea4 --- /dev/null +++ b/content/ru/docs/examples/goroutines-inside-a-middleware.md @@ -0,0 +1,35 @@ +--- +Заголовок: "Гороутины внутри промежуточного ПО" +черновик: false +--- + +При запуске новых Goroutines внутри промежуточного ПО или обработчика, вы **НЕ ДОЛЖНЫ** использовать оригинальный контекст внутри него, вы должны использовать копию, доступную только для чтения. + +```go +func main() { + r := gin.Default() + + r.GET("/long_async", func(c *gin.Context) { + // create copy to be used inside the goroutine + cCp := c.Copy() + go func() { + // simulate a long task with time.Sleep(). 5 seconds + time.Sleep(5 * time.Second) + + // note that you are using the copied context "cCp", IMPORTANT + log.Println("Done! in path " + cCp.Request.URL.Path) + }() + }) + + r.GET("/long_sync", func(c *gin.Context) { + // simulate a long task with time.Sleep(). 5 seconds + time.Sleep(5 * time.Second) + + // since we are NOT using a goroutine, we do not have to copy the context + log.Println("Done! in path " + c.Request.URL.Path) + }) + + // Listen and serve on 0.0.0.0:8080 + r.Run(":8080") +} +``` diff --git a/content/ru/docs/examples/graceful-restart-or-stop.md b/content/ru/docs/examples/graceful-restart-or-stop.md new file mode 100644 index 000000000..2f5caca7b --- /dev/null +++ b/content/ru/docs/examples/graceful-restart-or-stop.md @@ -0,0 +1,85 @@ +--- +Заголовок: "Благодатный перезапуск или остановка" +черновик: false +--- + +Вы хотите произвести плавный перезапуск или остановку вашего веб-сервера? +Есть несколько способов сделать это. + +Мы можем использовать [fvbock/endless](https://github.com/fvbock/endless) для замены стандартного `ListenAndServe`. Более подробную информацию см. в выпуске [#296](https://github.com/gin-gonic/gin/issues/296). + +```go +router := gin.Default() +router.GET("/", handler) +// [...] +endless.ListenAndServe(":4242", router) +``` + +Альтернатива бесконечности: + +* [manners](https://github.com/braintree/manners): Вежливый HTTP-сервер Go, который изящно завершает работу. +* [graceful](https://github.com/tylerb/graceful): Graceful - это пакет Go, позволяющий изящно завершить работу сервера http.Handler. +* [grace](https://github.com/facebookgo/grace): Graceful restart & zero downtime deploy для Go-серверов. + +Если вы используете Go 1.8, возможно, вам не понадобится эта библиотека! Лучше используйте встроенный метод http.Server [Shutdown()](https://golang.org/pkg/net/http/#Server.Shutdown) для изящного завершения работы. Посмотрите полный пример [graceful-shutdown](https://github.com/gin-gonic/examples/tree/master/graceful-shutdown) с gin. + +```go +// +build go1.8 + +package main + +import ( + "context" + "log" + "net/http" + "os" + "os/signal" + "syscall" + "time" + + "github.com/gin-gonic/gin" +) + +func main() { + router := gin.Default() + router.GET("/", func(c *gin.Context) { + time.Sleep(5 * time.Second) + c.String(http.StatusOK, "Welcome Gin Server") + }) + + srv := &http.Server{ + Addr: ":8080", + Handler: router.Handler(), + } + + go func() { + // service connections + if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed { + log.Fatalf("listen: %s\n", err) + } + }() + + // Wait for interrupt signal to gracefully shutdown the server with + // a timeout of 5 seconds. + quit := make(chan os.Signal, 1) + // kill (no param) default send syscall.SIGTERM + // kill -2 is syscall.SIGINT + // kill -9 is syscall. SIGKILL but can"t be catch, so don't need add it + signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) + <-quit + log.Println("Shutdown Server ...") + + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + if err := srv.Shutdown(ctx); err != nil { + log.Fatal("Server Shutdown:", err) + } + // catching ctx.Done(). timeout of 5 seconds. + select { + case <-ctx.Done(): + log.Println("timeout of 5 seconds.") + } + log.Println("Server exiting") +} +``` + diff --git a/content/ru/docs/examples/grouping-routes.md b/content/ru/docs/examples/grouping-routes.md new file mode 100644 index 000000000..6e23d2e6a --- /dev/null +++ b/content/ru/docs/examples/grouping-routes.md @@ -0,0 +1,28 @@ +--- +Заголовок: "Группировка маршрутов" +черновик: false +--- + +```go +func main() { + router := gin.Default() + + // Simple group: v1 + v1 := router.Group("/v1") + { + v1.POST("/login", loginEndpoint) + v1.POST("/submit", submitEndpoint) + v1.POST("/read", readEndpoint) + } + + // Simple group: v2 + v2 := router.Group("/v2") + { + v2.POST("/login", loginEndpoint) + v2.POST("/submit", submitEndpoint) + v2.POST("/read", readEndpoint) + } + + router.Run(":8080") +} +``` diff --git a/content/ru/docs/examples/html-rendering.md b/content/ru/docs/examples/html-rendering.md new file mode 100644 index 000000000..d489dda4e --- /dev/null +++ b/content/ru/docs/examples/html-rendering.md @@ -0,0 +1,150 @@ +--- +Заголовок: "HTML-рендеринг" +draft: false +--- + +Использование LoadHTMLGlob() или LoadHTMLFiles() + +```go +func main() { + router := gin.Default() + router.LoadHTMLGlob("templates/*") + //router.LoadHTMLFiles("templates/template1.html", "templates/template2.html") + router.GET("/index", func(c *gin.Context) { + c.HTML(http.StatusOK, "index.tmpl", gin.H{ + "title": "Main website", + }) + }) + router.Run(":8080") +} +``` + +templates/index.tmpl + +```html + +

+ {{ .title }} +

+ +``` + +Использование шаблонов с одинаковыми именами в разных каталогах + +```go +func main() { + router := gin.Default() + router.LoadHTMLGlob("templates/**/*") + router.GET("/posts/index", func(c *gin.Context) { + c.HTML(http.StatusOK, "posts/index.tmpl", gin.H{ + "title": "Posts", + }) + }) + router.GET("/users/index", func(c *gin.Context) { + c.HTML(http.StatusOK, "users/index.tmpl", gin.H{ + "title": "Users", + }) + }) + router.Run(":8080") +} +``` + +templates/posts/index.tmpl + +```html +{{ define "posts/index.tmpl" }} +

+ {{ .title }} +

+

Using posts/index.tmpl

+ +{{ end }} +``` + +templates/users/index.tmpl + +```html +{{ define "users/index.tmpl" }} +

+ {{ .title }} +

+

Using users/index.tmpl

+ +{{ end }} +``` + +### Пользовательский рендерер шаблонов + +Вы также можете использовать собственный рендерер html-шаблонов + +```go +import "html/template" + +func main() { + router := gin.Default() + html := template.Must(template.ParseFiles("file1", "file2")) + router.SetHTMLTemplate(html) + router.Run(":8080") +} +``` + +### Пользовательские разделители + +Вы можете использовать пользовательские разделители + +```go + r := gin.Default() + r.Delims("{[{", "}]}") + r.LoadHTMLGlob("/path/to/templates") +``` + +### Пользовательские функции шаблона + +Смотрите подробный [пример кода](https://github.com/gin-gonic/examples/tree/master/template). + +main.go + +```go +import ( + "fmt" + "html/template" + "net/http" + "time" + + "github.com/gin-gonic/gin" +) + +func formatAsDate(t time.Time) string { + year, month, day := t.Date() + return fmt.Sprintf("%d/%02d/%02d", year, month, day) +} + +func main() { + router := gin.Default() + router.Delims("{[{", "}]}") + router.SetFuncMap(template.FuncMap{ + "formatAsDate": formatAsDate, + }) + router.LoadHTMLFiles("./testdata/template/raw.tmpl") + + router.GET("/raw", func(c *gin.Context) { + c.HTML(http.StatusOK, "raw.tmpl", map[string]interface{}{ + "now": time.Date(2017, 07, 01, 0, 0, 0, 0, time.UTC), + }) + }) + + router.Run(":8080") +} + +``` + +raw.tmpl + +```sh +Date: {[{.now | formatAsDate}]} +``` + +Result: +```sh +Date: 2017/07/01 +``` diff --git a/content/ru/docs/examples/http-method.md b/content/ru/docs/examples/http-method.md new file mode 100644 index 000000000..de7199323 --- /dev/null +++ b/content/ru/docs/examples/http-method.md @@ -0,0 +1,25 @@ +--- +Заголовок: "Использование метода HTTP" +draft: false +--- + +```go +func main() { + // Creates a gin router with default middleware: + // logger and recovery (crash-free) middleware + router := gin.Default() + + router.GET("/someGet", getting) + router.POST("/somePost", posting) + router.PUT("/somePut", putting) + router.DELETE("/someDelete", deleting) + router.PATCH("/somePatch", patching) + router.HEAD("/someHead", head) + router.OPTIONS("/someOptions", options) + + // By default it serves on :8080 unless a + // PORT environment variable was defined. + router.Run() + // router.Run(":3000") for a hard coded port +} +``` diff --git a/content/ru/docs/examples/http2-server-push.md b/content/ru/docs/examples/http2-server-push.md new file mode 100644 index 000000000..22130a359 --- /dev/null +++ b/content/ru/docs/examples/http2-server-push.md @@ -0,0 +1,51 @@ +--- +Заголовок: "HTTP2 server push" +черновик: false +--- + +http.Pusher поддерживается только **go1.8+**. Подробную информацию смотрите в [golang blog](https://blog.golang.org/h2push). + +```go +package main + +import ( + "html/template" + "log" + + "github.com/gin-gonic/gin" +) + +var html = template.Must(template.New("https").Parse(` + + + Https Test + + + +

Welcome, Ginner!

+ + +`)) + +func main() { + r := gin.Default() + r.Static("/assets", "./assets") + r.SetHTMLTemplate(html) + + r.GET("/", func(c *gin.Context) { + if pusher := c.Writer.Pusher(); pusher != nil { + // use pusher.Push() to do server push + if err := pusher.Push("/assets/app.js", nil); err != nil { + log.Printf("Failed to push: %v", err) + } + } + c.HTML(200, "https", gin.H{ + "status": "success", + }) + }) + + // Listen and Serve in https://127.0.0.1:8080 + r.RunTLS(":8080", "./testdata/server.pem", "./testdata/server.key") +} +``` + diff --git a/content/ru/docs/examples/jsonp.md b/content/ru/docs/examples/jsonp.md new file mode 100644 index 000000000..b5c75a17c --- /dev/null +++ b/content/ru/docs/examples/jsonp.md @@ -0,0 +1,25 @@ +--- +Заголовок: "JSONP" +черновик: false +--- + +Использование JSONP для запроса данных с сервера в другом домене. Добавьте обратный вызов в тело ответа, если существует обратный вызов параметра запроса. + +```go +func main() { + r := gin.Default() + + r.GET("/JSONP?callback=x", func(c *gin.Context) { + data := map[string]interface{}{ + "foo": "bar", + } + + //callback is x + // Will output : x({\"foo\":\"bar\"}) + c.JSONP(http.StatusOK, data) + }) + + // Listen and serve on 0.0.0.0:8080 + r.Run(":8080") +} +``` diff --git a/content/ru/docs/examples/map-as-querystring-or-postform.md b/content/ru/docs/examples/map-as-querystring-or-postform.md new file mode 100644 index 000000000..dd11f2416 --- /dev/null +++ b/content/ru/docs/examples/map-as-querystring-or-postform.md @@ -0,0 +1,31 @@ +--- +Заголовок: "Структура данных Map виде строки запроса или параметров постформы" +draft: false +--- + +```sh +POST /post?ids[a]=1234&ids[b]=hello HTTP/1.1 +Content-Type: application/x-www-form-urlencoded + +names[first]=thinkerou&names[second]=tianou +``` + +```go +func main() { + router := gin.Default() + + router.POST("/post", func(c *gin.Context) { + + ids := c.QueryMap("ids") + names := c.PostFormMap("names") + + fmt.Printf("ids: %v; names: %v", ids, names) + }) + router.Run(":8080") +} +``` + +```sh +ids: map[b:hello a:1234], names: map[second:tianou first:thinkerou] +``` + diff --git a/content/ru/docs/examples/multipart-urlencoded-binding.md b/content/ru/docs/examples/multipart-urlencoded-binding.md new file mode 100644 index 000000000..1486ec109 --- /dev/null +++ b/content/ru/docs/examples/multipart-urlencoded-binding.md @@ -0,0 +1,41 @@ +--- +Заголовок: "Связка Multipart/Urlencoded" +draft: false +--- + +```go +package main + +import ( + "github.com/gin-gonic/gin" +) + +type LoginForm struct { + User string `form:"user" binding:"required"` + Password string `form:"password" binding:"required"` +} + +func main() { + router := gin.Default() + router.POST("/login", func(c *gin.Context) { + // you can bind multipart form with explicit binding declaration: + // c.ShouldBindWith(&form, binding.Form) + // or you can simply use autobinding with ShouldBind method: + var form LoginForm + // in this case proper binding will be automatically selected + if c.ShouldBind(&form) == nil { + if form.User == "user" && form.Password == "password" { + c.JSON(200, gin.H{"status": "you are logged in"}) + } else { + c.JSON(401, gin.H{"status": "unauthorized"}) + } + } + }) + router.Run(":8080") +} +``` + +Тестирование: +```sh +$ curl -v --form user=user --form password=password http://localhost:8080/login +``` diff --git a/content/ru/docs/examples/multipart-urlencoded-form.md b/content/ru/docs/examples/multipart-urlencoded-form.md new file mode 100644 index 000000000..b9e4b6b6f --- /dev/null +++ b/content/ru/docs/examples/multipart-urlencoded-form.md @@ -0,0 +1,23 @@ +--- +Заголовок: "Форма Multipart/Urlencoded" +draft: false +--- + +```go +func main() { + router := gin.Default() + + router.POST("/form_post", func(c *gin.Context) { + message := c.PostForm("message") + nick := c.DefaultPostForm("nick", "anonymous") + + c.JSON(200, gin.H{ + "status": "posted", + "message": message, + "nick": nick, + }) + }) + router.Run(":8080") +} +``` + diff --git a/content/ru/docs/examples/multiple-template.md b/content/ru/docs/examples/multiple-template.md new file mode 100644 index 000000000..6f3f0b3ea --- /dev/null +++ b/content/ru/docs/examples/multiple-template.md @@ -0,0 +1,6 @@ +--- +Заголовок: "Несколько HTML темплейтов" +черновик: false +--- + +Gin позволяет по умолчанию использовать только один html.Template. Проверьте [a multitemplate render](https://github.com/gin-contrib/multitemplate) для использования таких возможностей, как go 1.6 `block template`. diff --git a/content/ru/docs/examples/only-bind-query-string.md b/content/ru/docs/examples/only-bind-query-string.md new file mode 100644 index 000000000..ffae2c6fc --- /dev/null +++ b/content/ru/docs/examples/only-bind-query-string.md @@ -0,0 +1,37 @@ +--- +Заголовок: "Только связывание строки запроса" +draft: false +--- + +Функция `ShouldBindQuery` связывает только параметры запроса, но не данные поста. См. [подробную информацию](https://github.com/gin-gonic/gin/issues/742#issuecomment-315953017). + +```go +package main + +import ( + "log" + + "github.com/gin-gonic/gin" +) + +type Person struct { + Name string `form:"name"` + Address string `form:"address"` +} + +func main() { + route := gin.Default() + route.Any("/testing", startPage) + route.Run(":8085") +} + +func startPage(c *gin.Context) { + var person Person + if c.ShouldBindQuery(&person) == nil { + log.Println("====== Only Bind By Query String ======") + log.Println(person.Name) + log.Println(person.Address) + } + c.String(200, "Success") +} +``` diff --git a/content/ru/docs/examples/param-in-path.md b/content/ru/docs/examples/param-in-path.md new file mode 100644 index 000000000..4cecad160 --- /dev/null +++ b/content/ru/docs/examples/param-in-path.md @@ -0,0 +1,27 @@ +--- +Заголовок: "Параметры в path" +черновик: false +--- + +```go +func main() { + router := gin.Default() + + // This handler will match /user/john but will not match /user/ or /user + router.GET("/user/:name", func(c *gin.Context) { + name := c.Param("name") + c.String(http.StatusOK, "Hello %s", name) + }) + + // However, this one will match /user/john/ and also /user/john/send + // If no other routers match /user/john, it will redirect to /user/john/ + router.GET("/user/:name/*action", func(c *gin.Context) { + name := c.Param("name") + action := c.Param("action") + message := name + " is " + action + c.String(http.StatusOK, message) + }) + + router.Run(":8080") +} +``` diff --git a/content/ru/docs/examples/pure-json.md b/content/ru/docs/examples/pure-json.md new file mode 100644 index 000000000..cf2b94346 --- /dev/null +++ b/content/ru/docs/examples/pure-json.md @@ -0,0 +1,30 @@ +--- +Заголовок: "PureJSON" +черновик: false +--- + +Обычно JSON заменяет специальные HTML-символы их юникодными сущностями, например, `<` становится `\u003c`. Если вы хотите кодировать такие символы буквально, вы можете использовать PureJSON. +Эта функция недоступна в Go 1.6 и ниже. + +```go +func main() { + r := gin.Default() + + // Serves unicode entities + r.GET("/json", func(c *gin.Context) { + c.JSON(200, gin.H{ + "html": "Hello, world!", + }) + }) + + // Serves literal characters + r.GET("/purejson", func(c *gin.Context) { + c.PureJSON(200, gin.H{ + "html": "Hello, world!", + }) + }) + + // listen and serve on 0.0.0.0:8080 + r.Run(":8080") +} +``` diff --git a/content/ru/docs/examples/query-and-post-form.md b/content/ru/docs/examples/query-and-post-form.md new file mode 100644 index 000000000..0942d484f --- /dev/null +++ b/content/ru/docs/examples/query-and-post-form.md @@ -0,0 +1,32 @@ +--- +Заголовок: "Форма для запросов и сообщений" +draft: false +--- + +```sh +POST /post?id=1234&page=1 HTTP/1.1 +Content-Type: application/x-www-form-urlencoded + +name=manu&message=this_is_great +``` + +```go +func main() { + router := gin.Default() + + router.POST("/post", func(c *gin.Context) { + + id := c.Query("id") + page := c.DefaultQuery("page", "0") + name := c.PostForm("name") + message := c.PostForm("message") + + fmt.Printf("id: %s; page: %s; name: %s; message: %s", id, page, name, message) + }) + router.Run(":8080") +} +``` + +```sh +id: 1234; page: 1; name: manu; message: this_is_great +``` diff --git a/content/ru/docs/examples/querystring-param.md b/content/ru/docs/examples/querystring-param.md new file mode 100644 index 000000000..80f4920ad --- /dev/null +++ b/content/ru/docs/examples/querystring-param.md @@ -0,0 +1,20 @@ +--- +Заголовок: "Параметры строки запроса" +draft: false +--- + +```go +func main() { + router := gin.Default() + + // Query string parameters are parsed using the existing underlying request object. + // The request responds to a url matching: /welcome?firstname=Jane&lastname=Doe + router.GET("/welcome", func(c *gin.Context) { + firstname := c.DefaultQuery("firstname", "Guest") + lastname := c.Query("lastname") // shortcut for c.Request.URL.Query().Get("lastname") + + c.String(http.StatusOK, "Hello %s %s", firstname, lastname) + }) + router.Run(":8080") +} +``` diff --git a/content/ru/docs/examples/redirects.md b/content/ru/docs/examples/redirects.md new file mode 100644 index 000000000..a941d5c4d --- /dev/null +++ b/content/ru/docs/examples/redirects.md @@ -0,0 +1,32 @@ +--- +Заголовок: "Перенаправления" +черновик: false +--- + +Создать HTTP-перенаправление очень просто. Поддерживаются как внутренние, так и внешние расположения. + +```go +r.GET("/test", func(c *gin.Context) { + c.Redirect(http.StatusMovedPermanently, "http://www.google.com/") +}) +``` + +Выдача HTTP-перенаправления из POST. См. выпуск: [#444](https://github.com/gin-gonic/gin/issues/444) + +```go +r.POST("/test", func(c *gin.Context) { + c.Redirect(http.StatusFound, "/foo") +}) +``` + +Выдавая перенаправление Router, используйте `HandleContext`, как показано ниже. + +``` go +r.GET("/test", func(c *gin.Context) { + c.Request.URL.Path = "/test2" + r.HandleContext(c) +}) +r.GET("/test2", func(c *gin.Context) { + c.JSON(200, gin.H{"hello": "world"}) +}) +``` diff --git a/content/ru/docs/examples/rendering.md b/content/ru/docs/examples/rendering.md new file mode 100644 index 000000000..1bb3e6844 --- /dev/null +++ b/content/ru/docs/examples/rendering.md @@ -0,0 +1,54 @@ +--- +Заголовок: "Рендеринг XML/JSON/YAML/ProtoBuf" +черновик: false +--- + +```go +func main() { + r := gin.Default() + + // gin.H is a shortcut for map[string]interface{} + r.GET("/someJSON", func(c *gin.Context) { + c.JSON(http.StatusOK, gin.H{"message": "hey", "status": http.StatusOK}) + }) + + r.GET("/moreJSON", func(c *gin.Context) { + // You also can use a struct + var msg struct { + Name string `json:"user"` + Message string + Number int + } + msg.Name = "Lena" + msg.Message = "hey" + msg.Number = 123 + // Note that msg.Name becomes "user" in the JSON + // Will output : {"user": "Lena", "Message": "hey", "Number": 123} + c.JSON(http.StatusOK, msg) + }) + + r.GET("/someXML", func(c *gin.Context) { + c.XML(http.StatusOK, gin.H{"message": "hey", "status": http.StatusOK}) + }) + + r.GET("/someYAML", func(c *gin.Context) { + c.YAML(http.StatusOK, gin.H{"message": "hey", "status": http.StatusOK}) + }) + + r.GET("/someProtoBuf", func(c *gin.Context) { + reps := []int64{int64(1), int64(2)} + label := "test" + // The specific definition of protobuf is written in the testdata/protoexample file. + data := &protoexample.Test{ + Label: &label, + Reps: reps, + } + // Note that data becomes binary data in the response + // Will output protoexample.Test protobuf serialized data + c.ProtoBuf(http.StatusOK, data) + }) + + // Listen and serve on 0.0.0.0:8080 + r.Run(":8080") +} +``` diff --git a/content/ru/docs/examples/run-multiple-service.md b/content/ru/docs/examples/run-multiple-service.md new file mode 100644 index 000000000..2c581c247 --- /dev/null +++ b/content/ru/docs/examples/run-multiple-service.md @@ -0,0 +1,84 @@ +--- +название: "Запустить несколько сервисов" +черновик: false +--- + +Посмотрите [вопрос](https://github.com/gin-gonic/gin/issues/346) и попробуйте следующий пример: + +```go +package main + +import ( + "log" + "net/http" + "time" + + "github.com/gin-gonic/gin" + "golang.org/x/sync/errgroup" +) + +var ( + g errgroup.Group +) + +func router01() http.Handler { + e := gin.New() + e.Use(gin.Recovery()) + e.GET("/", func(c *gin.Context) { + c.JSON( + http.StatusOK, + gin.H{ + "code": http.StatusOK, + "message": "Welcome server 01", + }, + ) + }) + + return e +} + +func router02() http.Handler { + e := gin.New() + e.Use(gin.Recovery()) + e.GET("/", func(c *gin.Context) { + c.JSON( + http.StatusOK, + gin.H{ + "code": http.StatusOK, + "message": "Welcome server 02", + }, + ) + }) + + return e +} + +func main() { + server01 := &http.Server{ + Addr: ":8080", + Handler: router01(), + ReadTimeout: 5 * time.Second, + WriteTimeout: 10 * time.Second, + } + + server02 := &http.Server{ + Addr: ":8081", + Handler: router02(), + ReadTimeout: 5 * time.Second, + WriteTimeout: 10 * time.Second, + } + + g.Go(func() error { + return server01.ListenAndServe() + }) + + g.Go(func() error { + return server02.ListenAndServe() + }) + + if err := g.Wait(); err != nil { + log.Fatal(err) + } +} +``` + diff --git a/content/ru/docs/examples/secure-json.md b/content/ru/docs/examples/secure-json.md new file mode 100644 index 000000000..56ab23230 --- /dev/null +++ b/content/ru/docs/examples/secure-json.md @@ -0,0 +1,25 @@ +--- +название: "SecureJSON" +черновик: false +--- + +Использование SecureJSON для предотвращения перехвата json. По умолчанию добавляет `"while(1),"` в тело ответа, если заданная структура представляет собой массив значений. + +```go +func main() { + r := gin.Default() + + // You can also use your own secure json prefix + // r.SecureJsonPrefix(")]}',\n") + + r.GET("/someJSON", func(c *gin.Context) { + names := []string{"lena", "austin", "foo"} + + // Will output : while(1);["lena","austin","foo"] + c.SecureJSON(http.StatusOK, names) + }) + + // Listen and serve on 0.0.0.0:8080 + r.Run(":8080") +} +``` diff --git a/content/ru/docs/examples/security-headers.md b/content/ru/docs/examples/security-headers.md new file mode 100644 index 000000000..c6092216f --- /dev/null +++ b/content/ru/docs/examples/security-headers.md @@ -0,0 +1,75 @@ +--- +название: "Заголовки безопасности" +черновик: false +--- + +Важно использовать заголовки безопасности, чтобы защитить ваше веб-приложение от распространенных уязвимостей. Этот пример показывает, как добавить заголовки безопасности в ваше приложение Gin, а также как избежать атак, связанных с инъекцией заголовков хоста (SSRF, Open Redirection). + +```go +package main + +import ( + "net/http" + + "github.com/gin-gonic/gin" +) + +func main() { + r := gin.Default() + + expectedHost := "localhost:8080" + + // Setup Security Headers + r.Use(func(c *gin.Context) { + if c.Request.Host != expectedHost { + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "Invalid host header"}) + return + } + c.Header("X-Frame-Options", "DENY") + c.Header("Content-Security-Policy", "default-src 'self'; connect-src *; font-src *; script-src-elem * 'unsafe-inline'; img-src * data:; style-src * 'unsafe-inline';") + c.Header("X-XSS-Protection", "1; mode=block") + c.Header("Strict-Transport-Security", "max-age=31536000; includeSubDomains; preload") + c.Header("Referrer-Policy", "strict-origin") + c.Header("X-Content-Type-Options", "nosniff") + c.Header("Permissions-Policy", "geolocation=(),midi=(),sync-xhr=(),microphone=(),camera=(),magnetometer=(),gyroscope=(),fullscreen=(self),payment=()") + c.Next() + }) + + r.GET("/ping", func(c *gin.Context) { + c.JSON(200, gin.H{ + "message": "pong", + }) + }) + + r.Run() // listen and serve on 0.0.0.0:8080 +} +``` + +Вы можете проверить его с помощью `curl`: + +```bash +// Проверка заголовок + +curl localhost:8080/ping -I + +HTTP/1.1 404 Not Found +Content-Security-Policy: default-src 'self'; connect-src *; font-src *; script-src-elem * 'unsafe-inline'; img-src * data:; style-src * 'unsafe-inline'; +Content-Type: text/plain +Permissions-Policy: geolocation=(),midi=(),sync-xhr=(),microphone=(),camera=(),magnetometer=(),gyroscope=(),fullscreen=(self),payment=() +Referrer-Policy: strict-origin +Strict-Transport-Security: max-age=31536000; includeSubDomains; preload +X-Content-Type-Options: nosniff +X-Frame-Options: DENY +X-Xss-Protection: 1; mode=block +Date: Sat, 30 Mar 2024 08:20:44 GMT +Content-Length: 18 + +// Проверка инъекции в заголовок хоста + +curl localhost:8080/ping -I -H "Host:neti.ee" + +HTTP/1.1 400 Bad Request +Content-Type: application/json; charset=utf-8 +Date: Sat, 30 Mar 2024 08:21:09 GMT +Content-Length: 31 +``` \ No newline at end of file diff --git a/content/ru/docs/examples/serving-data-from-reader.md b/content/ru/docs/examples/serving-data-from-reader.md new file mode 100644 index 000000000..af201e164 --- /dev/null +++ b/content/ru/docs/examples/serving-data-from-reader.md @@ -0,0 +1,28 @@ +--- +Заголовок: "Манипуляция данных от читателя" +draft: false +--- + +```go +func main() { + router := gin.Default() + router.GET("/someDataFromReader", func(c *gin.Context) { + response, err := http.Get("https://raw.githubusercontent.com/gin-gonic/logo/master/color.png") + if err != nil || response.StatusCode != http.StatusOK { + c.Status(http.StatusServiceUnavailable) + return + } + + reader := response.Body + contentLength := response.ContentLength + contentType := response.Header.Get("Content-Type") + + extraHeaders := map[string]string{ + "Content-Disposition": `attachment; filename="gopher.png"`, + } + + c.DataFromReader(http.StatusOK, contentLength, contentType, reader, extraHeaders) + }) + router.Run(":8080") +} +``` diff --git a/content/ru/docs/examples/serving-static-files.md b/content/ru/docs/examples/serving-static-files.md new file mode 100644 index 000000000..308edc0a9 --- /dev/null +++ b/content/ru/docs/examples/serving-static-files.md @@ -0,0 +1,16 @@ +--- +Заголовок: "Использование статических файлов" +draft: false +--- + +```go +func main() { + router := gin.Default() + router.Static("/assets", "./assets") + router.StaticFS("/more_static", http.Dir("my_file_system")) + router.StaticFile("/favicon.ico", "./resources/favicon.ico") + + // Listen and serve on 0.0.0.0:8080 + router.Run(":8080") +} +``` diff --git a/content/ru/docs/examples/support-lets-encrypt.md b/content/ru/docs/examples/support-lets-encrypt.md new file mode 100644 index 000000000..c7812db31 --- /dev/null +++ b/content/ru/docs/examples/support-lets-encrypt.md @@ -0,0 +1,60 @@ +--- +Заголовок: "Поддержка Let's Encrypt" +черновик: false +--- + +пример для 1-строчных HTTPS-серверов LetsEncrypt. + +```go +package main + +import ( + "log" + + "github.com/gin-gonic/autotls" + "github.com/gin-gonic/gin" +) + +func main() { + r := gin.Default() + + // Ping handler + r.GET("/ping", func(c *gin.Context) { + c.String(200, "pong") + }) + + log.Fatal(autotls.Run(r, "example1.com", "example2.com")) +} +``` + +пример для пользовательского менеджера автосертификации. + +```go +package main + +import ( + "log" + + "github.com/gin-gonic/autotls" + "github.com/gin-gonic/gin" + "golang.org/x/crypto/acme/autocert" +) + +func main() { + r := gin.Default() + + // Ping handler + r.GET("/ping", func(c *gin.Context) { + c.String(200, "pong") + }) + + m := autocert.Manager{ + Prompt: autocert.AcceptTOS, + HostPolicy: autocert.HostWhitelist("example1.com", "example2.com"), + Cache: autocert.DirCache("/var/www/.cache"), + } + + log.Fatal(autotls.RunWithManager(r, &m)) +} +``` + diff --git a/content/ru/docs/examples/upload-file/_index.md b/content/ru/docs/examples/upload-file/_index.md new file mode 100644 index 000000000..442a9edb9 --- /dev/null +++ b/content/ru/docs/examples/upload-file/_index.md @@ -0,0 +1,6 @@ +--- +название: "Загрузить файлы" +draft: false +--- + +Раздел содержит список использования api загрузки файлов. diff --git a/content/ru/docs/examples/upload-file/multiple-file.md b/content/ru/docs/examples/upload-file/multiple-file.md new file mode 100644 index 000000000..d3b486079 --- /dev/null +++ b/content/ru/docs/examples/upload-file/multiple-file.md @@ -0,0 +1,37 @@ +--- +title: "Multiple files" +draft: false +--- + +Смотрите подробности [пример кода](https://github.com/gin-gonic/examples/tree/master/upload-file/multiple). + +```go +func main() { + router := gin.Default() + // Set a lower memory limit for multipart forms (default is 32 MiB) + router.MaxMultipartMemory = 8 << 20 // 8 MiB + router.POST("/upload", func(c *gin.Context) { + // Multipart form + form, _ := c.MultipartForm() + files := form.File["upload[]"] + + for _, file := range files { + log.Println(file.Filename) + + // Upload the file to specific dst. + c.SaveUploadedFile(file, dst) + } + c.String(http.StatusOK, fmt.Sprintf("%d files uploaded!", len(files))) + }) + router.Run(":8080") +} +``` + +Как тестировать с помощью `curl`: + +```sh +curl -X POST http://localhost:8080/upload \ + -F "upload[]=@/Users/appleboy/test1.zip" \ + -F "upload[]=@/Users/appleboy/test2.zip" \ + -H "Content-Type: multipart/form-data" +``` diff --git a/content/ru/docs/examples/upload-file/single-file.md b/content/ru/docs/examples/upload-file/single-file.md new file mode 100644 index 000000000..7c84d6e34 --- /dev/null +++ b/content/ru/docs/examples/upload-file/single-file.md @@ -0,0 +1,36 @@ +--- +title: "Single file" +draft: false +--- +Ссылки на проблему [#774](https://github.com/gin-gonic/gin/issues/774) и деталь [пример кода](https://github.com/gin-gonic/examples/tree/master/upload-file/single). + +`file.Filename` **НЕ ДОЛЖНО** быть доверенным. См. [`Content-Disposition` на MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition#Directives) и [#1693](https://github.com/gin-gonic/gin/issues/1693) + +> Имя файла всегда необязательно и не должно использоваться приложением вслепую: информация о пути должна быть удалена, и должно быть выполнено преобразование к правилам файловой системы сервера. + +```go +func main() { + router := gin.Default() + // Set a lower memory limit for multipart forms (default is 32 MiB) + router.MaxMultipartMemory = 8 << 20 // 8 MiB + router.POST("/upload", func(c *gin.Context) { + // single file + file, _ := c.FormFile("file") + log.Println(file.Filename) + + // Upload the file to specific dst. + c.SaveUploadedFile(file, dst) + + c.String(http.StatusOK, fmt.Sprintf("'%s' uploaded!", file.Filename)) + }) + router.Run(":8080") +} +``` + +Как тестировать с помощью `curl`: + +```sh +curl -X POST http://localhost:8080/upload \ + -F "file=@/Users/appleboy/test.zip" \ + -H "Content-Type: multipart/form-data" +``` diff --git a/content/ru/docs/examples/using-basicauth-middleware.md b/content/ru/docs/examples/using-basicauth-middleware.md new file mode 100644 index 000000000..6b83d49c9 --- /dev/null +++ b/content/ru/docs/examples/using-basicauth-middleware.md @@ -0,0 +1,41 @@ +--- +Заголовок: "Использование промежуточного ПО BasicAuth" +черновик: false +--- + +```go +// simulate some private data +var secrets = gin.H{ + "foo": gin.H{"email": "foo@bar.com", "phone": "123433"}, + "austin": gin.H{"email": "austin@example.com", "phone": "666"}, + "lena": gin.H{"email": "lena@guapa.com", "phone": "523443"}, +} + +func main() { + r := gin.Default() + + // Group using gin.BasicAuth() middleware + // gin.Accounts is a shortcut for map[string]string + authorized := r.Group("/admin", gin.BasicAuth(gin.Accounts{ + "foo": "bar", + "austin": "1234", + "lena": "hello2", + "manu": "4321", + })) + + // /admin/secrets endpoint + // hit "localhost:8080/admin/secrets + authorized.GET("/secrets", func(c *gin.Context) { + // get user, it was set by the BasicAuth middleware + user := c.MustGet(gin.AuthUserKey).(string) + if secret, ok := secrets[user]; ok { + c.JSON(http.StatusOK, gin.H{"user": user, "secret": secret}) + } else { + c.JSON(http.StatusOK, gin.H{"user": user, "secret": "NO SECRET :("}) + } + }) + + // Listen and serve on 0.0.0.0:8080 + r.Run(":8080") +} +``` diff --git a/content/ru/docs/examples/using-middleware.md b/content/ru/docs/examples/using-middleware.md new file mode 100644 index 000000000..00e1e4147 --- /dev/null +++ b/content/ru/docs/examples/using-middleware.md @@ -0,0 +1,43 @@ +--- +Заголовок: "Использование промежуточного ПО" +черновик: false +--- + +```go +func main() { + // Creates a router without any middleware by default + r := gin.New() + + // Global middleware + // Logger middleware will write the logs to gin.DefaultWriter even if you set with GIN_MODE=release. + // By default gin.DefaultWriter = os.Stdout + r.Use(gin.Logger()) + + // Recovery middleware recovers from any panics and writes a 500 if there was one. + r.Use(gin.Recovery()) + + // Per route middleware, you can add as many as you desire. + r.GET("/benchmark", MyBenchLogger(), benchEndpoint) + + // Authorization group + // authorized := r.Group("/", AuthRequired()) + // exactly the same as: + authorized := r.Group("/") + // per group middleware! in this case we use the custom created + // AuthRequired() middleware just in the "authorized" group. + authorized.Use(AuthRequired()) + { + authorized.POST("/login", loginEndpoint) + authorized.POST("/submit", submitEndpoint) + authorized.POST("/read", readEndpoint) + + // nested group + testing := authorized.Group("testing") + testing.GET("/analytics", analyticsEndpoint) + } + + // Listen and serve on 0.0.0.0:8080 + r.Run(":8080") +} +``` + diff --git a/content/ru/docs/examples/without-middleware.md b/content/ru/docs/examples/without-middleware.md new file mode 100644 index 000000000..e4884ecaa --- /dev/null +++ b/content/ru/docs/examples/without-middleware.md @@ -0,0 +1,17 @@ +--- +Заголовок: "Без промежуточного ПО по умолчанию" +draft: false +--- + +Используйте + +```go +r := gin.New() +``` + +место + +```go +// Default With the Logger and Recovery middleware already attached +r := gin.Default() +``` diff --git a/content/ru/docs/examples/write-log.md b/content/ru/docs/examples/write-log.md new file mode 100644 index 000000000..c42f21c75 --- /dev/null +++ b/content/ru/docs/examples/write-log.md @@ -0,0 +1,25 @@ +--- +Заголовок: "Как записать файл журнала" +draft: false +--- + +```go +func main() { + // Disable Console Color, you don't need console color when writing the logs to file. + gin.DisableConsoleColor() + + // Logging to a file. + f, _ := os.Create("gin.log") + gin.DefaultWriter = io.MultiWriter(f) + + // Use the following code if you need to write the logs to file and console at the same time. + // gin.DefaultWriter = io.MultiWriter(f, os.Stdout) + + router := gin.Default() + router.GET("/ping", func(c *gin.Context) { + c.String(200, "pong") + }) + +    router.Run(":8080") +} +``` diff --git a/content/ru/docs/faq/_index.md b/content/ru/docs/faq/_index.md new file mode 100644 index 000000000..1e0345a1a --- /dev/null +++ b/content/ru/docs/faq/_index.md @@ -0,0 +1,8 @@ +--- +Заголовок: "FAQ" +черновик: false +вес: 9 +--- + +TODO: записать некоторые часто задаваемые вопросы из вкладки GitHub Issue. + diff --git a/content/ru/docs/features/_index.md b/content/ru/docs/features/_index.md new file mode 100644 index 000000000..08607d579 --- /dev/null +++ b/content/ru/docs/features/_index.md @@ -0,0 +1,17 @@ +--- +Заголовок: "Особенности" +черновик: false +вес: 4 +--- + +## Стабильные возможности Gin v1: + +- Маршрутизатор с нулевым распределением. + +- По-прежнему самый быстрый http-маршрутизатор и фреймворк. От маршрутизации до написания. + +- Полный набор модульных тестов. + +- Проверено в бою. + +- API заморожен, новые релизы не сломают ваш код. diff --git a/content/ru/docs/introduction/_index.md b/content/ru/docs/introduction/_index.md new file mode 100644 index 000000000..1e93bee72 --- /dev/null +++ b/content/ru/docs/introduction/_index.md @@ -0,0 +1,49 @@ +--- +Заголовок: "Введение" +черновик: false +вес: 1 +--- + +Gin - это веб-фреймворк, написанный на языке Go (Golang). В нем реализован мартини-подобный API с гораздо лучшей производительностью, до 40 раз быстрее благодаря [httprouter](https://github.com/julienschmidt/httprouter). Если вам нужна производительность и хорошая продуктивность, вам понравится Gin. + +В этом разделе мы расскажем о том, что такое Gin, какие проблемы он решает и как он может помочь вашему проекту. + +Если же вы уже готовы использовать Gin в своем проекте, посетите [Quickstart](https://gin-gonic.com/docs/quickstart/). + +## Особенности + +### Быстрый + +Маршрутизация на основе радиксного дерева, малый объем памяти. Никаких отражений. Предсказуемая производительность API. + +### Поддержка промежуточного ПО + +Входящий HTTP-запрос может быть обработан цепочкой промежуточных программ и завершен конечным действием. +Например: Логгер, Авторизация, GZIP и, наконец, публикация сообщения в БД. + +### Защита от сбоев + +Gin может поймать панику, возникшую во время HTTP-запроса, и восстановить ее. Таким образом, ваш сервер будет всегда доступен. В качестве примера - можно также сообщить об этой панике в Sentry! + +Переведено с помощью DeepL.com (бесплатная версия) + +### Проверка JSON + +Gin может анализировать и проверять JSON-запрос - например, проверять наличие необходимых значений. + +### Группировка маршрутов + +Организуйте свои маршруты лучше. Требуемая и необязательная авторизация, разные версии API... Кроме того, группы могут быть неограниченно вложены друг в друга без снижения производительности. + +### Управление ошибками + +Gin предоставляет удобный способ сбора всех ошибок, возникших во время HTTP-запроса. В конечном итоге промежуточное ПО может записывать их в лог-файл, в базу данных или отправлять по сети. + +### Рендеринг встроенный + +Gin предоставляет простой в использовании API для рендеринга JSON, XML и HTML. + +### Расширяемость + +Создать новое промежуточное ПО очень просто, просто посмотрите примеры кодов. + diff --git a/content/ru/docs/jsoniter/_index.md b/content/ru/docs/jsoniter/_index.md new file mode 100644 index 000000000..0d88e1de7 --- /dev/null +++ b/content/ru/docs/jsoniter/_index.md @@ -0,0 +1,13 @@ +--- +название: "Jsoniter" +черновик: false +вес: 5 +--- + +## Сборка с [jsoniter](https://github.com/json-iterator/go) + +Gin использует `encoding/json` как пакет json по умолчанию, но вы можете изменить его на [jsoniter](https://github.com/json-iterator/go), собирая из других тегов. + +``sh +$ go build -tags=jsoniter . +``` diff --git a/content/ru/docs/quickstart/_index.md b/content/ru/docs/quickstart/_index.md new file mode 100644 index 000000000..080171f06 --- /dev/null +++ b/content/ru/docs/quickstart/_index.md @@ -0,0 +1,87 @@ +--- +название: "Быстрый старт" +черновик: false +вес: 2 +--- + +В этом кратком руководстве мы извлечем уроки из сегментов кода и узнаем, как: + +## Требования + +- Go 1.16 или выше + +## Установка + +Чтобы установить пакет Gin, необходимо сначала установить Go и настроить рабочее пространство Go. + +1. Скачайте и установите его: + +```sh +$ go get -u github.com/gin-gonic/gin +``` + +2. Импортируйте его в свой код: + +```go +import "github.com/gin-gonic/gin" +``` + +3. (Необязательно) Импортируйте `net/http`. Это необходимо, например, при использовании таких констант, как `http.StatusOK`. + +```go +import "net/http" +``` + +1. Создайте папку проекта и `cd` в ней + +```sh +$ mkdir -p $GOPATH/src/github.com/myusername/project && cd "$_" +``` + +2. Скопируйте начальный шаблон в свой проект + +```sh +$ curl https://raw.githubusercontent.com/gin-gonic/examples/master/basic/main.go > main.go +``` + +3. Запустите свой проект + +```sh +$ go run main.go +``` + +## Начало работы + +> Не знаете, как написать и выполнить код Go? [Нажмите здесь](https://golang.org/doc/code.html). + +Сначала создайте файл с именем `example.go`: + +```sh +# assume the following codes in example.go file +$ touch example.go +``` + +Затем поместите следующий код в файл `example.go`: + +```go +package main + +import "github.com/gin-gonic/gin" + +func main() { + r := gin.Default() + r.GET("/ping", func(c *gin.Context) { + c.JSON(200, gin.H{ + "message": "pong", + }) + }) + r.Run() // listen and serve on 0.0.0.0:8080 +} +``` + +И вы можете запустить код с помощью `go run example.go`: + +```sh +# run example.go and visit 0.0.0.0:8080/ping on browser +$ go run example.go +``` diff --git a/content/ru/docs/testing/_index.md b/content/ru/docs/testing/_index.md new file mode 100644 index 000000000..f175db7e2 --- /dev/null +++ b/content/ru/docs/testing/_index.md @@ -0,0 +1,89 @@ +--- +название: "Тестирование" +черновик: false +вес: 7 +--- + +## Как написать тестовый пример для Gin? + +Пакет `net/http/httptest` является предпочтительным способом для тестирования HTTP. + +```go +package main + +import "github.com/gin-gonic/gin" + +type User struct { + Username string `json:"username"` + Gender string `json:"gender"` +} + +func setupRouter() *gin.Engine { + r := gin.Default() + r.GET("/ping", func(c *gin.Context) { + c.String(200, "pong") + }) + return r +} + +func postUser(r *gin.Engine) *gin.Engine { + r.POST("/user/add", func(c *gin.Context) { + var user User + c.BindJSON(&user) + c.JSON(200, user) + }) + return r +} + +func main() { + r := setupRouter() + r = postUser(r) + r.Run(":8080") +} +``` + +Тест для примера кода, приведенного выше: + +```go +package main + +import ( + "net/http" + "net/http/httptest" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestPingRoute(t *testing.T) { + router := setupRouter() + + w := httptest.NewRecorder() + req, _ := http.NewRequest("GET", "/ping", nil) + router.ServeHTTP(w, req) + + assert.Equal(t, 200, w.Code) + assert.Equal(t, "pong", w.Body.String()) +} + +// Test for POST /user/add +func TestPostUser(t *testing.T) { + router := setupRouter() + router = postUser(router) + + w := httptest.NewRecorder() + + // Create an example user for testing + exampleUser := User{ + Username: "test_name", + Gender: "male", + } + userJson, _ := json.Marshal(exampleUser) + req, _ := http.NewRequest("POST", "/user/add", strings.NewReader(string(userJson))) + router.ServeHTTP(w, req) + + assert.Equal(t, 200, w.Code) + // Compare the response body with the json data of exampleUser + assert.Equal(t, string(userJson), w.Body.String()) +} +``` diff --git a/content/ru/docs/users/_index.md b/content/ru/docs/users/_index.md new file mode 100644 index 000000000..c3c973b91 --- /dev/null +++ b/content/ru/docs/users/_index.md @@ -0,0 +1,21 @@ +--- +title: "Users" +draft: false +weight: 8 +--- + +##### Awesome project lists using [Gin](https://github.com/gin-gonic/gin) web framework: + +* [gorush](https://github.com/appleboy/gorush): A push notification server written in Go. + +* [fnproject](https://github.com/fnproject/fn): The container native, cloud agnostic serverless platform. + +* [photoprism](https://github.com/photoprism/photoprism): Personal photo management powered by Go and Google TensorFlow. + +* [krakend](https://github.com/devopsfaith/krakend): Ultra performant API Gateway with middlewares. + +* [picfit](https://github.com/thoas/picfit): An image resizing server written in Go. + +* [gotify](https://github.com/gotify/server): A simple server for sending and receiving messages in real-time per web socket. + +* [cds](https://github.com/ovh/cds): Enterprise-Grade Continuous Delivery & DevOps Automation Open Source Platform. diff --git a/content/ru/featured-background.jpg b/content/ru/featured-background.jpg new file mode 100644 index 000000000..8bb8a4cc6 Binary files /dev/null and b/content/ru/featured-background.jpg differ diff --git a/content/ru/search.md b/content/ru/search.md new file mode 100644 index 000000000..ec487e522 --- /dev/null +++ b/content/ru/search.md @@ -0,0 +1,5 @@ +--- +title: Результаты поиска +layout: поиск +--- +