doc.go 59 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794
  1. // Copyright (c) 2017-2018 The Iris Authors. All rights reserved.
  2. //
  3. // Redistribution and use in source and binary forms, with or without
  4. // modification, are permitted provided that the following conditions are
  5. // met:
  6. //
  7. // * Redistributions of source code must retain the above copyright
  8. // notice, this list of conditions and the following disclaimer.
  9. // * Redistributions in binary form must reproduce the above
  10. // copyright notice, this list of conditions and the following disclaimer
  11. // in the documentation and/or other materials provided with the
  12. // distribution.
  13. // * Neither the name of Iris nor the names of its
  14. // contributors may be used to endorse or promote products derived from
  15. // this software without specific prior written permission.
  16. //
  17. // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  18. // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  19. // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  20. // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  21. // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  22. // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  23. // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  24. // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  25. // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  26. // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  27. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  28. /*
  29. Package iris provides a beautifully expressive and easy to use foundation for your next website, API, or distributed app.
  30. Source code and other details for the project are available at GitHub:
  31. https://github.com/kataras/iris
  32. Current Version
  33. 10.7.0
  34. Installation
  35. The only requirement is the Go Programming Language, at least version 1.8 but 1.10 and above is highly recommended.
  36. $ go get -u github.com/kataras/iris
  37. Example code:
  38. package main
  39. import "github.com/kataras/iris"
  40. // User is just a bindable object structure.
  41. type User struct {
  42. Username string `json:"username"`
  43. Firstname string `json:"firstname"`
  44. Lastname string `json:"lastname"`
  45. City string `json:"city"`
  46. Age int `json:"age"`
  47. }
  48. func main() {
  49. app := iris.New()
  50. // Define templates using the std html/template engine.
  51. // Parse and load all files inside "./views" folder with ".html" file extension.
  52. // Reload the templates on each request (development mode).
  53. app.RegisterView(iris.HTML("./views", ".html").Reload(true))
  54. // Register custom handler for specific http errors.
  55. app.OnErrorCode(iris.StatusInternalServerError, func(ctx iris.Context) {
  56. // .Values are used to communicate between handlers, middleware.
  57. errMessage := ctx.Values().GetString("error")
  58. if errMessage != "" {
  59. ctx.Writef("Internal server error: %s", errMessage)
  60. return
  61. }
  62. ctx.Writef("(Unexpected) internal server error")
  63. })
  64. app.Use(func(ctx iris.Context) {
  65. ctx.Application().Logger().Infof("Begin request for path: %s", ctx.Path())
  66. ctx.Next()
  67. })
  68. // app.Done(func(ctx iris.Context) {})
  69. // Method POST: http://localhost:8080/decode
  70. app.Post("/decode", func(ctx iris.Context) {
  71. var user User
  72. ctx.ReadJSON(&user)
  73. ctx.Writef("%s %s is %d years old and comes from %s", user.Firstname, user.Lastname, user.Age, user.City)
  74. })
  75. // Method GET: http://localhost:8080/encode
  76. app.Get("/encode", func(ctx iris.Context) {
  77. doe := User{
  78. Username: "Johndoe",
  79. Firstname: "John",
  80. Lastname: "Doe",
  81. City: "Neither FBI knows!!!",
  82. Age: 25,
  83. }
  84. ctx.JSON(doe)
  85. })
  86. // Method GET: http://localhost:8080/profile/anytypeofstring
  87. app.Get("/profile/{username:string}", profileByUsername)
  88. // Want to use a custom regex expression instead?
  89. // Easy: app.Get("/profile/{username:string regexp(^[a-zA-Z ]+$)}")
  90. //
  91. // If parameter type is missing then it's string which accepts anything,
  92. // i.e: /{paramname} it's exactly the same as /{paramname:string}.
  93. usersRoutes := app.Party("/users", logThisMiddleware)
  94. {
  95. // Method GET: http://localhost:8080/users/42
  96. usersRoutes.Get("/{id:int min(1)}", getUserByID)
  97. // Method POST: http://localhost:8080/users/create
  98. usersRoutes.Post("/create", createUser)
  99. }
  100. // Listen for incoming HTTP/1.x & HTTP/2 clients on localhost port 8080.
  101. app.Run(iris.Addr(":8080"), iris.WithCharset("UTF-8"))
  102. }
  103. func logThisMiddleware(ctx iris.Context) {
  104. ctx.Application().Logger().Infof("Path: %s | IP: %s", ctx.Path(), ctx.RemoteAddr())
  105. // .Next is required to move forward to the chain of handlers,
  106. // if missing then it stops the execution at this handler.
  107. ctx.Next()
  108. }
  109. func profileByUsername(ctx iris.Context) {
  110. // .Params are used to get dynamic path parameters.
  111. username := ctx.Params().Get("username")
  112. ctx.ViewData("Username", username)
  113. // renders "./views/users/profile.html"
  114. // with {{ .Username }} equals to the username dynamic path parameter.
  115. ctx.View("users/profile.html")
  116. }
  117. func getUserByID(ctx iris.Context) {
  118. userID := ctx.Params().Get("id") // Or convert directly using: .Values().GetInt/GetInt64 etc...
  119. // your own db fetch here instead of user :=...
  120. user := User{Username: "username" + userID}
  121. ctx.XML(user)
  122. }
  123. func createUser(ctx iris.Context) {
  124. var user User
  125. err := ctx.ReadForm(&user)
  126. if err != nil {
  127. ctx.Values().Set("error", "creating user, read and parse form failed. "+err.Error())
  128. ctx.StatusCode(iris.StatusInternalServerError)
  129. return
  130. }
  131. // renders "./views/users/create_verification.html"
  132. // with {{ . }} equals to the User object, i.e {{ .Username }} , {{ .Firstname}} etc...
  133. ctx.ViewData("", user)
  134. ctx.View("users/create_verification.html")
  135. }
  136. Listening and gracefully shutdown
  137. You can start the server(s) listening to any type of `net.Listener` or even `http.Server` instance.
  138. The method for initialization of the server should be passed at the end, via `Run` function.
  139. Below you'll see some useful examples:
  140. // Listening on tcp with network address 0.0.0.0:8080
  141. app.Run(iris.Addr(":8080"))
  142. // Same as before but using a custom http.Server which may be in use somewhere else too
  143. app.Run(iris.Server(&http.Server{Addr:":8080"}))
  144. // Using a custom net.Listener
  145. l, err := net.Listen("tcp4", ":8080")
  146. if err != nil {
  147. panic(err)
  148. }
  149. app.Run(iris.Listener(l))
  150. // TLS using files
  151. app.Run(iris.TLS("127.0.0.1:443", "mycert.cert", "mykey.key"))
  152. // Automatic TLS
  153. app.Run(iris.AutoTLS(":443", "example.com", "admin@example.com"))
  154. // UNIX socket
  155. if errOs := os.Remove(socketFile); errOs != nil && !os.IsNotExist(errOs) {
  156. app.Logger().Fatal(errOs)
  157. }
  158. l, err := net.Listen("unix", socketFile)
  159. if err != nil {
  160. app.Logger().Fatal(err)
  161. }
  162. if err = os.Chmod(socketFile, mode); err != nil {
  163. app.Logger().Fatal(err)
  164. }
  165. app.Run(iris.Listener(l))
  166. // Using any func() error,
  167. // the responsibility of starting up a listener is up to you with this way,
  168. // for the sake of simplicity we will use the
  169. // ListenAndServe function of the `net/http` package.
  170. app.Run(iris.Raw(&http.Server{Addr:":8080"}).ListenAndServe)
  171. UNIX and BSD hosts can take advantage of the reuse port feature.
  172. Example code:
  173. package main
  174. import (
  175. // Package tcplisten provides customizable TCP net.Listener with various
  176. // performance-related options:
  177. //
  178. // - SO_REUSEPORT. This option allows linear scaling server performance
  179. // on multi-CPU servers.
  180. // See https://www.nginx.com/blog/socket-sharding-nginx-release-1-9-1/ for details.
  181. //
  182. // - TCP_DEFER_ACCEPT. This option expects the server reads from the accepted
  183. // connection before writing to them.
  184. //
  185. // - TCP_FASTOPEN. See https://lwn.net/Articles/508865/ for details.
  186. "github.com/valyala/tcplisten"
  187. "github.com/kataras/iris"
  188. )
  189. // $ go get github.com/valyala/tcplisten
  190. // $ go run main.go
  191. func main() {
  192. app := iris.New()
  193. app.Get("/", func(ctx iris.Context) {
  194. ctx.HTML("<b>Hello World!</b>")
  195. })
  196. listenerCfg := tcplisten.Config{
  197. ReusePort: true,
  198. DeferAccept: true,
  199. FastOpen: true,
  200. }
  201. l, err := listenerCfg.NewListener("tcp", ":8080")
  202. if err != nil {
  203. panic(err)
  204. }
  205. app.Run(iris.Listener(l))
  206. }
  207. That's all with listening, you have the full control when you need it.
  208. Let's continue by learning how to catch CONTROL+C/COMMAND+C or unix kill command and shutdown the server gracefully.
  209. Gracefully Shutdown on CONTROL+C/COMMAND+C or when kill command sent is ENABLED BY-DEFAULT.
  210. In order to manually manage what to do when app is interrupted,
  211. we have to disable the default behavior with the option `WithoutInterruptHandler`
  212. and register a new interrupt handler (globally, across all possible hosts).
  213. Example code:
  214. package main
  215. import (
  216. stdContext "context"
  217. "time"
  218. "github.com/kataras/iris"
  219. )
  220. func main() {
  221. app := iris.New()
  222. iris.RegisterOnInterrupt(func() {
  223. timeout := 5 * time.Second
  224. ctx, cancel := stdContext.WithTimeout(stdContext.Background(), timeout)
  225. defer cancel()
  226. // close all hosts
  227. app.Shutdown(ctx)
  228. })
  229. app.Get("/", func(ctx iris.Context) {
  230. ctx.HTML(" <h1>hi, I just exist in order to see if the server is closed</h1>")
  231. })
  232. // http://localhost:8080
  233. app.Run(iris.Addr(":8080"), iris.WithoutInterruptHandler)
  234. }
  235. Hosts
  236. Access to all hosts that serve your application can be provided by
  237. the `Application#Hosts` field, after the `Run` method.
  238. But the most common scenario is that you may need access to the host before the `Run` method,
  239. there are two ways of gain access to the host supervisor, read below.
  240. First way is to use the `app.NewHost` to create a new host
  241. and use one of its `Serve` or `Listen` functions
  242. to start the application via the `iris#Raw` Runner.
  243. Note that this way needs an extra import of the `net/http` package.
  244. Example Code:
  245. h := app.NewHost(&http.Server{Addr:":8080"})
  246. h.RegisterOnShutdown(func(){
  247. println("terminate")
  248. })
  249. app.Run(iris.Raw(h.ListenAndServe))
  250. Second, and probably easier way is to use the `host.Configurator`.
  251. Note that this method requires an extra import statement of
  252. "github.com/kataras/iris/core/host" when using go < 1.9,
  253. if you're targeting on go1.9 then you can use the `iris#Supervisor`
  254. and omit the extra host import.
  255. All common `Runners` we saw earlier (`iris#Addr, iris#Listener, iris#Server, iris#TLS, iris#AutoTLS`)
  256. accept a variadic argument of `host.Configurator`, there are just `func(*host.Supervisor)`.
  257. Therefore the `Application` gives you the rights to modify the auto-created host supervisor through these.
  258. Example Code:
  259. package main
  260. import (
  261. stdContext "context"
  262. "time"
  263. "github.com/kataras/iris"
  264. "github.com/kataras/iris/core/host"
  265. )
  266. func main() {
  267. app := iris.New()
  268. app.Get("/", func(ctx iris.Context) {
  269. ctx.HTML("<h1>Hello, try to refresh the page after ~10 secs</h1>")
  270. })
  271. app.Logger().Info("Wait 10 seconds and check your terminal again")
  272. // simulate a shutdown action here...
  273. go func() {
  274. <-time.After(10 * time.Second)
  275. timeout := 5 * time.Second
  276. ctx, cancel := stdContext.WithTimeout(stdContext.Background(), timeout)
  277. defer cancel()
  278. // close all hosts, this will notify the callback we had register
  279. // inside the `configureHost` func.
  280. app.Shutdown(ctx)
  281. }()
  282. // start the server as usual, the only difference is that
  283. // we're adding a second (optional) function
  284. // to configure the just-created host supervisor.
  285. //
  286. // http://localhost:8080
  287. // wait 10 seconds and check your terminal.
  288. app.Run(iris.Addr(":8080", configureHost), iris.WithoutServerError(iris.ErrServerClosed))
  289. }
  290. func configureHost(su *host.Supervisor) {
  291. // here we have full access to the host that will be created
  292. // inside the `Run` function.
  293. //
  294. // we register a shutdown "event" callback
  295. su.RegisterOnShutdown(func() {
  296. println("terminate")
  297. })
  298. // su.RegisterOnError
  299. // su.RegisterOnServe
  300. }
  301. Read more about listening and gracefully shutdown by navigating to:
  302. https://github.com/kataras/iris/tree/master/_examples/#http-listening
  303. Routing
  304. All HTTP methods are supported, developers can also register handlers for same paths for different methods.
  305. The first parameter is the HTTP Method,
  306. second parameter is the request path of the route,
  307. third variadic parameter should contains one or more iris.Handler executed
  308. by the registered order when a user requests for that specific resouce path from the server.
  309. Example code:
  310. app := iris.New()
  311. app.Handle("GET", "/contact", func(ctx iris.Context) {
  312. ctx.HTML("<h1> Hello from /contact </h1>")
  313. })
  314. In order to make things easier for the user, iris provides functions for all HTTP Methods.
  315. The first parameter is the request path of the route,
  316. second variadic parameter should contains one or more iris.Handler executed
  317. by the registered order when a user requests for that specific resouce path from the server.
  318. Example code:
  319. app := iris.New()
  320. // Method: "GET"
  321. app.Get("/", handler)
  322. // Method: "POST"
  323. app.Post("/", handler)
  324. // Method: "PUT"
  325. app.Put("/", handler)
  326. // Method: "DELETE"
  327. app.Delete("/", handler)
  328. // Method: "OPTIONS"
  329. app.Options("/", handler)
  330. // Method: "TRACE"
  331. app.Trace("/", handler)
  332. // Method: "CONNECT"
  333. app.Connect("/", handler)
  334. // Method: "HEAD"
  335. app.Head("/", handler)
  336. // Method: "PATCH"
  337. app.Patch("/", handler)
  338. // register the route for all HTTP Methods
  339. app.Any("/", handler)
  340. func handler(ctx iris.Context){
  341. ctx.Writef("Hello from method: %s and path: %s", ctx.Method(), ctx.Path())
  342. }
  343. Grouping Routes
  344. A set of routes that are being groupped by path prefix can (optionally) share the same middleware handlers and template layout.
  345. A group can have a nested group too.
  346. `.Party` is being used to group routes, developers can declare an unlimited number of (nested) groups.
  347. Example code:
  348. users := app.Party("/users", myAuthMiddlewareHandler)
  349. // http://myhost.com/users/42/profile
  350. users.Get("/{id:int}/profile", userProfileHandler)
  351. // http://myhost.com/users/messages/1
  352. users.Get("/inbox/{id:int}", userMessageHandler)
  353. Custom HTTP Errors
  354. iris developers are able to register their own handlers for http statuses like 404 not found, 500 internal server error and so on.
  355. Example code:
  356. // when 404 then render the template $templatedir/errors/404.html
  357. app.OnErrorCode(iris.StatusNotFound, func(ctx iris.Context){
  358. ctx.View("errors/404.html")
  359. })
  360. app.OnErrorCode(500, func(ctx iris.Context){
  361. // ...
  362. })
  363. Basic HTTP API
  364. With the help of iris's expressionist router you can build any form of API you desire, with
  365. safety.
  366. Example code:
  367. package main
  368. import "github.com/kataras/iris"
  369. func main() {
  370. app := iris.New()
  371. // registers a custom handler for 404 not found http (error) status code,
  372. // fires when route not found or manually by ctx.StatusCode(iris.StatusNotFound).
  373. app.OnErrorCode(iris.StatusNotFound, notFoundHandler)
  374. // GET -> HTTP Method
  375. // / -> Path
  376. // func(ctx iris.Context) -> The route's handler.
  377. //
  378. // Third receiver should contains the route's handler(s), they are executed by order.
  379. app.Handle("GET", "/", func(ctx iris.Context) {
  380. // navigate to the middle of $GOPATH/src/github.com/kataras/iris/context/context.go
  381. // to overview all context's method (there a lot of them, read that and you will learn how iris works too)
  382. ctx.HTML("Hello from " + ctx.Path()) // Hello from /
  383. })
  384. app.Get("/home", func(ctx iris.Context) {
  385. ctx.Writef(`Same as app.Handle("GET", "/", [...])`)
  386. })
  387. app.Get("/donate", donateHandler, donateFinishHandler)
  388. // Pssst, don't forget dynamic-path example for more "magic"!
  389. app.Get("/api/users/{userid:int min(1)}", func(ctx iris.Context) {
  390. userID, err := ctx.Params().GetInt("userid")
  391. if err != nil {
  392. ctx.Writef("error while trying to parse userid parameter," +
  393. "this will never happen if :int is being used because if it's not integer it will fire Not Found automatically.")
  394. ctx.StatusCode(iris.StatusBadRequest)
  395. return
  396. }
  397. ctx.JSON(map[string]interface{}{
  398. // you can pass any custom structured go value of course.
  399. "user_id": userID,
  400. })
  401. })
  402. // app.Post("/", func(ctx iris.Context){}) -> for POST http method.
  403. // app.Put("/", func(ctx iris.Context){})-> for "PUT" http method.
  404. // app.Delete("/", func(ctx iris.Context){})-> for "DELETE" http method.
  405. // app.Options("/", func(ctx iris.Context){})-> for "OPTIONS" http method.
  406. // app.Trace("/", func(ctx iris.Context){})-> for "TRACE" http method.
  407. // app.Head("/", func(ctx iris.Context){})-> for "HEAD" http method.
  408. // app.Connect("/", func(ctx iris.Context){})-> for "CONNECT" http method.
  409. // app.Patch("/", func(ctx iris.Context){})-> for "PATCH" http method.
  410. // app.Any("/", func(ctx iris.Context){}) for all http methods.
  411. // More than one route can contain the same path with a different http mapped method.
  412. // You can catch any route creation errors with:
  413. // route, err := app.Get(...)
  414. // set a name to a route: route.Name = "myroute"
  415. // You can also group routes by path prefix, sharing middleware(s) and done handlers.
  416. adminRoutes := app.Party("/admin", adminMiddleware)
  417. adminRoutes.Done(func(ctx iris.Context) { // executes always last if ctx.Next()
  418. ctx.Application().Logger().Infof("response sent to " + ctx.Path())
  419. })
  420. // adminRoutes.Layout("/views/layouts/admin.html") // set a view layout for these routes, see more at view examples.
  421. // GET: http://localhost:8080/admin
  422. adminRoutes.Get("/", func(ctx iris.Context) {
  423. // [...]
  424. ctx.StatusCode(iris.StatusOK) // default is 200 == iris.StatusOK
  425. ctx.HTML("<h1>Hello from admin/</h1>")
  426. ctx.Next() // in order to execute the party's "Done" Handler(s)
  427. })
  428. // GET: http://localhost:8080/admin/login
  429. adminRoutes.Get("/login", func(ctx iris.Context) {
  430. // [...]
  431. })
  432. // POST: http://localhost:8080/admin/login
  433. adminRoutes.Post("/login", func(ctx iris.Context) {
  434. // [...]
  435. })
  436. // subdomains, easier than ever, should add localhost or 127.0.0.1 into your hosts file,
  437. // etc/hosts on unix or C:/windows/system32/drivers/etc/hosts on windows.
  438. v1 := app.Party("v1.")
  439. { // braces are optional, it's just type of style, to group the routes visually.
  440. // http://v1.localhost:8080
  441. v1.Get("/", func(ctx iris.Context) {
  442. ctx.HTML("Version 1 API. go to <a href='" + ctx.Path() + "/api" + "'>/api/users</a>")
  443. })
  444. usersAPI := v1.Party("/api/users")
  445. {
  446. // http://v1.localhost:8080/api/users
  447. usersAPI.Get("/", func(ctx iris.Context) {
  448. ctx.Writef("All users")
  449. })
  450. // http://v1.localhost:8080/api/users/42
  451. usersAPI.Get("/{userid:int}", func(ctx iris.Context) {
  452. ctx.Writef("user with id: %s", ctx.Params().Get("userid"))
  453. })
  454. }
  455. }
  456. // wildcard subdomains.
  457. wildcardSubdomain := app.Party("*.")
  458. {
  459. wildcardSubdomain.Get("/", func(ctx iris.Context) {
  460. ctx.Writef("Subdomain can be anything, now you're here from: %s", ctx.Subdomain())
  461. })
  462. }
  463. // http://localhost:8080
  464. // http://localhost:8080/home
  465. // http://localhost:8080/donate
  466. // http://localhost:8080/api/users/42
  467. // http://localhost:8080/admin
  468. // http://localhost:8080/admin/login
  469. //
  470. // http://localhost:8080/api/users/0
  471. // http://localhost:8080/api/users/blabla
  472. // http://localhost:8080/wontfound
  473. //
  474. // if hosts edited:
  475. // http://v1.localhost:8080
  476. // http://v1.localhost:8080/api/users
  477. // http://v1.localhost:8080/api/users/42
  478. // http://anything.localhost:8080
  479. app.Run(iris.Addr(":8080"))
  480. }
  481. func adminMiddleware(ctx iris.Context) {
  482. // [...]
  483. ctx.Next() // to move to the next handler, or don't that if you have any auth logic.
  484. }
  485. func donateHandler(ctx iris.Context) {
  486. ctx.Writef("Just like an inline handler, but it can be " +
  487. "used by other package, anywhere in your project.")
  488. // let's pass a value to the next handler
  489. // Values is the way handlers(or middleware) are communicating between each other.
  490. ctx.Values().Set("donate_url", "https://github.com/kataras/iris#-people")
  491. ctx.Next() // in order to execute the next handler in the chain, look donate route.
  492. }
  493. func donateFinishHandler(ctx iris.Context) {
  494. // values can be any type of object so we could cast the value to a string
  495. // but iris provides an easy to do that, if donate_url is not defined, then it returns an empty string instead.
  496. donateURL := ctx.Values().GetString("donate_url")
  497. ctx.Application().Logger().Infof("donate_url value was: " + donateURL)
  498. ctx.Writef("\n\nDonate sent(?).")
  499. }
  500. func notFoundHandler(ctx iris.Context) {
  501. ctx.HTML("Custom route for 404 not found http code, here you can render a view, html, json <b>any valid response</b>.")
  502. }
  503. Parameterized Path
  504. At the previous example,
  505. we've seen static routes, group of routes, subdomains, wildcard subdomains, a small example of parameterized path
  506. with a single known parameter and custom http errors, now it's time to see wildcard parameters and macros.
  507. iris, like net/http std package registers route's handlers
  508. by a Handler, the iris' type of handler is just a func(ctx iris.Context)
  509. where context comes from github.com/kataras/iris/context.
  510. Iris has the easiest and the most powerful routing process you have ever meet.
  511. At the same time,
  512. iris has its own interpeter(yes like a programming language)
  513. for route's path syntax and their dynamic path parameters parsing and evaluation,
  514. We call them "macros" for shortcut.
  515. How? It calculates its needs and if not any special regexp needed then it just
  516. registers the route with the low-level path syntax,
  517. otherwise it pre-compiles the regexp and adds the necessary middleware(s).
  518. Standard macro types for parameters:
  519. +------------------------+
  520. | {param:string} |
  521. +------------------------+
  522. string type
  523. anything
  524. +------------------------+
  525. | {param:int} |
  526. +------------------------+
  527. int type
  528. only numbers (0-9)
  529. +------------------------+
  530. | {param:long} |
  531. +------------------------+
  532. int64 type
  533. only numbers (0-9)
  534. +------------------------+
  535. | {param:boolean} |
  536. +------------------------+
  537. bool type
  538. only "1" or "t" or "T" or "TRUE" or "true" or "True"
  539. or "0" or "f" or "F" or "FALSE" or "false" or "False"
  540. +------------------------+
  541. | {param:alphabetical} |
  542. +------------------------+
  543. alphabetical/letter type
  544. letters only (upper or lowercase)
  545. +------------------------+
  546. | {param:file} |
  547. +------------------------+
  548. file type
  549. letters (upper or lowercase)
  550. numbers (0-9)
  551. underscore (_)
  552. dash (-)
  553. point (.)
  554. no spaces ! or other character
  555. +------------------------+
  556. | {param:path} |
  557. +------------------------+
  558. path type
  559. anything, should be the last part, more than one path segment,
  560. i.e: /path1/path2/path3 , ctx.Params().Get("param") == "/path1/path2/path3"
  561. if type is missing then parameter's type is defaulted to string, so
  562. {param} == {param:string}.
  563. If a function not found on that type then the "string"'s types functions are being used.
  564. i.e:
  565. {param:int min(3)}
  566. Besides the fact that iris provides the basic types and some default "macro funcs"
  567. you are able to register your own too!.
  568. Register a named path parameter function:
  569. app.Macros().Int.RegisterFunc("min", func(argument int) func(paramValue string) bool {
  570. [...]
  571. return true/false -> true means valid.
  572. })
  573. at the func(argument ...) you can have any standard type, it will be validated before the server starts
  574. so don't care about performance here, the only thing it runs at serve time is the returning func(paramValue string) bool.
  575. {param:string equal(iris)} , "iris" will be the argument here:
  576. app.Macros().String.RegisterFunc("equal", func(argument string) func(paramValue string) bool {
  577. return func(paramValue string){ return argument == paramValue }
  578. })
  579. Example Code:
  580. // you can use the "string" type which is valid for a single path parameter that can be anything.
  581. app.Get("/username/{name}", func(ctx iris.Context) {
  582. ctx.Writef("Hello %s", ctx.Params().Get("name"))
  583. }) // type is missing = {name:string}
  584. // Let's register our first macro attached to int macro type.
  585. // "min" = the function
  586. // "minValue" = the argument of the function
  587. // func(string) bool = the macro's path parameter evaluator, this executes in serve time when
  588. // a user requests a path which contains the :int macro type with the min(...) macro parameter function.
  589. app.Macros().Int.RegisterFunc("min", func(minValue int) func(string) bool {
  590. // do anything before serve here [...]
  591. // at this case we don't need to do anything
  592. return func(paramValue string) bool {
  593. n, err := strconv.Atoi(paramValue)
  594. if err != nil {
  595. return false
  596. }
  597. return n >= minValue
  598. }
  599. })
  600. // http://localhost:8080/profile/id>=1
  601. // this will throw 404 even if it's found as route on : /profile/0, /profile/blabla, /profile/-1
  602. // macro parameter functions are optional of course.
  603. app.Get("/profile/{id:int min(1)}", func(ctx iris.Context) {
  604. // second parameter is the error but it will always nil because we use macros,
  605. // the validaton already happened.
  606. id, _ := ctx.Params().GetInt("id")
  607. ctx.Writef("Hello id: %d", id)
  608. })
  609. // to change the error code per route's macro evaluator:
  610. app.Get("/profile/{id:int min(1)}/friends/{friendid:int min(1) else 504}", func(ctx iris.Context) {
  611. id, _ := ctx.Params().GetInt("id")
  612. friendid, _ := ctx.Params().GetInt("friendid")
  613. ctx.Writef("Hello id: %d looking for friend id: ", id, friendid)
  614. }) // this will throw e 504 error code instead of 404 if all route's macros not passed.
  615. // http://localhost:8080/game/a-zA-Z/level/0-9
  616. // remember, alphabetical is lowercase or uppercase letters only.
  617. app.Get("/game/{name:alphabetical}/level/{level:int}", func(ctx iris.Context) {
  618. ctx.Writef("name: %s | level: %s", ctx.Params().Get("name"), ctx.Params().Get("level"))
  619. })
  620. // let's use a trivial custom regexp that validates a single path parameter
  621. // which its value is only lowercase letters.
  622. // http://localhost:8080/lowercase/anylowercase
  623. app.Get("/lowercase/{name:string regexp(^[a-z]+)}", func(ctx iris.Context) {
  624. ctx.Writef("name should be only lowercase, otherwise this handler will never executed: %s", ctx.Params().Get("name"))
  625. })
  626. // http://localhost:8080/single_file/app.js
  627. app.Get("/single_file/{myfile:file}", func(ctx iris.Context) {
  628. ctx.Writef("file type validates if the parameter value has a form of a file name, got: %s", ctx.Params().Get("myfile"))
  629. })
  630. // http://localhost:8080/myfiles/any/directory/here/
  631. // this is the only macro type that accepts any number of path segments.
  632. app.Get("/myfiles/{directory:path}", func(ctx iris.Context) {
  633. ctx.Writef("path type accepts any number of path segments, path after /myfiles/ is: %s", ctx.Params().Get("directory"))
  634. })
  635. app.Run(iris.Addr(":8080"))
  636. }
  637. A path parameter name should contain only alphabetical letters, symbols, containing '_' and numbers are NOT allowed.
  638. If route failed to be registered, the app will panic without any warnings
  639. if you didn't catch the second return value(error) on .Handle/.Get....
  640. Last, do not confuse ctx.Values() with ctx.Params().
  641. Path parameter's values goes to ctx.Params() and context's local storage
  642. that can be used to communicate between handlers and middleware(s) goes to
  643. ctx.Values(), path parameters and the rest of any custom values are separated for your own good.
  644. Run
  645. $ go run main.go
  646. Static Files
  647. // StaticServe serves a directory as web resource
  648. // it's the simpliest form of the Static* functions
  649. // Almost same usage as StaticWeb
  650. // accepts only one required parameter which is the systemPath,
  651. // the same path will be used to register the GET and HEAD method routes.
  652. // If second parameter is empty, otherwise the requestPath is the second parameter
  653. // it uses gzip compression (compression on each request, no file cache).
  654. //
  655. // Returns the GET *Route.
  656. StaticServe(systemPath string, requestPath ...string) (*Route, error)
  657. // StaticContent registers a GET and HEAD method routes to the requestPath
  658. // that are ready to serve raw static bytes, memory cached.
  659. //
  660. // Returns the GET *Route.
  661. StaticContent(reqPath string, cType string, content []byte) (*Route, error)
  662. // StaticEmbedded used when files are distributed inside the app executable, using go-bindata mostly
  663. // First parameter is the request path, the path which the files in the vdir will be served to, for example "/static"
  664. // Second parameter is the (virtual) directory path, for example "./assets"
  665. // Third parameter is the Asset function
  666. // Forth parameter is the AssetNames function.
  667. //
  668. // Returns the GET *Route.
  669. //
  670. // Example: https://github.com/kataras/iris/tree/master/_examples/file-server/embedding-files-into-app
  671. StaticEmbedded(requestPath string, vdir string, assetFn func(name string) ([]byte, error), namesFn func() []string) (*Route, error)
  672. // Favicon serves static favicon
  673. // accepts 2 parameters, second is optional
  674. // favPath (string), declare the system directory path of the __.ico
  675. // requestPath (string), it's the route's path, by default this is the "/favicon.ico" because some browsers tries to get this by default first,
  676. // you can declare your own path if you have more than one favicon (desktop, mobile and so on)
  677. //
  678. // this func will add a route for you which will static serve the /yuorpath/yourfile.ico to the /yourfile.ico
  679. // (nothing special that you can't handle by yourself).
  680. // Note that you have to call it on every favicon you have to serve automatically (desktop, mobile and so on).
  681. //
  682. // Returns the GET *Route.
  683. Favicon(favPath string, requestPath ...string) (*Route, error)
  684. // StaticWeb returns a handler that serves HTTP requests
  685. // with the contents of the file system rooted at directory.
  686. //
  687. // first parameter: the route path
  688. // second parameter: the system directory
  689. // third OPTIONAL parameter: the exception routes
  690. // (= give priority to these routes instead of the static handler)
  691. // for more options look app.StaticHandler.
  692. //
  693. // app.StaticWeb("/static", "./static")
  694. //
  695. // As a special case, the returned file server redirects any request
  696. // ending in "/index.html" to the same path, without the final
  697. // "index.html".
  698. //
  699. // StaticWeb calls the StaticHandler(systemPath, listingDirectories: false, gzip: false ).
  700. //
  701. // Returns the GET *Route.
  702. StaticWeb(requestPath string, systemPath string, exceptRoutes ...*Route) (*Route, error)
  703. Example code:
  704. package main
  705. import "github.com/kataras/iris"
  706. func main() {
  707. app := iris.New()
  708. // This will serve the ./static/favicons/ion_32_32.ico to: localhost:8080/favicon.ico
  709. app.Favicon("./static/favicons/ion_32_32.ico")
  710. // app.Favicon("./static/favicons/ion_32_32.ico", "/favicon_48_48.ico")
  711. // This will serve the ./static/favicons/ion_32_32.ico to: localhost:8080/favicon_48_48.ico
  712. app.Get("/", func(ctx iris.Context) {
  713. ctx.HTML(`<a href="/favicon.ico"> press here to see the favicon.ico</a>.
  714. At some browsers like chrome, it should be visible at the top-left side of the browser's window,
  715. because some browsers make requests to the /favicon.ico automatically,
  716. so iris serves your favicon in that path too (you can change it).`)
  717. }) // if favicon doesn't show to you, try to clear your browser's cache.
  718. app.Run(iris.Addr(":8080"))
  719. }
  720. More examples can be found here: https://github.com/kataras/iris/tree/master/_examples/beginner/file-server
  721. Middleware Ecosystem
  722. Middleware is just a concept of ordered chain of handlers.
  723. Middleware can be registered globally, per-party, per-subdomain and per-route.
  724. Example code:
  725. // globally
  726. // before any routes, appends the middleware to all routes
  727. app.Use(func(ctx iris.Context){
  728. // ... any code here
  729. ctx.Next() // in order to continue to the next handler,
  730. // if that is missing then the next in chain handlers will be not executed,
  731. // useful for authentication middleware
  732. })
  733. // globally
  734. // after or before any routes, prepends the middleware to all routes
  735. app.UseGlobal(handler1, handler2, handler3)
  736. // per-route
  737. app.Post("/login", authenticationHandler, loginPageHandler)
  738. // per-party(group of routes)
  739. users := app.Party("/users", usersMiddleware)
  740. users.Get("/", usersIndex)
  741. // per-subdomain
  742. mysubdomain := app.Party("mysubdomain.", firstMiddleware)
  743. mysubdomain.Use(secondMiddleware)
  744. mysubdomain.Get("/", mysubdomainIndex)
  745. // per wildcard, dynamic subdomain
  746. dynamicSub := app.Party(".*", firstMiddleware, secondMiddleware)
  747. dynamicSub.Get("/", func(ctx iris.Context){
  748. ctx.Writef("Hello from subdomain: "+ ctx.Subdomain())
  749. })
  750. iris is able to wrap and convert any external, third-party Handler you used to use to your web application.
  751. Let's convert the https://github.com/rs/cors net/http external middleware which returns a `next form` handler.
  752. Example code:
  753. package main
  754. import (
  755. "github.com/rs/cors"
  756. "github.com/kataras/iris"
  757. )
  758. func main() {
  759. app := iris.New()
  760. corsOptions := cors.Options{
  761. AllowedOrigins: []string{"*"},
  762. AllowCredentials: true,
  763. }
  764. corsWrapper := cors.New(corsOptions).ServeHTTP
  765. app.WrapRouter(corsWrapper)
  766. v1 := app.Party("/api/v1")
  767. {
  768. v1.Get("/", h)
  769. v1.Put("/put", h)
  770. v1.Post("/post", h)
  771. }
  772. app.Run(iris.Addr(":8080"))
  773. }
  774. func h(ctx iris.Context) {
  775. ctx.Application().Logger().Infof(ctx.Path())
  776. ctx.Writef("Hello from %s", ctx.Path())
  777. }
  778. View Engine
  779. Iris supports 5 template engines out-of-the-box, developers can still use any external golang template engine,
  780. as `context/context#ResponseWriter()` is an `io.Writer`.
  781. All of these five template engines have common features with common API,
  782. like Layout, Template Funcs, Party-specific layout, partial rendering and more.
  783. The standard html,
  784. its template parser is the golang.org/pkg/html/template/
  785. Django,
  786. its template parser is the github.com/flosch/pongo2
  787. Pug(Jade),
  788. its template parser is the github.com/Joker/jade
  789. Handlebars,
  790. its template parser is the github.com/aymerick/raymond
  791. Amber,
  792. its template parser is the github.com/eknkc/amber
  793. Example code:
  794. package main
  795. import "github.com/kataras/iris"
  796. func main() {
  797. app := iris.New()
  798. // - standard html | iris.HTML(...)
  799. // - django | iris.Django(...)
  800. // - pug(jade) | iris.Pug(...)
  801. // - handlebars | iris.Handlebars(...)
  802. // - amber | iris.Amber(...)
  803. tmpl := iris.HTML("./templates", ".html")
  804. tmpl.Reload(true) // reload templates on each request (development mode)
  805. // default template funcs are:
  806. //
  807. // - {{ urlpath "mynamedroute" "pathParameter_ifneeded" }}
  808. // - {{ render "header.html" }}
  809. // - {{ render_r "header.html" }} // partial relative path to current page
  810. // - {{ yield }}
  811. // - {{ current }}
  812. // register a custom template func.
  813. tmpl.AddFunc("greet", func(s string) string {
  814. return "Greetings " + s + "!"
  815. })
  816. // register the view engine to the views, this will load the templates.
  817. app.RegisterView(tmpl)
  818. app.Get("/", hi)
  819. // http://localhost:8080
  820. app.Run(iris.Addr(":8080"), iris.WithCharset("UTF-8")) // defaults to that but you can change it.
  821. }
  822. func hi(ctx iris.Context) {
  823. ctx.ViewData("Title", "Hi Page")
  824. ctx.ViewData("Name", "iris") // {{.Name}} will render: iris
  825. // ctx.ViewData("", myCcustomStruct{})
  826. ctx.View("hi.html")
  827. }
  828. View engine supports bundled(https://github.com/shuLhan/go-bindata) template files too.
  829. go-bindata gives you two functions, asset and assetNames,
  830. these can be setted to each of the template engines using the `.Binary` func.
  831. Example code:
  832. package main
  833. import "github.com/kataras/iris"
  834. func main() {
  835. app := iris.New()
  836. // $ go get -u github.com/shuLhan/go-bindata/...
  837. // $ go-bindata ./templates/...
  838. // $ go build
  839. // $ ./embedding-templates-into-app
  840. // html files are not used, you can delete the folder and run the example
  841. app.RegisterView(iris.HTML("./templates", ".html").Binary(Asset, AssetNames))
  842. app.Get("/", hi)
  843. // http://localhost:8080
  844. app.Run(iris.Addr(":8080"))
  845. }
  846. type page struct {
  847. Title, Name string
  848. }
  849. func hi(ctx iris.Context) {
  850. ctx.ViewData("", page{Title: "Hi Page", Name: "iris"})
  851. ctx.View("hi.html")
  852. }
  853. A real example can be found here: https://github.com/kataras/iris/tree/master/_examples/view/embedding-templates-into-app.
  854. Enable auto-reloading of templates on each request. Useful while developers are in dev mode
  855. as they no neeed to restart their app on every template edit.
  856. Example code:
  857. pugEngine := iris.Pug("./templates", ".jade")
  858. pugEngine.Reload(true) // <--- set to true to re-build the templates on each request.
  859. app.RegisterView(pugEngine)
  860. Note:
  861. In case you're wondering, the code behind the view engines derives from the "github.com/kataras/iris/view" package,
  862. access to the engines' variables can be granded by "github.com/kataras/iris" package too.
  863. iris.HTML(...) is a shortcut of view.HTML(...)
  864. iris.Django(...) >> >> view.Django(...)
  865. iris.Pug(...) >> >> view.Pug(...)
  866. iris.Handlebars(...) >> >> view.Handlebars(...)
  867. iris.Amber(...) >> >> view.Amber(...)
  868. Each one of these template engines has different options located here: https://github.com/kataras/iris/tree/master/view .
  869. Sessions
  870. This example will show how to store and access data from a session.
  871. You don’t need any third-party library,
  872. but If you want you can use any session manager compatible or not.
  873. In this example we will only allow authenticated users to view our secret message on the /secret page.
  874. To get access to it, the will first have to visit /login to get a valid session cookie,
  875. which logs him in. Additionally he can visit /logout to revoke his access to our secret message.
  876. Example code:
  877. // main.go
  878. package main
  879. import (
  880. "github.com/kataras/iris"
  881. "github.com/kataras/iris/sessions"
  882. )
  883. var (
  884. cookieNameForSessionID = "mycookiesessionnameid"
  885. sess = sessions.New(sessions.Config{Cookie: cookieNameForSessionID})
  886. )
  887. func secret(ctx iris.Context) {
  888. // Check if user is authenticated
  889. if auth, _ := sess.Start(ctx).GetBoolean("authenticated"); !auth {
  890. ctx.StatusCode(iris.StatusForbidden)
  891. return
  892. }
  893. // Print secret message
  894. ctx.WriteString("The cake is a lie!")
  895. }
  896. func login(ctx iris.Context) {
  897. session := sess.Start(ctx)
  898. // Authentication goes here
  899. // ...
  900. // Set user as authenticated
  901. session.Set("authenticated", true)
  902. }
  903. func logout(ctx iris.Context) {
  904. session := sess.Start(ctx)
  905. // Revoke users authentication
  906. session.Set("authenticated", false)
  907. }
  908. func main() {
  909. app := iris.New()
  910. app.Get("/secret", secret)
  911. app.Get("/login", login)
  912. app.Get("/logout", logout)
  913. app.Run(iris.Addr(":8080"))
  914. }
  915. Running the example:
  916. $ go get github.com/kataras/iris/sessions
  917. $ go run main.go
  918. $ curl -s http://localhost:8080/secret
  919. Forbidden
  920. $ curl -s -I http://localhost:8080/login
  921. Set-Cookie: mycookiesessionnameid=MTQ4NzE5Mz...
  922. $ curl -s --cookie "mycookiesessionnameid=MTQ4NzE5Mz..." http://localhost:8080/secret
  923. The cake is a lie!
  924. Sessions persistence can be achieved using one (or more) `sessiondb`.
  925. Example Code:
  926. package main
  927. import (
  928. "time"
  929. "github.com/kataras/iris"
  930. "github.com/kataras/iris/sessions"
  931. "github.com/kataras/iris/sessions/sessiondb/redis"
  932. "github.com/kataras/iris/sessions/sessiondb/redis/service"
  933. )
  934. // tested with redis version 3.0.503.
  935. // for windows see: https://github.com/ServiceStack/redis-windows
  936. func main() {
  937. // replace with your running redis' server settings:
  938. db := redis.New(service.Config{
  939. Network: service.DefaultRedisNetwork,
  940. Addr: service.DefaultRedisAddr,
  941. Password: "",
  942. Database: "",
  943. MaxIdle: 0,
  944. MaxActive: 0,
  945. IdleTimeout: service.DefaultRedisIdleTimeout,
  946. Prefix: ""}) // optionally configure the bridge between your redis server
  947. // close connection when control+C/cmd+C
  948. iris.RegisterOnInterrupt(func() {
  949. db.Close()
  950. })
  951. defer db.Close() // close the database connection if application errored.
  952. sess := sessions.New(sessions.Config{
  953. Cookie: "sessionscookieid",
  954. Expires: 45 * time.Minute}, // <=0 means unlimited life. Defaults to 0.
  955. )
  956. //
  957. // IMPORTANT:
  958. //
  959. sess.UseDatabase(db)
  960. // the rest of the code stays the same.
  961. app := iris.New()
  962. app.Get("/", func(ctx iris.Context) {
  963. ctx.Writef("You should navigate to the /set, /get, /delete, /clear,/destroy instead")
  964. })
  965. app.Get("/set", func(ctx iris.Context) {
  966. s := sess.Start(ctx)
  967. //set session values
  968. s.Set("name", "iris")
  969. //test if setted here
  970. ctx.Writef("All ok session value of the 'name' is: %s", s.GetString("name"))
  971. })
  972. app.Get("/set/{key}/{value}", func(ctx iris.Context) {
  973. key, value := ctx.Params().Get("key"), ctx.Params().Get("value")
  974. s := sess.Start(ctx)
  975. // set session values
  976. s.Set(key, value)
  977. // test if setted here
  978. ctx.Writef("All ok session value of the '%s' is: %s", key, s.GetString(key))
  979. })
  980. app.Get("/get", func(ctx iris.Context) {
  981. // get a specific key, as string, if no found returns just an empty string
  982. name := sess.Start(ctx).GetString("name")
  983. ctx.Writef("The 'name' on the /set was: %s", name)
  984. })
  985. app.Get("/get/{key}", func(ctx iris.Context) {
  986. // get a specific key, as string, if no found returns just an empty string
  987. name := sess.Start(ctx).GetString(ctx.Params().Get("key"))
  988. ctx.Writef("The name on the /set was: %s", name)
  989. })
  990. app.Get("/delete", func(ctx iris.Context) {
  991. // delete a specific key
  992. sess.Start(ctx).Delete("name")
  993. })
  994. app.Get("/clear", func(ctx iris.Context) {
  995. // removes all entries
  996. sess.Start(ctx).Clear()
  997. })
  998. app.Get("/destroy", func(ctx iris.Context) {
  999. //destroy, removes the entire session data and cookie
  1000. sess.Destroy(ctx)
  1001. })
  1002. app.Get("/update", func(ctx iris.Context) {
  1003. // updates expire date with a new date
  1004. sess.ShiftExpiration(ctx)
  1005. })
  1006. app.Run(iris.Addr(":8080"), iris.WithoutServerError(iris.ErrServerClosed))
  1007. }
  1008. More examples:
  1009. https://github.com/kataras/iris/tree/master/_examples/sessions
  1010. Websockets
  1011. In this example we will create a small chat between web sockets via browser.
  1012. Example Server Code:
  1013. // main.go
  1014. package main
  1015. import (
  1016. "fmt"
  1017. "github.com/kataras/iris"
  1018. "github.com/kataras/iris/websocket"
  1019. )
  1020. func main() {
  1021. app := iris.New()
  1022. app.Get("/", func(ctx iris.Context) {
  1023. ctx.ServeFile("websockets.html", false) // second parameter: enable gzip?
  1024. })
  1025. setupWebsocket(app)
  1026. // x2
  1027. // http://localhost:8080
  1028. // http://localhost:8080
  1029. // write something, press submit, see the result.
  1030. app.Run(iris.Addr(":8080"))
  1031. }
  1032. func setupWebsocket(app *iris.Application) {
  1033. // create our echo websocket server
  1034. ws := websocket.New(websocket.Config{
  1035. ReadBufferSize: 1024,
  1036. WriteBufferSize: 1024,
  1037. })
  1038. ws.OnConnection(handleConnection)
  1039. // register the server on an endpoint.
  1040. // see the inline javascript code i the websockets.html, this endpoint is used to connect to the server.
  1041. app.Get("/echo", ws.Handler())
  1042. // serve the javascript built'n client-side library,
  1043. // see websockets.html script tags, this path is used.
  1044. app.Any("/iris-ws.js", func(ctx iris.Context) {
  1045. ctx.Write(websocket.ClientSource)
  1046. })
  1047. }
  1048. func handleConnection(c websocket.Connection) {
  1049. // Read events from browser
  1050. c.On("chat", func(msg string) {
  1051. // Print the message to the console, c.Context() is the iris's http context.
  1052. fmt.Printf("%s sent: %s\n", c.Context().RemoteAddr(), msg)
  1053. // Write message back to the client message owner:
  1054. // c.Emit("chat", msg)
  1055. c.To(websocket.Broadcast).Emit("chat", msg)
  1056. })
  1057. }
  1058. Example Client(javascript) Code:
  1059. <!-- websockets.html -->
  1060. <input id="input" type="text" />
  1061. <button onclick="send()">Send</button>
  1062. <pre id="output"></pre>
  1063. <script src="/iris-ws.js"></script>
  1064. <script>
  1065. var input = document.getElementById("input");
  1066. var output = document.getElementById("output");
  1067. // Ws comes from the auto-served '/iris-ws.js'
  1068. var socket = new Ws("ws://localhost:8080/echo");
  1069. socket.OnConnect(function () {
  1070. output.innerHTML += "Status: Connected\n";
  1071. });
  1072. socket.OnDisconnect(function () {
  1073. output.innerHTML += "Status: Disconnected\n";
  1074. });
  1075. // read events from the server
  1076. socket.On("chat", function (msg) {
  1077. addMessage(msg)
  1078. });
  1079. function send() {
  1080. addMessage("Me: "+input.value) // write ourselves
  1081. socket.Emit("chat", input.value);// send chat event data to the websocket server
  1082. input.value = ""; // clear the input
  1083. }
  1084. function addMessage(msg) {
  1085. output.innerHTML += msg + "\n";
  1086. }
  1087. </script>
  1088. Running the example:
  1089. $ go get github.com/kataras/iris/websocket
  1090. $ go run main.go
  1091. $ start http://localhost:8080
  1092. MVC - Model View Controller
  1093. Iris has first-class support for the MVC pattern, you'll not find
  1094. these stuff anywhere else in the Go world.
  1095. Example Code:
  1096. package main
  1097. import (
  1098. "github.com/kataras/iris"
  1099. "github.com/kataras/iris/mvc"
  1100. "github.com/kataras/iris/middleware/logger"
  1101. "github.com/kataras/iris/middleware/recover"
  1102. )
  1103. func main() {
  1104. app := iris.New()
  1105. // Optionally, add two built'n handlers
  1106. // that can recover from any http-relative panics
  1107. // and log the requests to the terminal.
  1108. app.Use(recover.New())
  1109. app.Use(logger.New())
  1110. // Serve a controller based on the root Router, "/".
  1111. mvc.New(app).Handle(new(ExampleController))
  1112. // http://localhost:8080
  1113. // http://localhost:8080/ping
  1114. // http://localhost:8080/hello
  1115. // http://localhost:8080/custom_path
  1116. app.Run(iris.Addr(":8080"))
  1117. }
  1118. // ExampleController serves the "/", "/ping" and "/hello".
  1119. type ExampleController struct{}
  1120. // Get serves
  1121. // Method: GET
  1122. // Resource: http://localhost:8080
  1123. func (c *ExampleController) Get() mvc.Result {
  1124. return mvc.Response{
  1125. ContentType: "text/html",
  1126. Text: "<h1>Welcome</h1>",
  1127. }
  1128. }
  1129. // GetPing serves
  1130. // Method: GET
  1131. // Resource: http://localhost:8080/ping
  1132. func (c *ExampleController) GetPing() string {
  1133. return "pong"
  1134. }
  1135. // GetHello serves
  1136. // Method: GET
  1137. // Resource: http://localhost:8080/hello
  1138. func (c *ExampleController) GetHello() interface{} {
  1139. return map[string]string{"message": "Hello Iris!"}
  1140. }
  1141. // GetUserBy serves
  1142. // Method: GET
  1143. // Resource: http://localhost:8080/user/{username:string}
  1144. // By is a reserved "keyword" to tell the framework that you're going to
  1145. // bind path parameters in the function's input arguments, and it also
  1146. // helps to have "Get" and "GetBy" in the same controller.
  1147. //
  1148. // func (c *ExampleController) GetUserBy(username string) mvc.Result {
  1149. // return mvc.View{
  1150. // Name: "user/username.html",
  1151. // Data: username,
  1152. // }
  1153. // }
  1154. Can use more than one, the factory will make sure
  1155. that the correct http methods are being registered for each route
  1156. for this controller, uncomment these if you want:
  1157. func (c *ExampleController) Post() {}
  1158. func (c *ExampleController) Put() {}
  1159. func (c *ExampleController) Delete() {}
  1160. func (c *ExampleController) Connect() {}
  1161. func (c *ExampleController) Head() {}
  1162. func (c *ExampleController) Patch() {}
  1163. func (c *ExampleController) Options() {}
  1164. func (c *ExampleController) Trace() {}
  1165. */
  1166. //
  1167. /*
  1168. func (c *ExampleController) All() {}
  1169. // OR
  1170. func (c *ExampleController) Any() {}
  1171. func (c *ExampleController) BeforeActivation(b mvc.BeforeActivation) {
  1172. // 1 -> the HTTP Method
  1173. // 2 -> the route's path
  1174. // 3 -> this controller's method name that should be handler for that route.
  1175. b.Handle("GET", "/mypath/{param}", "DoIt", optionalMiddlewareHere...)
  1176. }
  1177. // After activation, all dependencies are set-ed - so read only access on them
  1178. // but still possible to add custom controller or simple standard handlers.
  1179. func (c *ExampleController) AfterActivation(a mvc.AfterActivation) {}
  1180. Iris web framework supports Request data, Models, Persistence Data and Binding
  1181. with the fastest possible execution.
  1182. Characteristics:
  1183. All HTTP Methods are supported, for example if want to serve `GET`
  1184. then the controller should have a function named `Get()`,
  1185. you can define more than one method function to serve in the same Controller.
  1186. Register custom controller's struct's methods as handlers with custom paths(even with regex parametermized path)
  1187. via the `BeforeActivation` custom event callback, per-controller. Example:
  1188. package main
  1189. import (
  1190. "github.com/kataras/iris"
  1191. "github.com/kataras/iris/mvc"
  1192. )
  1193. func main() {
  1194. app := iris.New()
  1195. mvc.Configure(app.Party("/root"), myMVC)
  1196. app.Run(iris.Addr(":8080"))
  1197. }
  1198. func myMVC(app *mvc.Application) {
  1199. // app.Register(...)
  1200. // app.Router.Use/UseGlobal/Done(...)
  1201. app.Handle(new(MyController))
  1202. }
  1203. type MyController struct {}
  1204. func (m *MyController) BeforeActivation(b mvc.BeforeActivation) {
  1205. // b.Dependencies().Add/Remove
  1206. // b.Router().Use/UseGlobal/Done // and any standard API call you already know
  1207. // 1-> Method
  1208. // 2-> Path
  1209. // 3-> The controller's function name to be parsed as handler
  1210. // 4-> Any handlers that should run before the MyCustomHandler
  1211. b.Handle("GET", "/something/{id:long}", "MyCustomHandler", anyMiddleware...)
  1212. }
  1213. // GET: http://localhost:8080/root
  1214. func (m *MyController) Get() string { return "Hey" }
  1215. // GET: http://localhost:8080/root/something/{id:long}
  1216. func (m *MyController) MyCustomHandler(id int64) string { return "MyCustomHandler says Hey" }
  1217. Persistence data inside your Controller struct (share data between requests)
  1218. by defining services to the Dependencies or have a `Singleton` controller scope.
  1219. Share the dependencies between controllers or register them on a parent MVC Application, and ability
  1220. to modify dependencies per-controller on the `BeforeActivation` optional event callback inside a Controller,
  1221. i.e
  1222. func(c *MyController) BeforeActivation(b mvc.BeforeActivation) { b.Dependencies().Add/Remove(...) }
  1223. Access to the `Context` as a controller's field(no manual binding is neede) i.e `Ctx iris.Context` or via a method's input argument,
  1224. i.e
  1225. func(ctx iris.Context, otherArguments...)
  1226. Models inside your Controller struct (set-ed at the Method function and rendered by the View).
  1227. You can return models from a controller's method or set a field in the request lifecycle
  1228. and return that field to another method, in the same request lifecycle.
  1229. Flow as you used to, mvc application has its own `Router` which is a type of `iris/router.Party`, the standard iris api.
  1230. `Controllers` can be registered to any `Party`, including Subdomains, the Party's begin and done handlers work as expected.
  1231. Optional `BeginRequest(ctx)` function to perform any initialization before the method execution,
  1232. useful to call middlewares or when many methods use the same collection of data.
  1233. Optional `EndRequest(ctx)` function to perform any finalization after any method executed.
  1234. Session dynamic dependency via manager's `Start` to the MVC Application, i.e
  1235. mvcApp.Register(sessions.New(sessions.Config{Cookie: "iris_session_id"}).Start)
  1236. Inheritance, recursively.
  1237. Access to the dynamic path parameters via the controller's methods' input arguments, no binding is needed.
  1238. When you use the Iris' default syntax to parse handlers from a controller, you need to suffix the methods
  1239. with the `By` word, uppercase is a new sub path. Example:
  1240. Register one or more relative paths and able to get path parameters, i.e
  1241. If `mvc.New(app.Party("/user")).Handle(new(user.Controller))`
  1242. - `func(*Controller) Get()` - `GET:/user` , as usual.
  1243. - `func(*Controller) Post()` - `POST:/user`, as usual.
  1244. - `func(*Controller) GetLogin()` - `GET:/user/login`
  1245. - `func(*Controller) PostLogin()` - `POST:/user/login`
  1246. - `func(*Controller) GetProfileFollowers()` - `GET:/user/profile/followers`
  1247. - `func(*Controller) PostProfileFollowers()` - `POST:/user/profile/followers`
  1248. - `func(*Controller) GetBy(id int64)` - `GET:/user/{param:long}`
  1249. - `func(*Controller) PostBy(id int64)` - `POST:/user/{param:long}`
  1250. If `mvc.New(app.Party("/profile")).Handle(new(profile.Controller))`
  1251. - `func(*Controller) GetBy(username string)` - `GET:/profile/{param:string}`
  1252. If `mvc.New(app.Party("/assets")).Handle(new(file.Controller))`
  1253. - `func(*Controller) GetByWildard(path string)` - `GET:/assets/{param:path}`
  1254. If `mvc.New(app.Party("/equality")).Handle(new(profile.Equality))`
  1255. - `func(*Controller) GetBy(is bool)` - `GET:/equality/{param:boolean}`
  1256. - `func(*Controller) GetByOtherBy(is bool, otherID int64)` - `GET:/equality/{paramfirst:boolean}/other/{paramsecond:long}`
  1257. Supported types for method functions receivers: int, int64, bool and string.
  1258. Response via output arguments, optionally, i.e
  1259. func(c *ExampleController) Get() string |
  1260. (string, string) |
  1261. (string, int) |
  1262. (string, error) |
  1263. int |
  1264. (int, string) |
  1265. (any, int) |
  1266. error |
  1267. (int, error) |
  1268. (customStruct, error) |
  1269. (any, error) |
  1270. bool |
  1271. (any, bool)
  1272. customStruct |
  1273. (customStruct, int) |
  1274. (customStruct, string) |
  1275. `Result` or (`Result`, error)
  1276. Where `any` means everything, from custom structs to standard language's types-.
  1277. `Result` is an interface which contains only that function: Dispatch(ctx iris.Context)
  1278. and Get where HTTP Method function(Post, Put, Delete...).
  1279. Iris MVC Method Result
  1280. Iris has a very powerful and blazing fast MVC support, you can return any value of any type from a method function
  1281. and it will be sent to the client as expected.
  1282. * if `string` then it's the body.
  1283. * if `string` is the second output argument then it's the content type.
  1284. * if `int` then it's the status code.
  1285. * if `bool` is false then it throws 404 not found http error by skipping everything else.
  1286. * if `error` and not nil then (any type) response will be omitted and error's text with a 400 bad request will be rendered instead.
  1287. * if `(int, error)` and error is not nil then the response result will be the error's text with the status code as `int`.
  1288. * if `custom struct` or `interface{}` or `slice` or `map` then it will be rendered as json, unless a `string` content type is following.
  1289. * if `mvc.Result` then it executes its `Dispatch` function, so good design patters can be used to split the model's logic where needed.
  1290. Examples with good patterns to follow but not intend to be used in production of course can be found at:
  1291. https://github.com/kataras/iris/tree/master/_examples/#mvc.
  1292. Using Iris MVC for code reuse
  1293. By creating components that are independent of one another,
  1294. developers are able to reuse components quickly and easily in other applications.
  1295. The same (or similar) view for one application can be refactored for another application with
  1296. different data because the view is simply handling how the data is being displayed to the user.
  1297. If you're new to back-end web development read about the MVC architectural pattern first,
  1298. a good start is that wikipedia article: https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller.
  1299. That's the basics
  1300. But you should have a basic idea of the framework by now, we just scratched the surface.
  1301. If you enjoy what you just saw and want to learn more, please follow the below links:
  1302. Examples:
  1303. https://github.com/kataras/iris/tree/master/_examples
  1304. Middleware:
  1305. https://github.com/kataras/iris/tree/master/middleware
  1306. https://github.com/iris-contrib/middleware
  1307. Home Page:
  1308. https://iris-go.com
  1309. Book (in-progress):
  1310. https://docs.iris-go.com
  1311. */
  1312. package iris