mirror of
https://github.com/kataras/iris.git
synced 2025-12-18 02:17:05 +00:00
new simple _examples/README.md, wiki should live only inside kataras/iris/wiki and the provided e-book
Former-commit-id: 350eafb0f70f8433e394e103ff93fa332ee00a05
This commit is contained in:
18
_examples/dependency-injection/overview/datamodels/movie.go
Normal file
18
_examples/dependency-injection/overview/datamodels/movie.go
Normal file
@@ -0,0 +1,18 @@
|
||||
// file: datamodels/movie.go
|
||||
|
||||
package datamodels
|
||||
|
||||
// Movie is our sample data structure.
|
||||
// Keep note that the tags for public-use (for our web app)
|
||||
// should be kept in other file like "web/viewmodels/movie.go"
|
||||
// which could wrap by embedding the datamodels.Movie or
|
||||
// declare new fields instead butwe will use this datamodel
|
||||
// as the only one Movie model in our application,
|
||||
// for the shake of simplicty.
|
||||
type Movie struct {
|
||||
ID uint64 `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Year int `json:"year"`
|
||||
Genre string `json:"genre"`
|
||||
Poster string `json:"poster"`
|
||||
}
|
||||
44
_examples/dependency-injection/overview/datasource/movies.go
Normal file
44
_examples/dependency-injection/overview/datasource/movies.go
Normal file
@@ -0,0 +1,44 @@
|
||||
// file: datasource/movies.go
|
||||
|
||||
package datasource
|
||||
|
||||
import "github.com/kataras/iris/v12/_examples/dependency-injection/overview/datamodels"
|
||||
|
||||
// Movies is our imaginary data source.
|
||||
var Movies = map[uint64]datamodels.Movie{
|
||||
1: {
|
||||
ID: 1,
|
||||
Name: "Casablanca",
|
||||
Year: 1942,
|
||||
Genre: "Romance",
|
||||
Poster: "https://iris-go.com/images/examples/mvc-movies/1.jpg",
|
||||
},
|
||||
2: {
|
||||
ID: 2,
|
||||
Name: "Gone with the Wind",
|
||||
Year: 1939,
|
||||
Genre: "Romance",
|
||||
Poster: "https://iris-go.com/images/examples/mvc-movies/2.jpg",
|
||||
},
|
||||
3: {
|
||||
ID: 3,
|
||||
Name: "Citizen Kane",
|
||||
Year: 1941,
|
||||
Genre: "Mystery",
|
||||
Poster: "https://iris-go.com/images/examples/mvc-movies/3.jpg",
|
||||
},
|
||||
4: {
|
||||
ID: 4,
|
||||
Name: "The Wizard of Oz",
|
||||
Year: 1939,
|
||||
Genre: "Fantasy",
|
||||
Poster: "https://iris-go.com/images/examples/mvc-movies/4.jpg",
|
||||
},
|
||||
5: {
|
||||
ID: 5,
|
||||
Name: "North by Northwest",
|
||||
Year: 1959,
|
||||
Genre: "Thriller",
|
||||
Poster: "https://iris-go.com/images/examples/mvc-movies/5.jpg",
|
||||
},
|
||||
}
|
||||
55
_examples/dependency-injection/overview/main.go
Normal file
55
_examples/dependency-injection/overview/main.go
Normal file
@@ -0,0 +1,55 @@
|
||||
// file: main.go
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/kataras/iris/v12/_examples/dependency-injection/overview/datasource"
|
||||
"github.com/kataras/iris/v12/_examples/dependency-injection/overview/repositories"
|
||||
"github.com/kataras/iris/v12/_examples/dependency-injection/overview/services"
|
||||
"github.com/kataras/iris/v12/_examples/dependency-injection/overview/web/middleware"
|
||||
"github.com/kataras/iris/v12/_examples/dependency-injection/overview/web/routes"
|
||||
|
||||
"github.com/kataras/iris/v12"
|
||||
)
|
||||
|
||||
func main() {
|
||||
app := iris.New()
|
||||
app.Logger().SetLevel("debug")
|
||||
|
||||
// Load the template files.
|
||||
app.RegisterView(iris.HTML("./web/views", ".html"))
|
||||
|
||||
// Create our movie repository with some (memory) data from the datasource.
|
||||
repo := repositories.NewMovieRepository(datasource.Movies)
|
||||
|
||||
app.Party("/hello").ConfigureContainer(func(r *iris.APIContainer) {
|
||||
r.Get("/", routes.Hello)
|
||||
r.Get("/{name}", routes.HelloName)
|
||||
})
|
||||
|
||||
app.Party("/movies").ConfigureContainer(func(r *iris.APIContainer) {
|
||||
// Create our movie service, we will bind it to the movie app's dependencies.
|
||||
movieService := services.NewMovieService(repo)
|
||||
r.RegisterDependency(movieService)
|
||||
|
||||
// Add the basic authentication(admin:password) middleware
|
||||
// for the /movies based requests.
|
||||
r.Use(middleware.BasicAuth)
|
||||
|
||||
r.Get("/", routes.Movies)
|
||||
r.Get("/{id:uint64}", routes.MovieByID)
|
||||
r.Put("/{id:uint64}", routes.UpdateMovieByID)
|
||||
r.Delete("/{id:uint64}", routes.DeleteMovieByID)
|
||||
})
|
||||
|
||||
// http://localhost:8080/hello
|
||||
// http://localhost:8080/hello/iris
|
||||
// http://localhost:8080/movies ("admin": "password")
|
||||
// http://localhost:8080/movies/1
|
||||
app.Listen(
|
||||
// Start the web server at localhost:8080
|
||||
"localhost:8080",
|
||||
// enables faster json serialization and more:
|
||||
iris.WithOptimizations,
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,176 @@
|
||||
// file: repositories/movie_repository.go
|
||||
|
||||
package repositories
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"sync"
|
||||
|
||||
"github.com/kataras/iris/v12/_examples/dependency-injection/overview/datamodels"
|
||||
)
|
||||
|
||||
// Query represents the visitor and action queries.
|
||||
type Query func(datamodels.Movie) bool
|
||||
|
||||
// MovieRepository handles the basic operations of a movie entity/model.
|
||||
// It's an interface in order to be testable, i.e a memory movie repository or
|
||||
// a connected to an sql database.
|
||||
type MovieRepository interface {
|
||||
Exec(query Query, action Query, limit int, mode int) (ok bool)
|
||||
|
||||
Select(query Query) (movie datamodels.Movie, found bool)
|
||||
SelectMany(query Query, limit int) (results []datamodels.Movie)
|
||||
|
||||
InsertOrUpdate(movie datamodels.Movie) (updatedMovie datamodels.Movie, err error)
|
||||
Delete(query Query, limit int) (deleted bool)
|
||||
}
|
||||
|
||||
// NewMovieRepository returns a new movie memory-based repository,
|
||||
// the one and only repository type in our example.
|
||||
func NewMovieRepository(source map[uint64]datamodels.Movie) MovieRepository {
|
||||
return &movieMemoryRepository{source: source}
|
||||
}
|
||||
|
||||
// movieMemoryRepository is a "MovieRepository"
|
||||
// which manages the movies using the memory data source (map).
|
||||
type movieMemoryRepository struct {
|
||||
source map[uint64]datamodels.Movie
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
const (
|
||||
// ReadOnlyMode will RLock(read) the data .
|
||||
ReadOnlyMode = iota
|
||||
// ReadWriteMode will Lock(read/write) the data.
|
||||
ReadWriteMode
|
||||
)
|
||||
|
||||
func (r *movieMemoryRepository) Exec(query Query, action Query, actionLimit int, mode int) (ok bool) {
|
||||
loops := 0
|
||||
|
||||
if mode == ReadOnlyMode {
|
||||
r.mu.RLock()
|
||||
defer r.mu.RUnlock()
|
||||
} else {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
}
|
||||
|
||||
for _, movie := range r.source {
|
||||
ok = query(movie)
|
||||
if ok {
|
||||
if action(movie) {
|
||||
loops++
|
||||
if actionLimit >= loops {
|
||||
break // break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Select receives a query function
|
||||
// which is fired for every single movie model inside
|
||||
// our imaginary data source.
|
||||
// When that function returns true then it stops the iteration.
|
||||
//
|
||||
// It returns the query's return last known "found" value
|
||||
// and the last known movie model
|
||||
// to help callers to reduce the LOC.
|
||||
//
|
||||
// It's actually a simple but very clever prototype function
|
||||
// I'm using everywhere since I firstly think of it,
|
||||
// hope you'll find it very useful as well.
|
||||
func (r *movieMemoryRepository) Select(query Query) (movie datamodels.Movie, found bool) {
|
||||
found = r.Exec(query, func(m datamodels.Movie) bool {
|
||||
movie = m
|
||||
return true
|
||||
}, 1, ReadOnlyMode)
|
||||
|
||||
// set an empty datamodels.Movie if not found at all.
|
||||
if !found {
|
||||
movie = datamodels.Movie{}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// SelectMany same as Select but returns one or more datamodels.Movie as a slice.
|
||||
// If limit <=0 then it returns everything.
|
||||
func (r *movieMemoryRepository) SelectMany(query Query, limit int) (results []datamodels.Movie) {
|
||||
r.Exec(query, func(m datamodels.Movie) bool {
|
||||
results = append(results, m)
|
||||
return true
|
||||
}, limit, ReadOnlyMode)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// InsertOrUpdate adds or updates a movie to the (memory) storage.
|
||||
//
|
||||
// Returns the new movie and an error if any.
|
||||
func (r *movieMemoryRepository) InsertOrUpdate(movie datamodels.Movie) (datamodels.Movie, error) {
|
||||
id := movie.ID
|
||||
|
||||
if id == 0 { // Create new action
|
||||
var lastID uint64
|
||||
// find the biggest ID in order to not have duplications
|
||||
// in productions apps you can use a third-party
|
||||
// library to generate a UUID as string.
|
||||
r.mu.RLock()
|
||||
for _, item := range r.source {
|
||||
if item.ID > lastID {
|
||||
lastID = item.ID
|
||||
}
|
||||
}
|
||||
r.mu.RUnlock()
|
||||
|
||||
id = lastID + 1
|
||||
movie.ID = id
|
||||
|
||||
// map-specific thing
|
||||
r.mu.Lock()
|
||||
r.source[id] = movie
|
||||
r.mu.Unlock()
|
||||
|
||||
return movie, nil
|
||||
}
|
||||
|
||||
// Update action based on the movie.ID,
|
||||
// here we will allow updating the poster and genre if not empty.
|
||||
// Alternatively we could do pure replace instead:
|
||||
// r.source[id] = movie
|
||||
// and comment the code below;
|
||||
current, exists := r.Select(func(m datamodels.Movie) bool {
|
||||
return m.ID == id
|
||||
})
|
||||
|
||||
if !exists { // ID is not a real one, return an error.
|
||||
return datamodels.Movie{}, errors.New("failed to update a nonexistent movie")
|
||||
}
|
||||
|
||||
// or comment these and r.source[id] = m for pure replace
|
||||
if movie.Poster != "" {
|
||||
current.Poster = movie.Poster
|
||||
}
|
||||
|
||||
if movie.Genre != "" {
|
||||
current.Genre = movie.Genre
|
||||
}
|
||||
|
||||
// map-specific thing
|
||||
r.mu.Lock()
|
||||
r.source[id] = current
|
||||
r.mu.Unlock()
|
||||
|
||||
return movie, nil
|
||||
}
|
||||
|
||||
func (r *movieMemoryRepository) Delete(query Query, limit int) bool {
|
||||
return r.Exec(query, func(m datamodels.Movie) bool {
|
||||
delete(r.source, m.ID)
|
||||
return true
|
||||
}, limit, ReadWriteMode)
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
// file: services/movie_service.go
|
||||
|
||||
package services
|
||||
|
||||
import (
|
||||
"github.com/kataras/iris/v12/_examples/dependency-injection/overview/datamodels"
|
||||
"github.com/kataras/iris/v12/_examples/dependency-injection/overview/repositories"
|
||||
)
|
||||
|
||||
// MovieService handles some of the CRUID operations of the movie datamodel.
|
||||
// It depends on a movie repository for its actions.
|
||||
// It's here to decouple the data source from the higher level compoments.
|
||||
// As a result a different repository type can be used with the same logic without any aditional changes.
|
||||
// It's an interface and it's used as interface everywhere
|
||||
// because we may need to change or try an experimental different domain logic at the future.
|
||||
type MovieService interface {
|
||||
GetAll() []datamodels.Movie
|
||||
GetByID(id uint64) (datamodels.Movie, bool)
|
||||
DeleteByID(id uint64) bool
|
||||
UpdatePosterAndGenreByID(id uint64, poster string, genre string) (datamodels.Movie, error)
|
||||
}
|
||||
|
||||
// NewMovieService returns the default movie service.
|
||||
func NewMovieService(repo repositories.MovieRepository) MovieService {
|
||||
return &movieService{
|
||||
repo: repo,
|
||||
}
|
||||
}
|
||||
|
||||
type movieService struct {
|
||||
repo repositories.MovieRepository
|
||||
}
|
||||
|
||||
// GetAll returns all movies.
|
||||
func (s *movieService) GetAll() []datamodels.Movie {
|
||||
return s.repo.SelectMany(func(_ datamodels.Movie) bool {
|
||||
return true
|
||||
}, -1)
|
||||
}
|
||||
|
||||
// GetByID returns a movie based on its id.
|
||||
func (s *movieService) GetByID(id uint64) (datamodels.Movie, bool) {
|
||||
return s.repo.Select(func(m datamodels.Movie) bool {
|
||||
return m.ID == id
|
||||
})
|
||||
}
|
||||
|
||||
// UpdatePosterAndGenreByID updates a movie's poster and genre.
|
||||
func (s *movieService) UpdatePosterAndGenreByID(id uint64, poster string, genre string) (datamodels.Movie, error) {
|
||||
// update the movie and return it.
|
||||
return s.repo.InsertOrUpdate(datamodels.Movie{
|
||||
ID: id,
|
||||
Poster: poster,
|
||||
Genre: genre,
|
||||
})
|
||||
}
|
||||
|
||||
// DeleteByID deletes a movie by its id.
|
||||
//
|
||||
// Returns true if deleted otherwise false.
|
||||
func (s *movieService) DeleteByID(id uint64) bool {
|
||||
return s.repo.Delete(func(m datamodels.Movie) bool {
|
||||
return m.ID == id
|
||||
}, 1)
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
// file: web/middleware/basicauth.go
|
||||
|
||||
package middleware
|
||||
|
||||
import "github.com/kataras/iris/v12/middleware/basicauth"
|
||||
|
||||
// BasicAuth middleware sample.
|
||||
var BasicAuth = basicauth.New(basicauth.Config{
|
||||
Users: map[string]string{
|
||||
"admin": "password",
|
||||
},
|
||||
})
|
||||
50
_examples/dependency-injection/overview/web/routes/hello.go
Normal file
50
_examples/dependency-injection/overview/web/routes/hello.go
Normal file
@@ -0,0 +1,50 @@
|
||||
// file: web/routes/hello.go
|
||||
|
||||
package routes
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/kataras/iris/v12/hero"
|
||||
)
|
||||
|
||||
var helloView = hero.View{
|
||||
Name: "hello/index.html",
|
||||
Data: map[string]interface{}{
|
||||
"Title": "Hello Page",
|
||||
"MyMessage": "Welcome to my awesome website",
|
||||
},
|
||||
}
|
||||
|
||||
// Hello will return a predefined view with bind data.
|
||||
//
|
||||
// `hero.Result` is just an interface with a `Dispatch` function.
|
||||
// `hero.Response` and `hero.View` are the builtin result type dispatchers
|
||||
// you can even create custom response dispatchers by
|
||||
// implementing the `github.com/kataras/iris/hero#Result` interface.
|
||||
func Hello() hero.Result {
|
||||
return helloView
|
||||
}
|
||||
|
||||
// you can define a standard error in order to re-use anywhere in your app.
|
||||
var errBadName = errors.New("bad name")
|
||||
|
||||
// you can just return it as error or even better
|
||||
// wrap this error with an hero.Response to make it an hero.Result compatible type.
|
||||
var badName = hero.Response{Err: errBadName, Code: 400}
|
||||
|
||||
// HelloName returns a "Hello {name}" response.
|
||||
// Demos:
|
||||
// curl -i http://localhost:8080/hello/iris
|
||||
// curl -i http://localhost:8080/hello/anything
|
||||
func HelloName(name string) hero.Result {
|
||||
if name != "iris" {
|
||||
return badName
|
||||
}
|
||||
|
||||
// return hero.Response{Text: "Hello " + name} OR:
|
||||
return hero.View{
|
||||
Name: "hello/name.html",
|
||||
Data: name,
|
||||
}
|
||||
}
|
||||
59
_examples/dependency-injection/overview/web/routes/movies.go
Normal file
59
_examples/dependency-injection/overview/web/routes/movies.go
Normal file
@@ -0,0 +1,59 @@
|
||||
// file: web/routes/movie.go
|
||||
|
||||
package routes
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/kataras/iris/v12/_examples/dependency-injection/overview/datamodels"
|
||||
"github.com/kataras/iris/v12/_examples/dependency-injection/overview/services"
|
||||
|
||||
"github.com/kataras/iris/v12"
|
||||
)
|
||||
|
||||
// Movies returns list of the movies.
|
||||
// Demo:
|
||||
// curl -i http://localhost:8080/movies
|
||||
func Movies(service services.MovieService) (results []datamodels.Movie) {
|
||||
return service.GetAll()
|
||||
}
|
||||
|
||||
// MovieByID returns a movie.
|
||||
// Demo:
|
||||
// curl -i http://localhost:8080/movies/1
|
||||
func MovieByID(service services.MovieService, id uint64) (movie datamodels.Movie, found bool) {
|
||||
return service.GetByID(id) // it will throw 404 if not found.
|
||||
}
|
||||
|
||||
// UpdateMovieByID updates a movie.
|
||||
// Demo:
|
||||
// curl -i -X PUT -F "genre=Thriller" -F "poster=@/Users/kataras/Downloads/out.gif" http://localhost:8080/movies/1
|
||||
func UpdateMovieByID(ctx iris.Context, service services.MovieService, id uint64) (datamodels.Movie, error) {
|
||||
// get the request data for poster and genre
|
||||
file, info, err := ctx.FormFile("poster")
|
||||
if err != nil {
|
||||
return datamodels.Movie{}, errors.New("failed due form file 'poster' missing")
|
||||
}
|
||||
// we don't need the file so close it now.
|
||||
file.Close()
|
||||
|
||||
// imagine that is the url of the uploaded file...
|
||||
poster := info.Filename
|
||||
genre := ctx.FormValue("genre")
|
||||
|
||||
return service.UpdatePosterAndGenreByID(id, poster, genre)
|
||||
}
|
||||
|
||||
// DeleteMovieByID deletes a movie.
|
||||
// Demo:
|
||||
// curl -i -X DELETE -u admin:password http://localhost:8080/movies/1
|
||||
func DeleteMovieByID(service services.MovieService, id uint64) interface{} {
|
||||
wasDel := service.DeleteByID(id)
|
||||
if wasDel {
|
||||
// return the deleted movie's ID
|
||||
return iris.Map{"deleted": id}
|
||||
}
|
||||
// right here we can see that a method function can return any of those two types(map or int),
|
||||
// we don't have to specify the return type to a specific type.
|
||||
return iris.StatusBadRequest
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
<!-- file: web/views/hello/index.html -->
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>{{.Title}} - My App</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<p>{{.MyMessage}}</p>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,12 @@
|
||||
<!-- file: web/views/hello/name.html -->
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>{{.}}' Portfolio - My App</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>Hello {{.}}</h1>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
34
_examples/dependency-injection/sessions/main.go
Normal file
34
_examples/dependency-injection/sessions/main.go
Normal file
@@ -0,0 +1,34 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/kataras/iris/v12/_examples/dependency-injection/sessions/routes"
|
||||
|
||||
"github.com/kataras/iris/v12"
|
||||
"github.com/kataras/iris/v12/sessions"
|
||||
)
|
||||
|
||||
func main() {
|
||||
app := iris.New()
|
||||
sessionManager := sessions.New(sessions.Config{
|
||||
Cookie: "site_session_id",
|
||||
Expires: 60 * time.Minute,
|
||||
AllowReclaim: true,
|
||||
})
|
||||
|
||||
// Session is automatically binded through `sessions.Get(ctx)`
|
||||
// if a *sessions.Session input argument is present on the handler's function,
|
||||
// which `routes.Index` does.
|
||||
app.Use(sessionManager.Handler())
|
||||
|
||||
// Method: GET
|
||||
// Path: http://localhost:8080
|
||||
app.ConfigureContainer(registerRoutes)
|
||||
|
||||
app.Listen(":8080")
|
||||
}
|
||||
|
||||
func registerRoutes(api *iris.APIContainer) {
|
||||
api.Get("/", routes.Index)
|
||||
}
|
||||
17
_examples/dependency-injection/sessions/routes/index.go
Normal file
17
_examples/dependency-injection/sessions/routes/index.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package routes
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/kataras/iris/v12/sessions"
|
||||
)
|
||||
|
||||
// Index will increment a simple int version based on the visits that this user/session did.
|
||||
func Index(session *sessions.Session) string {
|
||||
// it increments a "visits" value of integer by one,
|
||||
// if the entry with key 'visits' doesn't exist it will create it for you.
|
||||
visits := session.Increment("visits", 1)
|
||||
|
||||
// write the current, updated visits.
|
||||
return fmt.Sprintf("%d visit(s) from my current session", visits)
|
||||
}
|
||||
156
_examples/dependency-injection/smart-contract/main.go
Normal file
156
_examples/dependency-injection/smart-contract/main.go
Normal file
@@ -0,0 +1,156 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/kataras/iris/v12"
|
||||
|
||||
// External package to optionally filter JSON responses before sent,
|
||||
// see `sendJSON` for more.
|
||||
"github.com/jmespath/go-jmespath"
|
||||
)
|
||||
|
||||
/*
|
||||
$ go get github.com/jmespath/go-jmespath
|
||||
*/
|
||||
|
||||
func main() {
|
||||
app := newApp()
|
||||
app.Logger().SetLevel("debug")
|
||||
|
||||
// http://localhost:8080/users?query=[?Name == 'John Doe'].Age
|
||||
// <- client will receive the age of a user which his name is "John Doe".
|
||||
// You can also test query=[0].Name to retrieve the first user's name.
|
||||
// Or even query=[0:3].Age to print the first three ages.
|
||||
// Learn more about jmespath and how to filter:
|
||||
// http://jmespath.readthedocs.io/en/latest/ and
|
||||
// https://github.com/jmespath/go-jmespath/tree/master/fuzz/testdata
|
||||
//
|
||||
// http://localhost:8080/users
|
||||
// http://localhost:8080/users/William%20Woe
|
||||
// http://localhost:8080/users/William%20Woe/age
|
||||
app.Listen(":8080")
|
||||
}
|
||||
|
||||
func newApp() *iris.Application {
|
||||
app := iris.New()
|
||||
|
||||
// PartyFunc is the same as usersRouter := app.Party("/users")
|
||||
// but it gives us an easy way to call router's registration functions,
|
||||
// i.e functions from another package that can handle this group of routes.
|
||||
app.PartyFunc("/users", registerUsersRoutes)
|
||||
|
||||
return app
|
||||
}
|
||||
|
||||
/*
|
||||
START OF USERS ROUTER
|
||||
*/
|
||||
|
||||
func registerUsersRoutes(usersRouter iris.Party) {
|
||||
// GET: /users
|
||||
usersRouter.Get("/", getAllUsersHandler)
|
||||
usersRouter.Party("/{name}").ConfigureContainer(registerUserRoutes)
|
||||
}
|
||||
|
||||
type user struct {
|
||||
Name string `json:"name"`
|
||||
Age int `json:"age"`
|
||||
}
|
||||
|
||||
var usersSample = []*user{
|
||||
{"William Woe", 25},
|
||||
{"Mary Moe", 15},
|
||||
{"John Doe", 17},
|
||||
}
|
||||
|
||||
func getAllUsersHandler(ctx iris.Context) {
|
||||
err := sendJSON(ctx, usersSample)
|
||||
if err != nil {
|
||||
fail(ctx, iris.StatusInternalServerError, "unable to send a list of all users: %v", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
START OF USERS.USER SUB ROUTER
|
||||
*/
|
||||
|
||||
func registerUserRoutes(userRouter *iris.APIContainer) {
|
||||
userRouter.RegisterDependency(userDependency)
|
||||
// GET: /users/{name:string}
|
||||
userRouter.Get("/", getUserHandler)
|
||||
// GET: /users/{name:string}/age
|
||||
userRouter.Get("/age", getUserAgeHandler)
|
||||
}
|
||||
|
||||
var userDependency = func(ctx iris.Context) *user {
|
||||
name := strings.Title(ctx.Params().Get("name"))
|
||||
for _, u := range usersSample {
|
||||
if u.Name == name {
|
||||
return u
|
||||
}
|
||||
}
|
||||
|
||||
// you may want or no to handle the error here, either way the main route handler
|
||||
// is going to be executed, always. A dynamic dependency(per-request) is not a middleware, so things like `ctx.Next()` or `ctx.StopExecution()`
|
||||
// do not apply here, look the `getUserHandler`'s first lines; we stop/exit the handler manually
|
||||
// if the received user is nil but depending on your app's needs, it is possible to do other things too.
|
||||
// A dynamic dependency like this can return more output values, i.e (*user, bool).
|
||||
fail(ctx, iris.StatusNotFound, "user with name '%s' not found", name)
|
||||
return nil
|
||||
}
|
||||
|
||||
func getUserHandler(ctx iris.Context, u *user) {
|
||||
sendJSON(ctx, u)
|
||||
}
|
||||
|
||||
func getUserAgeHandler(u *user) string {
|
||||
return fmt.Sprintf("%d", u.Age)
|
||||
}
|
||||
|
||||
/* END OF USERS.USER SUB ROUTER */
|
||||
|
||||
/* END OF USERS ROUTER */
|
||||
|
||||
// common JSON response for manual HTTP errors, optionally.
|
||||
type httpError struct {
|
||||
Code int `json:"code"`
|
||||
Reason string `json:"reason"`
|
||||
}
|
||||
|
||||
func (h httpError) Error() string {
|
||||
return fmt.Sprintf("Status Code: %d\nReason: %s", h.Code, h.Reason)
|
||||
}
|
||||
|
||||
func fail(ctx iris.Context, statusCode int, format string, a ...interface{}) {
|
||||
err := httpError{
|
||||
Code: statusCode,
|
||||
Reason: fmt.Sprintf(format, a...),
|
||||
}
|
||||
|
||||
// log all the >= 500 internal errors.
|
||||
if statusCode >= 500 {
|
||||
ctx.Application().Logger().Error(err)
|
||||
}
|
||||
|
||||
// no next handlers will run.
|
||||
ctx.StopWithJSON(statusCode, err)
|
||||
}
|
||||
|
||||
// JSON helper to give end-user the ability to put indention chars or filtering the response, you can do that, optionally.
|
||||
// If you'd like to see that function inside the Iris' Context itself raise a [Feature Request] issue and link this example.
|
||||
func sendJSON(ctx iris.Context, resp interface{}) (err error) {
|
||||
indent := ctx.URLParamDefault("indent", " ")
|
||||
// i.e [?Name == 'John Doe'].Age # to output the [age] of a user which his name is "John Doe".
|
||||
if query := ctx.URLParam("query"); query != "" && query != "[]" {
|
||||
resp, err = jmespath.Search(query, resp)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
_, err = ctx.JSON(resp, iris.JSON{Indent: indent, UnescapeHTML: true})
|
||||
return err
|
||||
}
|
||||
Reference in New Issue
Block a user