mirror of
https://github.com/kataras/iris.git
synced 2025-01-23 10:41:03 +01:00
add tutorial for the official mongodb go driver
Former-commit-id: 8353dd101c37c223bba404403f9f8fa2d042fede
This commit is contained in:
parent
680b5a0923
commit
4284739151
|
@ -17,6 +17,10 @@ Developers are not forced to upgrade if they don't really need it. Upgrade whene
|
||||||
|
|
||||||
**How to upgrade**: Open your command-line and execute this command: `go get -u github.com/kataras/iris` or let the automatic updater do that for you.
|
**How to upgrade**: Open your command-line and execute this command: `go get -u github.com/kataras/iris` or let the automatic updater do that for you.
|
||||||
|
|
||||||
|
# Soon
|
||||||
|
|
||||||
|
Coming soon, stay tuned by reading the [PR](https://github.com/kataras/iris/pull/1175) progress.
|
||||||
|
|
||||||
# Fr, 11 January 2019 | v11.1.1
|
# Fr, 11 January 2019 | v11.1.1
|
||||||
|
|
||||||
Happy new year! This is a minor release, contains mostly bug fixes.
|
Happy new year! This is a minor release, contains mostly bug fixes.
|
||||||
|
|
|
@ -1020,7 +1020,7 @@ Iris, unlike others, is 100% compatible with the standards and that's why the ma
|
||||||
|
|
||||||
## Support
|
## Support
|
||||||
|
|
||||||
- [HISTORY](HISTORY.md#fr-11-january-2019--v1111) file is your best friend, it contains information about the latest features and changes
|
- [HISTORY](HISTORY.md#soon) file is your best friend, it contains information about the latest features and changes
|
||||||
- Did you happen to find a bug? Post it at [github issues](https://github.com/kataras/iris/issues)
|
- Did you happen to find a bug? Post it at [github issues](https://github.com/kataras/iris/issues)
|
||||||
- Do you have any questions or need to speak with someone experienced to solve a problem at real-time? Join us to the [community chat](https://chat.iris-go.com)
|
- Do you have any questions or need to speak with someone experienced to solve a problem at real-time? Join us to the [community chat](https://chat.iris-go.com)
|
||||||
- Complete our form-based user experience report by clicking [here](https://docs.google.com/forms/d/e/1FAIpQLSdCxZXPANg_xHWil4kVAdhmh7EBBHQZ_4_xSZVDL-oCC_z5pA/viewform?usp=sf_link)
|
- Complete our form-based user experience report by clicking [here](https://docs.google.com/forms/d/e/1FAIpQLSdCxZXPANg_xHWil4kVAdhmh7EBBHQZ_4_xSZVDL-oCC_z5pA/viewform?usp=sf_link)
|
||||||
|
|
2
VERSION
2
VERSION
|
@ -1 +1 @@
|
||||||
11.1.1:https://github.com/kataras/iris/blob/master/HISTORY.md#fr-11-january-2019--v1111
|
11.2.0:https://github.com/kataras/iris/blob/master/HISTORY.md#soon
|
2
_examples/tutorial/mongodb/.env
Normal file
2
_examples/tutorial/mongodb/.env
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
PORT=8080
|
||||||
|
DSN=mongodb://localhost:27017
|
BIN
_examples/tutorial/mongodb/0_create_movie.png
Normal file
BIN
_examples/tutorial/mongodb/0_create_movie.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 92 KiB |
BIN
_examples/tutorial/mongodb/1_update_movie.png
Normal file
BIN
_examples/tutorial/mongodb/1_update_movie.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 69 KiB |
BIN
_examples/tutorial/mongodb/2_get_all_movies.png
Normal file
BIN
_examples/tutorial/mongodb/2_get_all_movies.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 102 KiB |
BIN
_examples/tutorial/mongodb/3_get_movie.png
Normal file
BIN
_examples/tutorial/mongodb/3_get_movie.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 83 KiB |
BIN
_examples/tutorial/mongodb/4_delete_movie.png
Normal file
BIN
_examples/tutorial/mongodb/4_delete_movie.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 48 KiB |
58
_examples/tutorial/mongodb/README.md
Normal file
58
_examples/tutorial/mongodb/README.md
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
# Build RESTful API with the official MongoDB Go Driver and Iris
|
||||||
|
|
||||||
|
Article is coming soon, follow and stay tuned
|
||||||
|
|
||||||
|
- <https://medium.com/@kataras>
|
||||||
|
- <https://dev.to/kataras>
|
||||||
|
|
||||||
|
Read [the fully functional example](main.go).
|
||||||
|
|
||||||
|
```sh
|
||||||
|
$ go get -u github.com/mongodb/mongo-go-driver
|
||||||
|
$ go get -u github.com/joho/godotenv
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# .env file contents
|
||||||
|
PORT=8080
|
||||||
|
DSN=mongodb://localhost:27017
|
||||||
|
```
|
||||||
|
|
||||||
|
```sh
|
||||||
|
$ go run main.go
|
||||||
|
> 2019/01/28 05:17:59 Loading environment variables from file: .env
|
||||||
|
> 2019/01/28 05:17:59 ◽ PORT=8080
|
||||||
|
> 2019/01/28 05:17:59 ◽ DSN=mongodb://localhost:27017
|
||||||
|
> Now listening on: http://localhost:8080
|
||||||
|
```
|
||||||
|
|
||||||
|
```sh
|
||||||
|
GET : http://localhost:8080/api/store/movies
|
||||||
|
POST : http://localhost:8080/api/store/movies
|
||||||
|
GET : http://localhost:8080/api/store/movies/{id}
|
||||||
|
PUT : http://localhost:8080/api/store/movies/{id}
|
||||||
|
DELETE : http://localhost:8080/api/store/movies/{id}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Screens
|
||||||
|
|
||||||
|
### Add a Movie
|
||||||
|
![](0_create_movie.png)
|
||||||
|
|
||||||
|
### Update a Movie
|
||||||
|
|
||||||
|
![](1_update_movie.png)
|
||||||
|
|
||||||
|
### Get all Movies
|
||||||
|
|
||||||
|
![](2_get_all_movies.png)
|
||||||
|
|
||||||
|
### Get a Movie by its ID
|
||||||
|
|
||||||
|
![](3_get_movie.png)
|
||||||
|
|
||||||
|
### Delete a Movie by its ID
|
||||||
|
|
||||||
|
![](4_delete_movie.png)
|
||||||
|
|
101
_examples/tutorial/mongodb/api/store/movie.go
Normal file
101
_examples/tutorial/mongodb/api/store/movie.go
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
package storeapi
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/kataras/iris/_examples/tutorial/mongodb/httputil"
|
||||||
|
"github.com/kataras/iris/_examples/tutorial/mongodb/store"
|
||||||
|
|
||||||
|
"github.com/kataras/iris"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MovieHandler struct {
|
||||||
|
service store.MovieService
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMovieHandler(service store.MovieService) *MovieHandler {
|
||||||
|
return &MovieHandler{service: service}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *MovieHandler) GetAll(ctx iris.Context) {
|
||||||
|
movies, err := h.service.GetAll(nil)
|
||||||
|
if err != nil {
|
||||||
|
httputil.InternalServerErrorJSON(ctx, err, "Server was unable to retrieve all movies")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if movies == nil {
|
||||||
|
// will return "null" if empty, with this "trick" we return "[]" json.
|
||||||
|
movies = make([]store.Movie, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.JSON(movies)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *MovieHandler) Get(ctx iris.Context) {
|
||||||
|
id := ctx.Params().Get("id")
|
||||||
|
|
||||||
|
m, err := h.service.GetByID(nil, id)
|
||||||
|
if err != nil {
|
||||||
|
if err == store.ErrNotFound {
|
||||||
|
ctx.NotFound()
|
||||||
|
} else {
|
||||||
|
httputil.InternalServerErrorJSON(ctx, err, "Server was unable to retrieve movie [%s]", id)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.JSON(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *MovieHandler) Add(ctx iris.Context) {
|
||||||
|
m := new(store.Movie)
|
||||||
|
|
||||||
|
err := ctx.ReadJSON(m)
|
||||||
|
if err != nil {
|
||||||
|
httputil.FailJSON(ctx, iris.StatusBadRequest, err, "Malformed request payload")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = h.service.Create(nil, m)
|
||||||
|
if err != nil {
|
||||||
|
httputil.InternalServerErrorJSON(ctx, err, "Server was unable to create a movie")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.StatusCode(iris.StatusCreated)
|
||||||
|
ctx.JSON(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *MovieHandler) Update(ctx iris.Context) {
|
||||||
|
id := ctx.Params().Get("id")
|
||||||
|
|
||||||
|
var m store.Movie
|
||||||
|
err := ctx.ReadJSON(&m)
|
||||||
|
if err != nil {
|
||||||
|
httputil.FailJSON(ctx, iris.StatusBadRequest, err, "Malformed request payload")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = h.service.Update(nil, id, m)
|
||||||
|
if err != nil {
|
||||||
|
if err == store.ErrNotFound {
|
||||||
|
ctx.NotFound()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
httputil.InternalServerErrorJSON(ctx, err, "Server was unable to update movie [%s]", id)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *MovieHandler) Delete(ctx iris.Context) {
|
||||||
|
id := ctx.Params().Get("id")
|
||||||
|
|
||||||
|
err := h.service.Delete(nil, id)
|
||||||
|
if err != nil {
|
||||||
|
if err == store.ErrNotFound {
|
||||||
|
ctx.NotFound()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
httputil.InternalServerErrorJSON(ctx, err, "Server was unable to delete movie [%s]", id)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
71
_examples/tutorial/mongodb/env/env.go
vendored
Normal file
71
_examples/tutorial/mongodb/env/env.go
vendored
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
package env
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/joho/godotenv"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// Port is the PORT environment variable or 8080 if missing.
|
||||||
|
// Used to open the tcp listener for our web server.
|
||||||
|
Port string
|
||||||
|
// DSN is the DSN environment variable or mongodb://localhost:27017 if missing.
|
||||||
|
// Used to connect to the mongodb.
|
||||||
|
DSN string
|
||||||
|
)
|
||||||
|
|
||||||
|
func parse() {
|
||||||
|
Port = getDefault("PORT", "8080")
|
||||||
|
DSN = getDefault("DSN", "mongodb://localhost:27017")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load loads environment variables that are being used across the whole app.
|
||||||
|
// Loading from file(s), i.e .env or dev.env
|
||||||
|
//
|
||||||
|
// Example of a 'dev.env':
|
||||||
|
// PORT=8080
|
||||||
|
// DSN=mongodb://localhost:27017
|
||||||
|
//
|
||||||
|
// After `Load` the callers can get an environment variable via `os.Getenv`.
|
||||||
|
func Load(envFileName string) {
|
||||||
|
if args := os.Args; len(args) > 1 && args[1] == "help" {
|
||||||
|
fmt.Fprintln(os.Stderr, "https://github.com/kataras/iris/blob/master/_examples/tutorials/mongodb/README.md")
|
||||||
|
os.Exit(-1)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("Loading environment variables from file: %s\n", envFileName)
|
||||||
|
// If more than one filename passed with comma separated then load from all
|
||||||
|
// of these, a env file can be a partial too.
|
||||||
|
envFiles := strings.Split(envFileName, ",")
|
||||||
|
for i := range envFiles {
|
||||||
|
if filepath.Ext(envFiles[i]) == "" {
|
||||||
|
envFiles[i] += ".env"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := godotenv.Load(envFiles...); err != nil {
|
||||||
|
panic(fmt.Sprintf("error loading environment variables from [%s]: %v", envFileName, err))
|
||||||
|
}
|
||||||
|
|
||||||
|
envMap, _ := godotenv.Read(envFiles...)
|
||||||
|
for k, v := range envMap {
|
||||||
|
log.Printf("◽ %s=%s\n", k, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
parse()
|
||||||
|
}
|
||||||
|
|
||||||
|
func getDefault(key string, def string) string {
|
||||||
|
value := os.Getenv(key)
|
||||||
|
if value == "" {
|
||||||
|
os.Setenv(key, def)
|
||||||
|
value = def
|
||||||
|
}
|
||||||
|
|
||||||
|
return value
|
||||||
|
}
|
130
_examples/tutorial/mongodb/httputil/error.go
Normal file
130
_examples/tutorial/mongodb/httputil/error.go
Normal file
|
@ -0,0 +1,130 @@
|
||||||
|
package httputil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"runtime"
|
||||||
|
"runtime/debug"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/kataras/iris"
|
||||||
|
)
|
||||||
|
|
||||||
|
var validStackFuncs = []func(string) bool{
|
||||||
|
func(file string) bool {
|
||||||
|
return strings.Contains(file, "/mongodb/api/")
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// RuntimeCallerStack returns the app's `file:line` stacktrace
|
||||||
|
// to give more information about an error cause.
|
||||||
|
func RuntimeCallerStack() (s string) {
|
||||||
|
var pcs [10]uintptr
|
||||||
|
n := runtime.Callers(1, pcs[:])
|
||||||
|
frames := runtime.CallersFrames(pcs[:n])
|
||||||
|
|
||||||
|
for {
|
||||||
|
frame, more := frames.Next()
|
||||||
|
for _, fn := range validStackFuncs {
|
||||||
|
if fn(frame.File) {
|
||||||
|
s += fmt.Sprintf("\n\t\t\t%s:%d", frame.File, frame.Line)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !more {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// HTTPError describes an HTTP error.
|
||||||
|
type HTTPError struct {
|
||||||
|
error
|
||||||
|
Stack string `json:"-"` // the whole stacktrace.
|
||||||
|
CallerStack string `json:"-"` // the caller, file:lineNumber
|
||||||
|
When time.Time `json:"-"` // the time that the error occurred.
|
||||||
|
// ErrorCode int: maybe a collection of known error codes.
|
||||||
|
StatusCode int `json:"statusCode"`
|
||||||
|
// could be named as "reason" as well
|
||||||
|
// it's the message of the error.
|
||||||
|
Description string `json:"description"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func newError(statusCode int, err error, format string, args ...interface{}) HTTPError {
|
||||||
|
if format == "" {
|
||||||
|
format = http.StatusText(statusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
desc := fmt.Sprintf(format, args...)
|
||||||
|
if err == nil {
|
||||||
|
err = errors.New(desc)
|
||||||
|
}
|
||||||
|
|
||||||
|
return HTTPError{
|
||||||
|
err,
|
||||||
|
string(debug.Stack()),
|
||||||
|
RuntimeCallerStack(),
|
||||||
|
time.Now(),
|
||||||
|
statusCode,
|
||||||
|
desc,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err HTTPError) writeHeaders(ctx iris.Context) {
|
||||||
|
ctx.StatusCode(err.StatusCode)
|
||||||
|
ctx.Header("X-Content-Type-Options", "nosniff")
|
||||||
|
}
|
||||||
|
|
||||||
|
// LogFailure will print out the failure to the "logger".
|
||||||
|
func LogFailure(logger io.Writer, ctx iris.Context, err HTTPError) {
|
||||||
|
timeFmt := err.When.Format("2006/01/02 15:04:05")
|
||||||
|
firstLine := fmt.Sprintf("%s %s: %s", timeFmt, http.StatusText(err.StatusCode), err.Error())
|
||||||
|
whitespace := strings.Repeat(" ", len(timeFmt)+1)
|
||||||
|
fmt.Fprintf(logger, "%s\n%sIP: %s\n%sURL: %s\n%sSource: %s\n",
|
||||||
|
firstLine, whitespace, ctx.RemoteAddr(), whitespace, ctx.FullRequestURI(), whitespace, err.CallerStack)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fail will send the status code, write the error's reason
|
||||||
|
// and return the HTTPError for further use, i.e logging, see `InternalServerError`.
|
||||||
|
func Fail(ctx iris.Context, statusCode int, err error, format string, args ...interface{}) HTTPError {
|
||||||
|
httpErr := newError(statusCode, err, format, args...)
|
||||||
|
httpErr.writeHeaders(ctx)
|
||||||
|
|
||||||
|
ctx.WriteString(httpErr.Description)
|
||||||
|
return httpErr
|
||||||
|
}
|
||||||
|
|
||||||
|
// FailJSON will send to the client the error data as JSON.
|
||||||
|
// Useful for APIs.
|
||||||
|
func FailJSON(ctx iris.Context, statusCode int, err error, format string, args ...interface{}) HTTPError {
|
||||||
|
httpErr := newError(statusCode, err, format, args...)
|
||||||
|
httpErr.writeHeaders(ctx)
|
||||||
|
|
||||||
|
ctx.JSON(httpErr)
|
||||||
|
|
||||||
|
return httpErr
|
||||||
|
}
|
||||||
|
|
||||||
|
// InternalServerError logs to the server's terminal
|
||||||
|
// and dispatches to the client the 500 Internal Server Error.
|
||||||
|
// Internal Server errors are critical, so we log them to the `os.Stderr`.
|
||||||
|
func InternalServerError(ctx iris.Context, err error, format string, args ...interface{}) {
|
||||||
|
LogFailure(os.Stderr, ctx, Fail(ctx, iris.StatusInternalServerError, err, format, args...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// InternalServerErrorJSON acts exactly like `InternalServerError` but instead it sends the data as JSON.
|
||||||
|
// Useful for APIs.
|
||||||
|
func InternalServerErrorJSON(ctx iris.Context, err error, format string, args ...interface{}) {
|
||||||
|
LogFailure(os.Stderr, ctx, FailJSON(ctx, iris.StatusInternalServerError, err, format, args...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnauthorizedJSON sends JSON format of StatusUnauthorized(401) HTTPError value.
|
||||||
|
func UnauthorizedJSON(ctx iris.Context, err error, format string, args ...interface{}) HTTPError {
|
||||||
|
return FailJSON(ctx, iris.StatusUnauthorized, err, format, args...)
|
||||||
|
}
|
81
_examples/tutorial/mongodb/main.go
Normal file
81
_examples/tutorial/mongodb/main.go
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
// go get -u github.com/mongodb/mongo-go-driver
|
||||||
|
// go get -u github.com/joho/godotenv
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
// APIs
|
||||||
|
storeapi "github.com/kataras/iris/_examples/tutorial/mongodb/api/store"
|
||||||
|
|
||||||
|
//
|
||||||
|
"github.com/kataras/iris/_examples/tutorial/mongodb/env"
|
||||||
|
"github.com/kataras/iris/_examples/tutorial/mongodb/store"
|
||||||
|
|
||||||
|
"github.com/kataras/iris"
|
||||||
|
|
||||||
|
"github.com/mongodb/mongo-go-driver/mongo"
|
||||||
|
)
|
||||||
|
|
||||||
|
const version = "0.0.1"
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
var envFileName = ".env"
|
||||||
|
|
||||||
|
flagset := flag.CommandLine
|
||||||
|
flagset.StringVar(&envFileName, "env", envFileName, "the env file which web app will use to extract its environment variables")
|
||||||
|
flag.CommandLine.Parse(os.Args[1:])
|
||||||
|
|
||||||
|
env.Load(envFileName)
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
client, err := mongo.Connect(context.Background(), env.DSN)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = client.Ping(context.Background(), nil)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
defer client.Disconnect(context.TODO())
|
||||||
|
|
||||||
|
db := client.Database("store")
|
||||||
|
|
||||||
|
var (
|
||||||
|
// Collections.
|
||||||
|
moviesCollection = db.Collection("movies")
|
||||||
|
|
||||||
|
// Services.
|
||||||
|
movieService = store.NewMovieService(moviesCollection)
|
||||||
|
)
|
||||||
|
|
||||||
|
app := iris.New()
|
||||||
|
app.Use(func(ctx iris.Context) {
|
||||||
|
ctx.Header("Server", "Iris MongoDB/"+version)
|
||||||
|
ctx.Next()
|
||||||
|
})
|
||||||
|
|
||||||
|
storeAPI := app.Party("/api/store")
|
||||||
|
{
|
||||||
|
movieHandler := storeapi.NewMovieHandler(movieService)
|
||||||
|
storeAPI.Get("/movies", movieHandler.GetAll)
|
||||||
|
storeAPI.Post("/movies", movieHandler.Add)
|
||||||
|
storeAPI.Get("/movies/{id}", movieHandler.Get)
|
||||||
|
storeAPI.Put("/movies/{id}", movieHandler.Update)
|
||||||
|
storeAPI.Delete("/movies/{id}", movieHandler.Delete)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GET: http://localhost:8080/api/store/movies
|
||||||
|
// POST: http://localhost:8080/api/store/movies
|
||||||
|
// GET: http://localhost:8080/api/store/movies/{id}
|
||||||
|
// PUT: http://localhost:8080/api/store/movies/{id}
|
||||||
|
// DELETE: http://localhost:8080/api/store/movies/{id}
|
||||||
|
app.Run(iris.Addr(fmt.Sprintf(":%s", env.Port)), iris.WithOptimizations)
|
||||||
|
}
|
180
_examples/tutorial/mongodb/store/movie.go
Normal file
180
_examples/tutorial/mongodb/store/movie.go
Normal file
|
@ -0,0 +1,180 @@
|
||||||
|
package store
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"github.com/mongodb/mongo-go-driver/bson"
|
||||||
|
"github.com/mongodb/mongo-go-driver/bson/primitive"
|
||||||
|
"github.com/mongodb/mongo-go-driver/mongo"
|
||||||
|
// up to you:
|
||||||
|
// "github.com/mongodb/mongo-go-driver/mongo/options"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Movie struct {
|
||||||
|
ID primitive.ObjectID `json:"_id" bson:"_id"` /* you need the bson:"_id" to be able to retrieve with ID filled */
|
||||||
|
Name string `json:"name"`
|
||||||
|
Cover string `json:"cover"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type MovieService interface {
|
||||||
|
GetAll(ctx context.Context) ([]Movie, error)
|
||||||
|
GetByID(ctx context.Context, id string) (Movie, error)
|
||||||
|
Create(ctx context.Context, m *Movie) error
|
||||||
|
Update(ctx context.Context, id string, m Movie) error
|
||||||
|
Delete(ctx context.Context, id string) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type movieService struct {
|
||||||
|
C *mongo.Collection
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ MovieService = (*movieService)(nil)
|
||||||
|
|
||||||
|
func NewMovieService(collection *mongo.Collection) MovieService {
|
||||||
|
// up to you:
|
||||||
|
// indexOpts := new(options.IndexOptions)
|
||||||
|
// indexOpts.SetName("movieIndex").
|
||||||
|
// SetUnique(true).
|
||||||
|
// SetBackground(true).
|
||||||
|
// SetSparse(true)
|
||||||
|
|
||||||
|
// collection.Indexes().CreateOne(context.Background(), mongo.IndexModel{
|
||||||
|
// Keys: []string{"_id", "name"},
|
||||||
|
// Options: indexOpts,
|
||||||
|
// })
|
||||||
|
|
||||||
|
return &movieService{C: collection}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *movieService) GetAll(ctx context.Context) ([]Movie, error) {
|
||||||
|
// Note:
|
||||||
|
// The mongodb's go-driver's docs says that you can pass `nil` to "find all" but this gives NilDocument error,
|
||||||
|
// probably it's a bug or a documentation's mistake, you have to pass `bson.D{}` instead.
|
||||||
|
cur, err := s.C.Find(ctx, bson.D{})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer cur.Close(ctx)
|
||||||
|
|
||||||
|
var results []Movie
|
||||||
|
|
||||||
|
for cur.Next(ctx) {
|
||||||
|
if err = cur.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// elem := bson.D{}
|
||||||
|
var elem Movie
|
||||||
|
err = cur.Decode(&elem)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// results = append(results, Movie{ID: elem[0].Value.(primitive.ObjectID)})
|
||||||
|
|
||||||
|
results = append(results, elem)
|
||||||
|
}
|
||||||
|
|
||||||
|
return results, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func matchID(id string) (bson.D, error) {
|
||||||
|
objectID, err := primitive.ObjectIDFromHex(id)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
filter := bson.D{{Key: "_id", Value: objectID}}
|
||||||
|
return filter, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var ErrNotFound = errors.New("not found")
|
||||||
|
|
||||||
|
func (s *movieService) GetByID(ctx context.Context, id string) (Movie, error) {
|
||||||
|
var movie Movie
|
||||||
|
filter, err := matchID(id)
|
||||||
|
if err != nil {
|
||||||
|
return movie, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = s.C.FindOne(ctx, filter).Decode(&movie)
|
||||||
|
if err == mongo.ErrNoDocuments {
|
||||||
|
return movie, ErrNotFound
|
||||||
|
}
|
||||||
|
return movie, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *movieService) Create(ctx context.Context, m *Movie) error {
|
||||||
|
if m.ID.IsZero() {
|
||||||
|
m.ID = primitive.NewObjectID()
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := s.C.InsertOne(ctx, m)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// The following doesn't work if you have the `bson:"_id` on Movie.ID field,
|
||||||
|
// therefore we manually generate a new ID (look above).
|
||||||
|
// res, err := ...InsertOne
|
||||||
|
// objectID := res.InsertedID.(primitive.ObjectID)
|
||||||
|
// m.ID = objectID
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *movieService) Update(ctx context.Context, id string, m Movie) error {
|
||||||
|
filter, err := matchID(id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// update := bson.D{
|
||||||
|
// {Key: "$set", Value: m},
|
||||||
|
// }
|
||||||
|
// ^ this will override all fields, you can do that, depending on your design. but let's check each field:
|
||||||
|
elem := bson.D{}
|
||||||
|
|
||||||
|
if m.Name != "" {
|
||||||
|
elem = append(elem, bson.E{Key: "name", Value: m.Name})
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.Description != "" {
|
||||||
|
elem = append(elem, bson.E{Key: "description", Value: m.Description})
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.Cover != "" {
|
||||||
|
elem = append(elem, bson.E{Key: "cover", Value: m.Cover})
|
||||||
|
}
|
||||||
|
|
||||||
|
update := bson.D{
|
||||||
|
{Key: "$set", Value: elem},
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = s.C.UpdateOne(ctx, filter, update)
|
||||||
|
if err != nil {
|
||||||
|
if err == mongo.ErrNoDocuments {
|
||||||
|
return ErrNotFound
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *movieService) Delete(ctx context.Context, id string) error {
|
||||||
|
filter, err := matchID(id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = s.C.DeleteOne(ctx, filter)
|
||||||
|
if err != nil {
|
||||||
|
if err == mongo.ErrNoDocuments {
|
||||||
|
return ErrNotFound
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
7
doc.go
7
doc.go
|
@ -27,7 +27,10 @@
|
||||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Package iris provides a beautifully expressive and easy to use foundation for your next website, API, or distributed app.
|
Package iris implements the highest realistic performance, easy to learn Go web framework.
|
||||||
|
Iris provides a beautifully expressive and easy to use foundation for your next website, API, or distributed app.
|
||||||
|
Low-level handlers compatible with `net/http` and high-level fastest MVC implementation and handlers dependency injection.
|
||||||
|
Easy to learn for new gophers and advanced features for experienced, it goes as far as you dive into it!
|
||||||
|
|
||||||
Source code and other details for the project are available at GitHub:
|
Source code and other details for the project are available at GitHub:
|
||||||
|
|
||||||
|
@ -35,7 +38,7 @@ Source code and other details for the project are available at GitHub:
|
||||||
|
|
||||||
Current Version
|
Current Version
|
||||||
|
|
||||||
11.1.1
|
11.2.0
|
||||||
|
|
||||||
Installation
|
Installation
|
||||||
|
|
||||||
|
|
2
iris.go
2
iris.go
|
@ -33,7 +33,7 @@ import (
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// Version is the current version number of the Iris Web Framework.
|
// Version is the current version number of the Iris Web Framework.
|
||||||
Version = "11.1.1"
|
Version = "11.2.0"
|
||||||
)
|
)
|
||||||
|
|
||||||
// HTTP status codes as registered with IANA.
|
// HTTP status codes as registered with IANA.
|
||||||
|
|
Loading…
Reference in New Issue
Block a user