mirror of
https://github.com/kataras/iris.git
synced 2026-08-06 20:48:58 +00:00
last version of v12
This commit is contained in:
@@ -0,0 +1,121 @@
|
||||
// Package main shows how to use a dependency to check if a user is logged in
|
||||
// using a special custom Go type `Authenticated`, which when,
|
||||
// present on a controller's method or a field then
|
||||
// it limits the visibility to "authenticated" users only.
|
||||
//
|
||||
// The same result could be done through a middleware as well, however using a static type
|
||||
// any person reads your code can see that an "X" controller or method
|
||||
// should only be used by "authenticated" users.
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/kataras/iris/v12"
|
||||
"github.com/kataras/iris/v12/mvc"
|
||||
"github.com/kataras/iris/v12/sessions"
|
||||
)
|
||||
|
||||
func main() {
|
||||
app := newApp()
|
||||
// app.UseRouter(iris.Compression)
|
||||
app.Logger().SetLevel("debug")
|
||||
|
||||
// Open a client, e.g. Postman and visit the below endpoints.
|
||||
// GET: http://localhost:8080/user (UnauthenticatedUserController.Get)
|
||||
// POST: http://localhost:8080/user/login (UnauthenticatedUserController.PostLogin)
|
||||
// GET: http://localhost:8080/user (UserController.Get)
|
||||
// POST: http://localhost:8080/user/logout (UserController.PostLogout)
|
||||
app.Listen(":8080")
|
||||
}
|
||||
|
||||
func newApp() *iris.Application {
|
||||
app := iris.New()
|
||||
sess := sessions.New(sessions.Config{
|
||||
Cookie: "myapp_session_id",
|
||||
AllowReclaim: true,
|
||||
})
|
||||
app.Use(sess.Handler())
|
||||
|
||||
userRouter := app.Party("/user")
|
||||
{
|
||||
// Use that in order to be able to register a route twice,
|
||||
// last one will be executed if the previous route's handler(s) stopped and the response can be reset-ed.
|
||||
// See core/router/route_register_rule_test.go#TestRegisterRuleOverlap.
|
||||
userRouter.SetRegisterRule(iris.RouteOverlap)
|
||||
|
||||
// Initialize a new MVC application on top of the "userRouter".
|
||||
userApp := mvc.New(userRouter)
|
||||
// Register Dependencies.
|
||||
userApp.Register(authDependency)
|
||||
|
||||
// Register Controllers.
|
||||
userApp.Handle(new(UserController))
|
||||
userApp.Handle(new(UnauthenticatedUserController))
|
||||
}
|
||||
|
||||
return app
|
||||
}
|
||||
|
||||
// Authenticated is a custom type used as "annotation" for resources that requires authentication,
|
||||
// its value should be the logged user ID.
|
||||
type Authenticated uint64
|
||||
|
||||
func authDependency(ctx iris.Context, session *sessions.Session) Authenticated {
|
||||
userID := session.GetUint64Default("user_id", 0)
|
||||
if userID == 0 {
|
||||
// If execution was stopped
|
||||
// any controller's method will not be executed at all.
|
||||
//
|
||||
// Note that, the below will not fire the error to the user:
|
||||
// ctx.StopWithStatus(iris.StatusUnauthorized)
|
||||
// because of the imaginary:
|
||||
// UnauthenticatedUserController.Get() (string, int) {
|
||||
// return "...", iris.StatusOK
|
||||
// }
|
||||
//
|
||||
// OR
|
||||
// If you don't want to set a status code at all:
|
||||
ctx.StopExecution()
|
||||
return 0
|
||||
}
|
||||
|
||||
return Authenticated(userID)
|
||||
}
|
||||
|
||||
// UnauthenticatedUserController serves the "public" Unauthorized User API.
|
||||
type UnauthenticatedUserController struct{}
|
||||
|
||||
// Get registers a route that will be executed when authentication is not passed
|
||||
// (see UserController.Get) too.
|
||||
func (c *UnauthenticatedUserController) Get() string {
|
||||
return "custom action to redirect on authentication page"
|
||||
}
|
||||
|
||||
// PostLogin serves
|
||||
// POST: /user/login
|
||||
func (c *UnauthenticatedUserController) PostLogin(session *sessions.Session) mvc.Response {
|
||||
session.Set("user_id", 1)
|
||||
|
||||
// Redirect (you can still use the Context.Redirect if you want so).
|
||||
return mvc.Response{
|
||||
Path: "/user",
|
||||
Code: iris.StatusFound,
|
||||
}
|
||||
}
|
||||
|
||||
// UserController serves the "public" User API.
|
||||
type UserController struct {
|
||||
CurrentUserID Authenticated
|
||||
}
|
||||
|
||||
// Get returns a message for the sake of the example.
|
||||
// GET: /user
|
||||
func (c *UserController) Get() string {
|
||||
return `UserController.Get: The Authenticated type
|
||||
can be used to secure a controller's method too.`
|
||||
}
|
||||
|
||||
// PostLogout serves
|
||||
// POST: /user/logout
|
||||
func (c *UserController) PostLogout(ctx iris.Context) {
|
||||
sessions.Get(ctx).Man.Destroy(ctx)
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/kataras/iris/v12/httptest"
|
||||
)
|
||||
|
||||
func TestMVCOverlapping(t *testing.T) {
|
||||
app := newApp()
|
||||
|
||||
e := httptest.New(t, app, httptest.URL("http://example.com"))
|
||||
// unauthenticated.
|
||||
e.GET("/user").Expect().Status(httptest.StatusOK).Body().IsEqual("custom action to redirect on authentication page")
|
||||
// login.
|
||||
e.POST("/user/login").Expect().Status(httptest.StatusOK)
|
||||
// authenticated.
|
||||
e.GET("/user").Expect().Status(httptest.StatusOK).Body().IsEqual(`UserController.Get: The Authenticated type
|
||||
can be used to secure a controller's method too.`)
|
||||
// logout.
|
||||
e.POST("/user/logout").Expect().Status(httptest.StatusOK)
|
||||
// unauthenticated.
|
||||
e.GET("/user").Expect().Status(httptest.StatusOK).Body().IsEqual("custom action to redirect on authentication page")
|
||||
}
|
||||
+27
-11
@@ -4,6 +4,7 @@ import (
|
||||
"fmt"
|
||||
|
||||
"github.com/kataras/iris/v12"
|
||||
"github.com/kataras/iris/v12/middleware/accesslog"
|
||||
"github.com/kataras/iris/v12/middleware/recover"
|
||||
"github.com/kataras/iris/v12/sessions"
|
||||
|
||||
@@ -12,19 +13,32 @@ import (
|
||||
|
||||
func main() {
|
||||
app := iris.New()
|
||||
app.Use(recover.New())
|
||||
app.Logger().SetLevel("debug")
|
||||
mvc.Configure(app.Party("/basic"), basicMVC)
|
||||
|
||||
app.Run(iris.Addr(":8080"))
|
||||
basic := app.Party("/basic")
|
||||
{
|
||||
// Register middlewares to run under the /basic path prefix.
|
||||
ac := accesslog.File("./basic_access.log")
|
||||
defer ac.Close()
|
||||
|
||||
basic.UseRouter(ac.Handler)
|
||||
basic.UseRouter(recover.New())
|
||||
|
||||
mvc.Configure(basic, basicMVC)
|
||||
}
|
||||
|
||||
app.Listen(":8080")
|
||||
}
|
||||
|
||||
func basicMVC(app *mvc.Application) {
|
||||
// You can use normal middlewares at MVC apps of course.
|
||||
app.Router.Use(func(ctx iris.Context) {
|
||||
ctx.Application().Logger().Infof("Path: %s", ctx.Path())
|
||||
ctx.Next()
|
||||
})
|
||||
// Disable verbose logging of controllers for this and its children mvc apps
|
||||
// when the log level is "debug":
|
||||
app.SetControllersNoLog(true)
|
||||
|
||||
// You can still register middlewares at MVC apps of course.
|
||||
// The app.Router returns the Party that this MVC
|
||||
// was registered on.
|
||||
// app.Router.UseRouter/Use/....
|
||||
|
||||
// Register dependencies which will be binding to the controller(s),
|
||||
// can be either a function which accepts an iris.Context and returns a single value (dynamic binding)
|
||||
@@ -32,6 +46,7 @@ func basicMVC(app *mvc.Application) {
|
||||
app.Register(
|
||||
sessions.New(sessions.Config{}).Start,
|
||||
&prefixedLogger{prefix: "DEV"},
|
||||
accesslog.GetFields, // Set custom fields through a controller or controller's methods.
|
||||
)
|
||||
|
||||
// GET: http://localhost:8080/basic
|
||||
@@ -68,9 +83,9 @@ func (s *prefixedLogger) Log(msg string) {
|
||||
}
|
||||
|
||||
type basicController struct {
|
||||
Logger LoggerService
|
||||
|
||||
Session *sessions.Session
|
||||
Logger LoggerService // the static logger service attached to this app.
|
||||
Session *sessions.Session // current HTTP session.
|
||||
LogFields *accesslog.Fields // accesslog middleware custom fields.
|
||||
}
|
||||
|
||||
func (c *basicController) BeforeActivation(b mvc.BeforeActivation) {
|
||||
@@ -85,6 +100,7 @@ func (c *basicController) AfterActivation(a mvc.AfterActivation) {
|
||||
|
||||
func (c *basicController) Get() string {
|
||||
count := c.Session.Increment("count", 1)
|
||||
c.LogFields.Set("count", count)
|
||||
|
||||
body := fmt.Sprintf("Hello from basicController\nTotal visits from you: %d", count)
|
||||
c.Logger.Log(body)
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/kataras/iris/v12"
|
||||
"github.com/kataras/iris/v12/mvc"
|
||||
)
|
||||
|
||||
func main() {
|
||||
app := iris.New()
|
||||
usersRouter := app.Party("/users")
|
||||
mvc.New(usersRouter).Handle(new(myController))
|
||||
// Same as:
|
||||
// usersRouter.Get("/{p:path}", func(ctx iris.Context) {
|
||||
// wildcardPathParameter := ctx.Params().Get("p")
|
||||
// ctx.JSON(response{
|
||||
// Message: "The path parameter is: " + wildcardPathParameter,
|
||||
// })
|
||||
// })
|
||||
|
||||
/*
|
||||
curl --location --request GET 'http://localhost:8080/users/path_segment_1/path_segment_2'
|
||||
|
||||
Expected Output:
|
||||
{
|
||||
"message": "The wildcard is: path_segment_1/path_segment_2"
|
||||
}
|
||||
*/
|
||||
app.Listen(":8080")
|
||||
}
|
||||
|
||||
type myController struct{}
|
||||
|
||||
type response struct {
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
func (c *myController) GetByWildcard(wildcardPathParameter string) response {
|
||||
return response{
|
||||
Message: "The path parameter is: " + wildcardPathParameter,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/kataras/iris/v12"
|
||||
"github.com/kataras/iris/v12/mvc"
|
||||
)
|
||||
|
||||
func main() {
|
||||
app := iris.New()
|
||||
app.RegisterView(iris.HTML("./views", ".html"))
|
||||
|
||||
m := mvc.New(app)
|
||||
m.Handle(new(controller))
|
||||
|
||||
app.Listen(":8080")
|
||||
}
|
||||
|
||||
type errorResponse struct {
|
||||
Code int
|
||||
Message string
|
||||
}
|
||||
|
||||
/*
|
||||
// Note: if a struct implements the standard go error, so it's an error
|
||||
// and its Error() is not empty, then its text will be rendered instead,
|
||||
// override any Dispatch method.
|
||||
func (e errorResponse) Error() string {
|
||||
return e.Message
|
||||
}
|
||||
*/
|
||||
|
||||
// implements mvc.Result.
|
||||
func (e errorResponse) Dispatch(ctx iris.Context) {
|
||||
// If u want to use mvc.Result on any method without an output return value
|
||||
// go for it:
|
||||
//
|
||||
view := mvc.View{Code: e.Code, Data: e} // use Code and Message as the template data.
|
||||
switch e.Code {
|
||||
case iris.StatusNotFound:
|
||||
view.Name = "404"
|
||||
default:
|
||||
view.Name = "500"
|
||||
}
|
||||
view.Dispatch(ctx)
|
||||
|
||||
// Otherwise use ctx methods:
|
||||
//
|
||||
// ctx.StatusCode(e.Code)
|
||||
// switch e.Code {
|
||||
// case iris.StatusNotFound:
|
||||
// // use Code and Message as the template data.
|
||||
// if err := ctx.View("404.html", e)
|
||||
// default:
|
||||
// if err := ctx.View("500.html", e)
|
||||
// }
|
||||
}
|
||||
|
||||
type controller struct{}
|
||||
|
||||
type user struct {
|
||||
ID uint64 `json:"id"`
|
||||
}
|
||||
|
||||
func (c *controller) GetBy(userid uint64) mvc.Result {
|
||||
if userid != 1 {
|
||||
return errorResponse{
|
||||
Code: iris.StatusNotFound,
|
||||
Message: "User Not Found",
|
||||
}
|
||||
}
|
||||
|
||||
return mvc.Response{
|
||||
Object: user{ID: userid},
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Client Error Page</title>
|
||||
</head>
|
||||
<body>
|
||||
<h2>{{.Code}}</h2>
|
||||
<h3>{{.Message}}</h3>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,12 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Server Error Page</title>
|
||||
</head>
|
||||
<body>
|
||||
<h2>{{.Code}}</h2>
|
||||
<h3>{{.Message}}</h3>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,60 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/kataras/iris/v12"
|
||||
"github.com/kataras/iris/v12/mvc"
|
||||
)
|
||||
|
||||
func main() {
|
||||
app := iris.New()
|
||||
app.RegisterView(iris.HTML("./views", ".html"))
|
||||
|
||||
// Hijack each output value of a method (can be used per-party too).
|
||||
app.ConfigureContainer().
|
||||
UseResultHandler(func(next iris.ResultHandler) iris.ResultHandler {
|
||||
return func(ctx iris.Context, v interface{}) error {
|
||||
switch val := v.(type) {
|
||||
case errorResponse:
|
||||
return next(ctx, errorView(val))
|
||||
default:
|
||||
return next(ctx, v)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
m := mvc.New(app)
|
||||
m.Handle(new(controller))
|
||||
|
||||
app.Listen(":8080")
|
||||
}
|
||||
|
||||
func errorView(e errorResponse) mvc.Result {
|
||||
switch e.Code {
|
||||
case iris.StatusNotFound:
|
||||
return mvc.View{Code: e.Code, Name: "404.html", Data: e}
|
||||
default:
|
||||
return mvc.View{Code: e.Code, Name: "500.html", Data: e}
|
||||
}
|
||||
}
|
||||
|
||||
type errorResponse struct {
|
||||
Code int
|
||||
Message string
|
||||
}
|
||||
|
||||
type controller struct{}
|
||||
|
||||
type user struct {
|
||||
ID uint64 `json:"id"`
|
||||
}
|
||||
|
||||
func (c *controller) GetBy(userid uint64) interface{} {
|
||||
if userid != 1 {
|
||||
return errorResponse{
|
||||
Code: iris.StatusNotFound,
|
||||
Message: "User Not Found",
|
||||
}
|
||||
}
|
||||
|
||||
return user{ID: userid}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Client Error Page</title>
|
||||
</head>
|
||||
<body>
|
||||
<h2>{{.Code}}</h2>
|
||||
<h3>{{.Message}}</h3>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,12 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Server Error Page</title>
|
||||
</head>
|
||||
<body>
|
||||
<h2>{{.Code}}</h2>
|
||||
<h3>{{.Message}}</h3>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,74 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/kataras/iris/v12"
|
||||
"github.com/kataras/iris/v12/mvc"
|
||||
)
|
||||
|
||||
func main() {
|
||||
app := newApp()
|
||||
app.Logger().SetLevel("debug")
|
||||
app.Listen(":8080")
|
||||
}
|
||||
|
||||
func newApp() *iris.Application {
|
||||
app := iris.New()
|
||||
app.RegisterView(iris.HTML("./views", ".html"))
|
||||
|
||||
m := mvc.New(app)
|
||||
m.Handle(new(controller))
|
||||
|
||||
return app
|
||||
}
|
||||
|
||||
type controller struct{}
|
||||
|
||||
func (c *controller) Get() string {
|
||||
return "Hello!"
|
||||
}
|
||||
|
||||
func (c *controller) GetError() mvc.Result {
|
||||
return mvc.View{
|
||||
// Map to mvc.Code and mvc.Err respectfully on HandleHTTPError method.
|
||||
Code: iris.StatusBadRequest,
|
||||
Err: fmt.Errorf("custom error"),
|
||||
}
|
||||
}
|
||||
|
||||
// The input parameter of mvc.Code is optional but a good practise to follow.
|
||||
// You could register a Context and get its error code through ctx.GetStatusCode().
|
||||
//
|
||||
// This can accept dependencies and output values like any other Controller Method,
|
||||
// however be careful if your registered dependencies depend only on successful(200...) requests.
|
||||
//
|
||||
// Also note that, if you register more than one controller.HandleHTTPError
|
||||
// in the same Party, you need to use the RouteOverlap feature as shown
|
||||
// in the "authenticated-controller" example, and a dependency on
|
||||
// a controller's field (or method's input argument) is required
|
||||
// to select which, between those two controllers, is responsible
|
||||
// to handle http errors.
|
||||
func (c *controller) HandleHTTPError(statusCode mvc.Code, err mvc.Err) mvc.View {
|
||||
if err != nil {
|
||||
// Do something with that error,
|
||||
// e.g. view.Data = MyPageData{Message: err.Error()}
|
||||
}
|
||||
|
||||
code := int(statusCode) // cast it to int.
|
||||
|
||||
view := mvc.View{
|
||||
Code: code,
|
||||
Name: "unexpected-error.html",
|
||||
}
|
||||
|
||||
switch code {
|
||||
case 404:
|
||||
view.Name = "404.html"
|
||||
// [...]
|
||||
case 500:
|
||||
view.Name = "500.html"
|
||||
}
|
||||
|
||||
return view
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/kataras/iris/v12/httptest"
|
||||
)
|
||||
|
||||
func TestControllerHandleHTTPError(t *testing.T) {
|
||||
const (
|
||||
expectedIndex = "Hello!"
|
||||
expectedNotFound = "<h3>Not Found Custom Page Rendered through Controller's HandleHTTPError</h3>"
|
||||
)
|
||||
|
||||
app := newApp()
|
||||
|
||||
e := httptest.New(t, app)
|
||||
e.GET("/").Expect().Status(httptest.StatusOK).Body().IsEqual(expectedIndex)
|
||||
e.GET("/a_notefound").Expect().Status(httptest.StatusNotFound).ContentType("text/html").Body().IsEqual(expectedNotFound)
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
<h3>Not Found Custom Page Rendered through Controller's HandleHTTPError</h3>
|
||||
@@ -0,0 +1 @@
|
||||
<h3>Internal Server Err</h3>
|
||||
@@ -0,0 +1 @@
|
||||
<h1>Unexpected Error</h1>
|
||||
@@ -0,0 +1,101 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/kataras/iris/v12"
|
||||
"github.com/kataras/iris/v12/mvc"
|
||||
)
|
||||
|
||||
func main() {
|
||||
app := iris.New()
|
||||
app.RegisterView(iris.HTML("./views", ".html"))
|
||||
|
||||
m := mvc.New(app)
|
||||
m.Handle(new(controller))
|
||||
|
||||
app.Listen(":8080")
|
||||
}
|
||||
|
||||
type controller struct{}
|
||||
|
||||
// Generic response type for JSON results.
|
||||
type response struct {
|
||||
ID uint64 `json:"id,omitempty"`
|
||||
Data interface{} `json:"data,omitempty"` // {data: result } on fetch actions.
|
||||
Code int `json:"code,omitempty"`
|
||||
Message string `json:"message,omitempty"`
|
||||
Timestamp int64 `json:"timestamp,omitempty"`
|
||||
}
|
||||
|
||||
func (r *response) Preflight(ctx iris.Context) error {
|
||||
if r.ID > 0 {
|
||||
r.Timestamp = time.Now().Unix()
|
||||
}
|
||||
|
||||
if code := r.Code; code > 0 {
|
||||
// You can call ctx.View or mvc.View{...}.Dispatch
|
||||
// to render HTML on Code != 200
|
||||
// but in order to not proceed with the response resulting
|
||||
// as JSON you MUST return the iris.ErrStopExecution error.
|
||||
// Example:
|
||||
if code != 200 {
|
||||
mvc.View{
|
||||
/* calls the ctx.StatusCode */
|
||||
Code: code,
|
||||
/* use any r.Data as the template data
|
||||
OR the whole "response" as its data. */
|
||||
Data: r,
|
||||
/* automatically pick the template per error (just for the sake of the example) */
|
||||
Name: fmt.Sprintf("%d", code),
|
||||
}.Dispatch(ctx)
|
||||
|
||||
return iris.ErrStopExecution
|
||||
}
|
||||
|
||||
ctx.StatusCode(r.Code)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type user struct {
|
||||
ID uint64 `json:"id"`
|
||||
}
|
||||
|
||||
func (c *controller) GetBy(userid uint64) *response {
|
||||
if userid != 1 {
|
||||
return &response{
|
||||
Code: iris.StatusNotFound,
|
||||
Message: "User Not Found",
|
||||
}
|
||||
}
|
||||
|
||||
return &response{
|
||||
ID: userid,
|
||||
Data: user{ID: userid},
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
You can use that `response` structure on non-mvc applications too, using handlers:
|
||||
|
||||
c := app.ConfigureContainer()
|
||||
c.Get("/{id:uint64}", getUserByID)
|
||||
func getUserByID(id uint64) response {
|
||||
if userid != 1 {
|
||||
return response{
|
||||
Code: iris.StatusNotFound,
|
||||
Message: "User Not Found",
|
||||
}
|
||||
}
|
||||
|
||||
return response{
|
||||
ID: userid,
|
||||
Data: user{ID: userid},
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
@@ -0,0 +1,12 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Client Error Page</title>
|
||||
</head>
|
||||
<body>
|
||||
<h2>{{.Code}}</h2>
|
||||
<h3>{{.Message}}</h3>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,12 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Server Error Page</title>
|
||||
</head>
|
||||
<body>
|
||||
<h2>{{.Code}}</h2>
|
||||
<h3>{{.Message}}</h3>
|
||||
</body>
|
||||
</html>
|
||||
@@ -23,7 +23,7 @@ func main() {
|
||||
mvcApp.Handle(new(myController))
|
||||
|
||||
// http://localhost:8080
|
||||
app.Run(iris.Addr(":8080"))
|
||||
app.Listen(":8080")
|
||||
}
|
||||
|
||||
type myController struct {
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
pb "grpcexample/helloworld"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Set up a connection to the server.
|
||||
cred, err := credentials.NewClientTLSFromFile("../server.crt", "localhost")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
conn, err := grpc.Dial("localhost:443", grpc.WithTransportCredentials(cred), grpc.WithBlock())
|
||||
defer conn.Close()
|
||||
|
||||
client := pb.NewGreeterBidirectionalStreamClient(conn)
|
||||
stream, err := client.SayHello(context.Background())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
waitCh := make(chan struct{})
|
||||
|
||||
// Implement the send channel.
|
||||
// As an exercise you can implement the read channel one (reading from server, see the server/main.go).
|
||||
go func() {
|
||||
for {
|
||||
println("Sleeping for 2 seconds...")
|
||||
time.Sleep(2 * time.Second)
|
||||
println("Sending a <test> msg...")
|
||||
msg := &pb.HelloRequest{Name: "test"}
|
||||
|
||||
err = stream.Send(msg)
|
||||
if err != nil {
|
||||
panic("stream.Send: " + err.Error())
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
<-waitCh
|
||||
stream.CloseSend()
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
module grpcexample
|
||||
|
||||
go 1.22
|
||||
|
||||
require (
|
||||
github.com/golang/protobuf v1.5.4
|
||||
github.com/kataras/iris/v12 v12.2.11-0.20240424221303-df7737ed577a
|
||||
google.golang.org/grpc v1.63.2
|
||||
google.golang.org/protobuf v1.33.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/BurntSushi/toml v1.3.2 // indirect
|
||||
github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 // indirect
|
||||
github.com/CloudyKit/jet/v6 v6.2.0 // indirect
|
||||
github.com/Joker/jade v1.1.3 // indirect
|
||||
github.com/Shopify/goreferrer v0.0.0-20220729165902-8cddb4f5de06 // indirect
|
||||
github.com/andybalholm/brotli v1.1.0 // indirect
|
||||
github.com/aymerick/douceur v0.2.0 // indirect
|
||||
github.com/blang/semver/v4 v4.0.0 // indirect
|
||||
github.com/fatih/structs v1.1.0 // indirect
|
||||
github.com/flosch/pongo2/v4 v4.0.2 // indirect
|
||||
github.com/gobwas/httphead v0.1.0 // indirect
|
||||
github.com/gobwas/pool v0.2.1 // indirect
|
||||
github.com/gobwas/ws v1.3.2 // indirect
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
github.com/gomarkdown/markdown v0.0.0-20240328165702-4d01890c35c0 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/gorilla/css v1.0.0 // indirect
|
||||
github.com/gorilla/websocket v1.5.1 // indirect
|
||||
github.com/iris-contrib/schema v0.0.6 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/kataras/blocks v0.0.8 // indirect
|
||||
github.com/kataras/golog v0.1.11 // indirect
|
||||
github.com/kataras/neffos v0.0.24-0.20240408172741-99c879ba0ede // indirect
|
||||
github.com/kataras/pio v0.0.13 // indirect
|
||||
github.com/kataras/sitemap v0.0.6 // indirect
|
||||
github.com/kataras/tunnel v0.0.4 // indirect
|
||||
github.com/klauspost/compress v1.17.7 // indirect
|
||||
github.com/mailgun/raymond/v2 v2.0.48 // indirect
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
github.com/mediocregopher/radix/v3 v3.8.1 // indirect
|
||||
github.com/microcosm-cc/bluemonday v1.0.26 // indirect
|
||||
github.com/nats-io/nats.go v1.34.1 // indirect
|
||||
github.com/nats-io/nkeys v0.4.7 // indirect
|
||||
github.com/nats-io/nuid v1.0.1 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/schollz/closestmatch v2.1.0+incompatible // indirect
|
||||
github.com/sirupsen/logrus v1.8.1 // indirect
|
||||
github.com/tdewolff/minify/v2 v2.20.19 // indirect
|
||||
github.com/tdewolff/parse/v2 v2.7.12 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect
|
||||
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
|
||||
github.com/yosssi/ace v0.0.5 // indirect
|
||||
golang.org/x/crypto v0.22.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20240404231335-c0f41cb1a7a0 // indirect
|
||||
golang.org/x/net v0.24.0 // indirect
|
||||
golang.org/x/sys v0.19.0 // indirect
|
||||
golang.org/x/text v0.14.0 // indirect
|
||||
golang.org/x/time v0.5.0 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
@@ -0,0 +1,206 @@
|
||||
github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8=
|
||||
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
||||
github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 h1:sR+/8Yb4slttB4vD+b9btVEnWgL3Q00OBTzVT8B9C0c=
|
||||
github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno=
|
||||
github.com/CloudyKit/jet/v6 v6.2.0 h1:EpcZ6SR9n28BUGtNJSvlBqf90IpjeFr36Tizxhn/oME=
|
||||
github.com/CloudyKit/jet/v6 v6.2.0/go.mod h1:d3ypHeIRNo2+XyqnGA8s+aphtcVpjP5hPwP/Lzo7Ro4=
|
||||
github.com/Joker/hpp v1.0.0 h1:65+iuJYdRXv/XyN62C1uEmmOx3432rNG/rKlX6V7Kkc=
|
||||
github.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY=
|
||||
github.com/Joker/jade v1.1.3 h1:Qbeh12Vq6BxURXT1qZBRHsDxeURB8ztcL6f3EXSGeHk=
|
||||
github.com/Joker/jade v1.1.3/go.mod h1:T+2WLyt7VH6Lp0TRxQrUYEs64nRc83wkMQrfeIQKduM=
|
||||
github.com/Shopify/goreferrer v0.0.0-20220729165902-8cddb4f5de06 h1:KkH3I3sJuOLP3TjA/dfr4NAY8bghDwnXiU7cTKxQqo0=
|
||||
github.com/Shopify/goreferrer v0.0.0-20220729165902-8cddb4f5de06/go.mod h1:7erjKLwalezA0k99cWs5L11HWOAPNjdUZ6RxH1BXbbM=
|
||||
github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=
|
||||
github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
|
||||
github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M=
|
||||
github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY=
|
||||
github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
|
||||
github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
|
||||
github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM=
|
||||
github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=
|
||||
github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
|
||||
github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=
|
||||
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
|
||||
github.com/flosch/pongo2/v4 v4.0.2 h1:gv+5Pe3vaSVmiJvh/BZa82b7/00YUGm0PIyVVLop0Hw=
|
||||
github.com/flosch/pongo2/v4 v4.0.2/go.mod h1:B5ObFANs/36VwxxlgKpdchIJHMvHB562PW+BWPhwZD8=
|
||||
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
|
||||
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
|
||||
github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU=
|
||||
github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM=
|
||||
github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og=
|
||||
github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
|
||||
github.com/gobwas/ws v1.3.2 h1:zlnbNHxumkRvfPWgfXu8RBwyNR1x8wh9cf5PTOCqs9Q=
|
||||
github.com/gobwas/ws v1.3.2/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY=
|
||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
|
||||
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/gomarkdown/markdown v0.0.0-20240328165702-4d01890c35c0 h1:4gjrh/PN2MuWCCElk8/I4OCKRKWCCo2zEct3VKCbibU=
|
||||
github.com/gomarkdown/markdown v0.0.0-20240328165702-4d01890c35c0/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
|
||||
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY=
|
||||
github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c=
|
||||
github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY=
|
||||
github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=
|
||||
github.com/imkira/go-interpol v1.1.0 h1:KIiKr0VSG2CUW1hl1jpiyuzuJeKUUpC8iM1AIE7N1Vk=
|
||||
github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA=
|
||||
github.com/iris-contrib/httpexpect/v2 v2.15.2 h1:T9THsdP1woyAqKHwjkEsbCnMefsAFvk8iJJKokcJ3Go=
|
||||
github.com/iris-contrib/httpexpect/v2 v2.15.2/go.mod h1:JLDgIqnFy5loDSUv1OA2j0mb6p/rDhiCqigP22Uq9xE=
|
||||
github.com/iris-contrib/schema v0.0.6 h1:CPSBLyx2e91H2yJzPuhGuifVRnZBBJ3pCOMbOvPZaTw=
|
||||
github.com/iris-contrib/schema v0.0.6/go.mod h1:iYszG0IOsuIsfzjymw1kMzTL8YQcCWlm65f3wX8J5iA=
|
||||
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||
github.com/kataras/blocks v0.0.8 h1:MrpVhoFTCR2v1iOOfGng5VJSILKeZZI+7NGfxEh3SUM=
|
||||
github.com/kataras/blocks v0.0.8/go.mod h1:9Jm5zx6BB+06NwA+OhTbHW1xkMOYxahnqTN5DveZ2Yg=
|
||||
github.com/kataras/golog v0.1.11 h1:dGkcCVsIpqiAMWTlebn/ZULHxFvfG4K43LF1cNWSh20=
|
||||
github.com/kataras/golog v0.1.11/go.mod h1:mAkt1vbPowFUuUGvexyQ5NFW6djEgGyxQBIARJ0AH4A=
|
||||
github.com/kataras/iris/v12 v12.2.11-0.20240424221303-df7737ed577a h1:uaDFlkh2LzTPtoOk/1e6jy/ChoM5/FxTgTiEehlJuD0=
|
||||
github.com/kataras/iris/v12 v12.2.11-0.20240424221303-df7737ed577a/go.mod h1:uMAeX8OqG9vqdhyrIPv8Lajo/wXTtAF43wchP9WHt2w=
|
||||
github.com/kataras/neffos v0.0.24-0.20240408172741-99c879ba0ede h1:ZnSJQ+ri9x46Yz15wHqSb93Q03yY12XMVLUFDJJ0+/g=
|
||||
github.com/kataras/neffos v0.0.24-0.20240408172741-99c879ba0ede/go.mod h1:i0dtcTbpnw1lqIbojYtGtZlu6gDWPxJ4Xl2eJ6oQ1bE=
|
||||
github.com/kataras/pio v0.0.13 h1:x0rXVX0fviDTXOOLOmr4MUxOabu1InVSTu5itF8CXCM=
|
||||
github.com/kataras/pio v0.0.13/go.mod h1:k3HNuSw+eJ8Pm2lA4lRhg3DiCjVgHlP8hmXApSej3oM=
|
||||
github.com/kataras/sitemap v0.0.6 h1:w71CRMMKYMJh6LR2wTgnk5hSgjVNB9KL60n5e2KHvLY=
|
||||
github.com/kataras/sitemap v0.0.6/go.mod h1:dW4dOCNs896OR1HmG+dMLdT7JjDk7mYBzoIRwuj5jA4=
|
||||
github.com/kataras/tunnel v0.0.4 h1:sCAqWuJV7nPzGrlb0os3j49lk2JhILT0rID38NHNLpA=
|
||||
github.com/kataras/tunnel v0.0.4/go.mod h1:9FkU4LaeifdMWqZu7o20ojmW4B7hdhv2CMLwfnHGpYw=
|
||||
github.com/klauspost/compress v1.17.7 h1:ehO88t2UGzQK66LMdE8tibEd1ErmzZjNEqWkjLAKQQg=
|
||||
github.com/klauspost/compress v1.17.7/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/mailgun/raymond/v2 v2.0.48 h1:5dmlB680ZkFG2RN/0lvTAghrSxIESeu9/2aeDqACtjw=
|
||||
github.com/mailgun/raymond/v2 v2.0.48/go.mod h1:lsgvL50kgt1ylcFJYZiULi5fjPBkkhNfj4KA0W54Z18=
|
||||
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
|
||||
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
|
||||
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mediocregopher/radix/v3 v3.8.1 h1:rOkHflVuulFKlwsLY01/M2cM2tWCjDoETcMqKbAWu1M=
|
||||
github.com/mediocregopher/radix/v3 v3.8.1/go.mod h1:8FL3F6UQRXHXIBSPUs5h0RybMF8i4n7wVopoX3x7Bv8=
|
||||
github.com/microcosm-cc/bluemonday v1.0.26 h1:xbqSvqzQMeEHCqMi64VAs4d8uy6Mequs3rQ0k/Khz58=
|
||||
github.com/microcosm-cc/bluemonday v1.0.26/go.mod h1:JyzOCs9gkyQyjs+6h10UEVSe02CGwkhd72Xdqh78TWs=
|
||||
github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=
|
||||
github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=
|
||||
github.com/nats-io/nats.go v1.34.1 h1:syWey5xaNHZgicYBemv0nohUPPmaLteiBEUT6Q5+F/4=
|
||||
github.com/nats-io/nats.go v1.34.1/go.mod h1:Ubdu4Nh9exXdSz0RVWRFBbRfrbSxOYd26oF0wkWclB8=
|
||||
github.com/nats-io/nkeys v0.4.7 h1:RwNJbbIdYCoClSDNY7QVKZlyb/wfT6ugvFCiKy6vDvI=
|
||||
github.com/nats-io/nkeys v0.4.7/go.mod h1:kqXRgRDPlGy7nGaEDMuYzmiJCIAAWDK0IMBtDmGD0nc=
|
||||
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
|
||||
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/sanity-io/litter v1.5.5 h1:iE+sBxPBzoK6uaEP5Lt3fHNgpKcHXc/A2HGETy0uJQo=
|
||||
github.com/sanity-io/litter v1.5.5/go.mod h1:9gzJgR2i4ZpjZHsKvUXIRQVk7P+yM3e+jAF7bU2UI5U=
|
||||
github.com/schollz/closestmatch v2.1.0+incompatible h1:Uel2GXEpJqOWBrlyI+oY9LTiyyjYS17cCYRqP13/SHk=
|
||||
github.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g=
|
||||
github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=
|
||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
||||
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
|
||||
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/tdewolff/minify/v2 v2.20.19 h1:tX0SR0LUrIqGoLjXnkIzRSIbKJ7PaNnSENLD4CyH6Xo=
|
||||
github.com/tdewolff/minify/v2 v2.20.19/go.mod h1:ulkFoeAVWMLEyjuDz1ZIWOA31g5aWOawCFRp9R/MudM=
|
||||
github.com/tdewolff/parse/v2 v2.7.12 h1:tgavkHc2ZDEQVKy1oWxwIyh5bP4F5fEh/JmBwPP/3LQ=
|
||||
github.com/tdewolff/parse/v2 v2.7.12/go.mod h1:3FbJWZp3XT9OWVN3Hmfp0p/a08v4h8J9W1aghka0soA=
|
||||
github.com/tdewolff/test v1.0.11-0.20231101010635-f1265d231d52/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE=
|
||||
github.com/tdewolff/test v1.0.11-0.20240106005702-7de5f7df4739 h1:IkjBCtQOOjIn03u/dMQK9g+Iw9ewps4mCl1nB8Sscbo=
|
||||
github.com/tdewolff/test v1.0.11-0.20240106005702-7de5f7df4739/go.mod h1:XPuWBzvdUzhCuxWO1ojpXsyzsA5bFoS3tO/Q3kFuTG8=
|
||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||
github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8=
|
||||
github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok=
|
||||
github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=
|
||||
github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
|
||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
|
||||
github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
|
||||
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
|
||||
github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0 h1:6fRhSjgLCkTD3JnJxvaJ4Sj+TYblw757bqYgZaOq5ZY=
|
||||
github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI=
|
||||
github.com/yosssi/ace v0.0.5 h1:tUkIP/BLdKqrlrPwcmH0shwEEhTRHoGnc1wFIWmaBUA=
|
||||
github.com/yosssi/ace v0.0.5/go.mod h1:ALfIzm2vT7t5ZE7uoIZqF3TQ7SAOyupFZnkrF5id+K0=
|
||||
github.com/yudai/gojsondiff v1.0.0 h1:27cbfqXLVEJ1o8I6v3y9lg8Ydm53EKqHXAOMxEGlCOA=
|
||||
github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg=
|
||||
github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 h1:BHyfKlQyqbsFN5p3IfnEUduWvb9is428/nNb5L3U01M=
|
||||
github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM=
|
||||
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
|
||||
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
|
||||
golang.org/x/exp v0.0.0-20240404231335-c0f41cb1a7a0 h1:985EYyeCOxTpcgOTJpflJUwOeEz0CQOdPt73OzpE9F8=
|
||||
golang.org/x/exp v0.0.0-20240404231335-c0f41cb1a7a0/go.mod h1:/lliqkxwWAhPjf5oSOIJup2XcqJaw8RGS6k3TGEc7GI=
|
||||
golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
|
||||
golang.org/x/net v0.0.0-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
|
||||
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
|
||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
|
||||
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
|
||||
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de h1:cZGRis4/ot9uVm639a+rHCUaG0JJHEsdyzSQTMX+suY=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:H4O17MA/PE9BsGx3w+a+W2VOLLD1Qf7oJneAoU6WktY=
|
||||
google.golang.org/grpc v1.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM=
|
||||
google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA=
|
||||
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
|
||||
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b h1:QRR6H1YWRnHb4Y/HeNFCTJLFVxaq6wH4YuVdsUOr75U=
|
||||
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
|
||||
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
moul.io/http2curl/v2 v2.3.0 h1:9r3JfDzWPcbIklMOs2TnIFzDYvfAZvjeavG6EzP7jYs=
|
||||
moul.io/http2curl/v2 v2.3.0/go.mod h1:RW4hyBjTWSYDOxapodpNEtX0g5Eb16sxklBqmd2RHcE=
|
||||
@@ -0,0 +1,39 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package helloworld;
|
||||
|
||||
option go_package = ".;helloworld";
|
||||
|
||||
// The request message containing the user's name.
|
||||
message HelloRequest {
|
||||
string name = 1;
|
||||
}
|
||||
|
||||
// The response message containing the greetings
|
||||
message HelloReply {
|
||||
string message = 1;
|
||||
}
|
||||
|
||||
// The greeting service definition.
|
||||
service Greeter {
|
||||
// Sends a greeting
|
||||
rpc SayHello (HelloRequest) returns (HelloReply) {}
|
||||
}
|
||||
|
||||
// The greeting service definition. (Server-side streaming RPC)
|
||||
service GreeterServerSideSStream {
|
||||
// Sends a greeting
|
||||
rpc SayHello (HelloRequest) returns (stream HelloReply) {}
|
||||
}
|
||||
|
||||
// The greeting service definition. (Client-side streaming RPC)
|
||||
service GreeterClientSideStream {
|
||||
// Sends a greeting
|
||||
rpc SayHello (stream HelloRequest) returns (HelloReply) {}
|
||||
}
|
||||
|
||||
// The greeting service definition. (Bidirectional streaming RPC)
|
||||
service GreeterBidirectionalStream {
|
||||
// Sends a greeting
|
||||
rpc SayHello (stream HelloRequest) returns (stream HelloReply) {}
|
||||
}
|
||||
@@ -0,0 +1,242 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.25.0
|
||||
// protoc v3.12.3
|
||||
// source: helloworld.proto
|
||||
|
||||
package helloworld
|
||||
|
||||
import (
|
||||
proto "github.com/golang/protobuf/proto"
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
)
|
||||
|
||||
const (
|
||||
// Verify that this generated code is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
// This is a compile-time assertion that a sufficiently up-to-date version
|
||||
// of the legacy proto package is being used.
|
||||
const _ = proto.ProtoPackageIsVersion4
|
||||
|
||||
// The request message containing the user's name.
|
||||
type HelloRequest struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
|
||||
}
|
||||
|
||||
func (x *HelloRequest) Reset() {
|
||||
*x = HelloRequest{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_helloworld_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *HelloRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*HelloRequest) ProtoMessage() {}
|
||||
|
||||
func (x *HelloRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_helloworld_proto_msgTypes[0]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use HelloRequest.ProtoReflect.Descriptor instead.
|
||||
func (*HelloRequest) Descriptor() ([]byte, []int) {
|
||||
return file_helloworld_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
func (x *HelloRequest) GetName() string {
|
||||
if x != nil {
|
||||
return x.Name
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// The response message containing the greetings
|
||||
type HelloReply struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"`
|
||||
}
|
||||
|
||||
func (x *HelloReply) Reset() {
|
||||
*x = HelloReply{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_helloworld_proto_msgTypes[1]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *HelloReply) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*HelloReply) ProtoMessage() {}
|
||||
|
||||
func (x *HelloReply) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_helloworld_proto_msgTypes[1]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use HelloReply.ProtoReflect.Descriptor instead.
|
||||
func (*HelloReply) Descriptor() ([]byte, []int) {
|
||||
return file_helloworld_proto_rawDescGZIP(), []int{1}
|
||||
}
|
||||
|
||||
func (x *HelloReply) GetMessage() string {
|
||||
if x != nil {
|
||||
return x.Message
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
var File_helloworld_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_helloworld_proto_rawDesc = []byte{
|
||||
0x0a, 0x10, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x2e, 0x70, 0x72, 0x6f,
|
||||
0x74, 0x6f, 0x12, 0x0a, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x22, 0x22,
|
||||
0x0a, 0x0c, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12,
|
||||
0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61,
|
||||
0x6d, 0x65, 0x22, 0x26, 0x0a, 0x0a, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79,
|
||||
0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28,
|
||||
0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x32, 0x49, 0x0a, 0x07, 0x47, 0x72,
|
||||
0x65, 0x65, 0x74, 0x65, 0x72, 0x12, 0x3e, 0x0a, 0x08, 0x53, 0x61, 0x79, 0x48, 0x65, 0x6c, 0x6c,
|
||||
0x6f, 0x12, 0x18, 0x2e, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x2e, 0x48,
|
||||
0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x68, 0x65,
|
||||
0x6c, 0x6c, 0x6f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65,
|
||||
0x70, 0x6c, 0x79, 0x22, 0x00, 0x32, 0x5c, 0x0a, 0x18, 0x47, 0x72, 0x65, 0x65, 0x74, 0x65, 0x72,
|
||||
0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x53, 0x69, 0x64, 0x65, 0x53, 0x53, 0x74, 0x72, 0x65, 0x61,
|
||||
0x6d, 0x12, 0x40, 0x0a, 0x08, 0x53, 0x61, 0x79, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x12, 0x18, 0x2e,
|
||||
0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f,
|
||||
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x77,
|
||||
0x6f, 0x72, 0x6c, 0x64, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22,
|
||||
0x00, 0x30, 0x01, 0x32, 0x5b, 0x0a, 0x17, 0x47, 0x72, 0x65, 0x65, 0x74, 0x65, 0x72, 0x43, 0x6c,
|
||||
0x69, 0x65, 0x6e, 0x74, 0x53, 0x69, 0x64, 0x65, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x40,
|
||||
0x0a, 0x08, 0x53, 0x61, 0x79, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x12, 0x18, 0x2e, 0x68, 0x65, 0x6c,
|
||||
0x6c, 0x6f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x71,
|
||||
0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x77, 0x6f, 0x72, 0x6c,
|
||||
0x64, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x28, 0x01,
|
||||
0x32, 0x60, 0x0a, 0x1a, 0x47, 0x72, 0x65, 0x65, 0x74, 0x65, 0x72, 0x42, 0x69, 0x64, 0x69, 0x72,
|
||||
0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x42,
|
||||
0x0a, 0x08, 0x53, 0x61, 0x79, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x12, 0x18, 0x2e, 0x68, 0x65, 0x6c,
|
||||
0x6c, 0x6f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x71,
|
||||
0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x77, 0x6f, 0x72, 0x6c,
|
||||
0x64, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x28, 0x01,
|
||||
0x30, 0x01, 0x42, 0x0e, 0x5a, 0x0c, 0x2e, 0x3b, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x77, 0x6f, 0x72,
|
||||
0x6c, 0x64, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
file_helloworld_proto_rawDescOnce sync.Once
|
||||
file_helloworld_proto_rawDescData = file_helloworld_proto_rawDesc
|
||||
)
|
||||
|
||||
func file_helloworld_proto_rawDescGZIP() []byte {
|
||||
file_helloworld_proto_rawDescOnce.Do(func() {
|
||||
file_helloworld_proto_rawDescData = protoimpl.X.CompressGZIP(file_helloworld_proto_rawDescData)
|
||||
})
|
||||
return file_helloworld_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_helloworld_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
|
||||
var file_helloworld_proto_goTypes = []interface{}{
|
||||
(*HelloRequest)(nil), // 0: helloworld.HelloRequest
|
||||
(*HelloReply)(nil), // 1: helloworld.HelloReply
|
||||
}
|
||||
var file_helloworld_proto_depIdxs = []int32{
|
||||
0, // 0: helloworld.Greeter.SayHello:input_type -> helloworld.HelloRequest
|
||||
0, // 1: helloworld.GreeterServerSideSStream.SayHello:input_type -> helloworld.HelloRequest
|
||||
0, // 2: helloworld.GreeterClientSideStream.SayHello:input_type -> helloworld.HelloRequest
|
||||
0, // 3: helloworld.GreeterBidirectionalStream.SayHello:input_type -> helloworld.HelloRequest
|
||||
1, // 4: helloworld.Greeter.SayHello:output_type -> helloworld.HelloReply
|
||||
1, // 5: helloworld.GreeterServerSideSStream.SayHello:output_type -> helloworld.HelloReply
|
||||
1, // 6: helloworld.GreeterClientSideStream.SayHello:output_type -> helloworld.HelloReply
|
||||
1, // 7: helloworld.GreeterBidirectionalStream.SayHello:output_type -> helloworld.HelloReply
|
||||
4, // [4:8] is the sub-list for method output_type
|
||||
0, // [0:4] is the sub-list for method input_type
|
||||
0, // [0:0] is the sub-list for extension type_name
|
||||
0, // [0:0] is the sub-list for extension extendee
|
||||
0, // [0:0] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_helloworld_proto_init() }
|
||||
func file_helloworld_proto_init() {
|
||||
if File_helloworld_proto != nil {
|
||||
return
|
||||
}
|
||||
if !protoimpl.UnsafeEnabled {
|
||||
file_helloworld_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*HelloRequest); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_helloworld_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*HelloReply); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_helloworld_proto_rawDesc,
|
||||
NumEnums: 0,
|
||||
NumMessages: 2,
|
||||
NumExtensions: 0,
|
||||
NumServices: 4,
|
||||
},
|
||||
GoTypes: file_helloworld_proto_goTypes,
|
||||
DependencyIndexes: file_helloworld_proto_depIdxs,
|
||||
MessageInfos: file_helloworld_proto_msgTypes,
|
||||
}.Build()
|
||||
File_helloworld_proto = out.File
|
||||
file_helloworld_proto_rawDesc = nil
|
||||
file_helloworld_proto_goTypes = nil
|
||||
file_helloworld_proto_depIdxs = nil
|
||||
}
|
||||
@@ -0,0 +1,450 @@
|
||||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||
|
||||
package helloworld
|
||||
|
||||
import (
|
||||
context "context"
|
||||
grpc "google.golang.org/grpc"
|
||||
codes "google.golang.org/grpc/codes"
|
||||
status "google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the grpc package it is being compiled against.
|
||||
const _ = grpc.SupportPackageIsVersion7
|
||||
|
||||
// GreeterClient is the client API for Greeter service.
|
||||
//
|
||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
|
||||
type GreeterClient interface {
|
||||
// Sends a greeting
|
||||
SayHello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloReply, error)
|
||||
}
|
||||
|
||||
type greeterClient struct {
|
||||
cc grpc.ClientConnInterface
|
||||
}
|
||||
|
||||
func NewGreeterClient(cc grpc.ClientConnInterface) GreeterClient {
|
||||
return &greeterClient{cc}
|
||||
}
|
||||
|
||||
func (c *greeterClient) SayHello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloReply, error) {
|
||||
out := new(HelloReply)
|
||||
err := c.cc.Invoke(ctx, "/helloworld.Greeter/SayHello", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// GreeterServer is the server API for Greeter service.
|
||||
// All implementations must embed UnimplementedGreeterServer
|
||||
// for forward compatibility
|
||||
type GreeterServer interface {
|
||||
// Sends a greeting
|
||||
SayHello(context.Context, *HelloRequest) (*HelloReply, error)
|
||||
mustEmbedUnimplementedGreeterServer()
|
||||
}
|
||||
|
||||
// UnimplementedGreeterServer must be embedded to have forward compatible implementations.
|
||||
type UnimplementedGreeterServer struct {
|
||||
}
|
||||
|
||||
func (UnimplementedGreeterServer) SayHello(context.Context, *HelloRequest) (*HelloReply, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method SayHello not implemented")
|
||||
}
|
||||
func (UnimplementedGreeterServer) mustEmbedUnimplementedGreeterServer() {}
|
||||
|
||||
// UnsafeGreeterServer may be embedded to opt out of forward compatibility for this service.
|
||||
// Use of this interface is not recommended, as added methods to GreeterServer will
|
||||
// result in compilation errors.
|
||||
type UnsafeGreeterServer interface {
|
||||
mustEmbedUnimplementedGreeterServer()
|
||||
}
|
||||
|
||||
func RegisterGreeterServer(s grpc.ServiceRegistrar, srv GreeterServer) {
|
||||
s.RegisterService(&_Greeter_serviceDesc, srv)
|
||||
}
|
||||
|
||||
func _Greeter_SayHello_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(HelloRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(GreeterServer).SayHello(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/helloworld.Greeter/SayHello",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(GreeterServer).SayHello(ctx, req.(*HelloRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
var _Greeter_serviceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "helloworld.Greeter",
|
||||
HandlerType: (*GreeterServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "SayHello",
|
||||
Handler: _Greeter_SayHello_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "helloworld.proto",
|
||||
}
|
||||
|
||||
// GreeterServerSideSStreamClient is the client API for GreeterServerSideSStream service.
|
||||
//
|
||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
|
||||
type GreeterServerSideSStreamClient interface {
|
||||
// Sends a greeting
|
||||
SayHello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (GreeterServerSideSStream_SayHelloClient, error)
|
||||
}
|
||||
|
||||
type greeterServerSideSStreamClient struct {
|
||||
cc grpc.ClientConnInterface
|
||||
}
|
||||
|
||||
func NewGreeterServerSideSStreamClient(cc grpc.ClientConnInterface) GreeterServerSideSStreamClient {
|
||||
return &greeterServerSideSStreamClient{cc}
|
||||
}
|
||||
|
||||
func (c *greeterServerSideSStreamClient) SayHello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (GreeterServerSideSStream_SayHelloClient, error) {
|
||||
stream, err := c.cc.NewStream(ctx, &_GreeterServerSideSStream_serviceDesc.Streams[0], "/helloworld.GreeterServerSideSStream/SayHello", opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
x := &greeterServerSideSStreamSayHelloClient{stream}
|
||||
if err := x.ClientStream.SendMsg(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := x.ClientStream.CloseSend(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return x, nil
|
||||
}
|
||||
|
||||
type GreeterServerSideSStream_SayHelloClient interface {
|
||||
Recv() (*HelloReply, error)
|
||||
grpc.ClientStream
|
||||
}
|
||||
|
||||
type greeterServerSideSStreamSayHelloClient struct {
|
||||
grpc.ClientStream
|
||||
}
|
||||
|
||||
func (x *greeterServerSideSStreamSayHelloClient) Recv() (*HelloReply, error) {
|
||||
m := new(HelloReply)
|
||||
if err := x.ClientStream.RecvMsg(m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// GreeterServerSideSStreamServer is the server API for GreeterServerSideSStream service.
|
||||
// All implementations must embed UnimplementedGreeterServerSideSStreamServer
|
||||
// for forward compatibility
|
||||
type GreeterServerSideSStreamServer interface {
|
||||
// Sends a greeting
|
||||
SayHello(*HelloRequest, GreeterServerSideSStream_SayHelloServer) error
|
||||
mustEmbedUnimplementedGreeterServerSideSStreamServer()
|
||||
}
|
||||
|
||||
// UnimplementedGreeterServerSideSStreamServer must be embedded to have forward compatible implementations.
|
||||
type UnimplementedGreeterServerSideSStreamServer struct {
|
||||
}
|
||||
|
||||
func (UnimplementedGreeterServerSideSStreamServer) SayHello(*HelloRequest, GreeterServerSideSStream_SayHelloServer) error {
|
||||
return status.Errorf(codes.Unimplemented, "method SayHello not implemented")
|
||||
}
|
||||
func (UnimplementedGreeterServerSideSStreamServer) mustEmbedUnimplementedGreeterServerSideSStreamServer() {
|
||||
}
|
||||
|
||||
// UnsafeGreeterServerSideSStreamServer may be embedded to opt out of forward compatibility for this service.
|
||||
// Use of this interface is not recommended, as added methods to GreeterServerSideSStreamServer will
|
||||
// result in compilation errors.
|
||||
type UnsafeGreeterServerSideSStreamServer interface {
|
||||
mustEmbedUnimplementedGreeterServerSideSStreamServer()
|
||||
}
|
||||
|
||||
func RegisterGreeterServerSideSStreamServer(s grpc.ServiceRegistrar, srv GreeterServerSideSStreamServer) {
|
||||
s.RegisterService(&_GreeterServerSideSStream_serviceDesc, srv)
|
||||
}
|
||||
|
||||
func _GreeterServerSideSStream_SayHello_Handler(srv interface{}, stream grpc.ServerStream) error {
|
||||
m := new(HelloRequest)
|
||||
if err := stream.RecvMsg(m); err != nil {
|
||||
return err
|
||||
}
|
||||
return srv.(GreeterServerSideSStreamServer).SayHello(m, &greeterServerSideSStreamSayHelloServer{stream})
|
||||
}
|
||||
|
||||
type GreeterServerSideSStream_SayHelloServer interface {
|
||||
Send(*HelloReply) error
|
||||
grpc.ServerStream
|
||||
}
|
||||
|
||||
type greeterServerSideSStreamSayHelloServer struct {
|
||||
grpc.ServerStream
|
||||
}
|
||||
|
||||
func (x *greeterServerSideSStreamSayHelloServer) Send(m *HelloReply) error {
|
||||
return x.ServerStream.SendMsg(m)
|
||||
}
|
||||
|
||||
var _GreeterServerSideSStream_serviceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "helloworld.GreeterServerSideSStream",
|
||||
HandlerType: (*GreeterServerSideSStreamServer)(nil),
|
||||
Methods: []grpc.MethodDesc{},
|
||||
Streams: []grpc.StreamDesc{
|
||||
{
|
||||
StreamName: "SayHello",
|
||||
Handler: _GreeterServerSideSStream_SayHello_Handler,
|
||||
ServerStreams: true,
|
||||
},
|
||||
},
|
||||
Metadata: "helloworld.proto",
|
||||
}
|
||||
|
||||
// GreeterClientSideStreamClient is the client API for GreeterClientSideStream service.
|
||||
//
|
||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
|
||||
type GreeterClientSideStreamClient interface {
|
||||
// Sends a greeting
|
||||
SayHello(ctx context.Context, opts ...grpc.CallOption) (GreeterClientSideStream_SayHelloClient, error)
|
||||
}
|
||||
|
||||
type greeterClientSideStreamClient struct {
|
||||
cc grpc.ClientConnInterface
|
||||
}
|
||||
|
||||
func NewGreeterClientSideStreamClient(cc grpc.ClientConnInterface) GreeterClientSideStreamClient {
|
||||
return &greeterClientSideStreamClient{cc}
|
||||
}
|
||||
|
||||
func (c *greeterClientSideStreamClient) SayHello(ctx context.Context, opts ...grpc.CallOption) (GreeterClientSideStream_SayHelloClient, error) {
|
||||
stream, err := c.cc.NewStream(ctx, &_GreeterClientSideStream_serviceDesc.Streams[0], "/helloworld.GreeterClientSideStream/SayHello", opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
x := &greeterClientSideStreamSayHelloClient{stream}
|
||||
return x, nil
|
||||
}
|
||||
|
||||
type GreeterClientSideStream_SayHelloClient interface {
|
||||
Send(*HelloRequest) error
|
||||
CloseAndRecv() (*HelloReply, error)
|
||||
grpc.ClientStream
|
||||
}
|
||||
|
||||
type greeterClientSideStreamSayHelloClient struct {
|
||||
grpc.ClientStream
|
||||
}
|
||||
|
||||
func (x *greeterClientSideStreamSayHelloClient) Send(m *HelloRequest) error {
|
||||
return x.ClientStream.SendMsg(m)
|
||||
}
|
||||
|
||||
func (x *greeterClientSideStreamSayHelloClient) CloseAndRecv() (*HelloReply, error) {
|
||||
if err := x.ClientStream.CloseSend(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
m := new(HelloReply)
|
||||
if err := x.ClientStream.RecvMsg(m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// GreeterClientSideStreamServer is the server API for GreeterClientSideStream service.
|
||||
// All implementations must embed UnimplementedGreeterClientSideStreamServer
|
||||
// for forward compatibility
|
||||
type GreeterClientSideStreamServer interface {
|
||||
// Sends a greeting
|
||||
SayHello(GreeterClientSideStream_SayHelloServer) error
|
||||
mustEmbedUnimplementedGreeterClientSideStreamServer()
|
||||
}
|
||||
|
||||
// UnimplementedGreeterClientSideStreamServer must be embedded to have forward compatible implementations.
|
||||
type UnimplementedGreeterClientSideStreamServer struct {
|
||||
}
|
||||
|
||||
func (UnimplementedGreeterClientSideStreamServer) SayHello(GreeterClientSideStream_SayHelloServer) error {
|
||||
return status.Errorf(codes.Unimplemented, "method SayHello not implemented")
|
||||
}
|
||||
func (UnimplementedGreeterClientSideStreamServer) mustEmbedUnimplementedGreeterClientSideStreamServer() {
|
||||
}
|
||||
|
||||
// UnsafeGreeterClientSideStreamServer may be embedded to opt out of forward compatibility for this service.
|
||||
// Use of this interface is not recommended, as added methods to GreeterClientSideStreamServer will
|
||||
// result in compilation errors.
|
||||
type UnsafeGreeterClientSideStreamServer interface {
|
||||
mustEmbedUnimplementedGreeterClientSideStreamServer()
|
||||
}
|
||||
|
||||
func RegisterGreeterClientSideStreamServer(s grpc.ServiceRegistrar, srv GreeterClientSideStreamServer) {
|
||||
s.RegisterService(&_GreeterClientSideStream_serviceDesc, srv)
|
||||
}
|
||||
|
||||
func _GreeterClientSideStream_SayHello_Handler(srv interface{}, stream grpc.ServerStream) error {
|
||||
return srv.(GreeterClientSideStreamServer).SayHello(&greeterClientSideStreamSayHelloServer{stream})
|
||||
}
|
||||
|
||||
type GreeterClientSideStream_SayHelloServer interface {
|
||||
SendAndClose(*HelloReply) error
|
||||
Recv() (*HelloRequest, error)
|
||||
grpc.ServerStream
|
||||
}
|
||||
|
||||
type greeterClientSideStreamSayHelloServer struct {
|
||||
grpc.ServerStream
|
||||
}
|
||||
|
||||
func (x *greeterClientSideStreamSayHelloServer) SendAndClose(m *HelloReply) error {
|
||||
return x.ServerStream.SendMsg(m)
|
||||
}
|
||||
|
||||
func (x *greeterClientSideStreamSayHelloServer) Recv() (*HelloRequest, error) {
|
||||
m := new(HelloRequest)
|
||||
if err := x.ServerStream.RecvMsg(m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
var _GreeterClientSideStream_serviceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "helloworld.GreeterClientSideStream",
|
||||
HandlerType: (*GreeterClientSideStreamServer)(nil),
|
||||
Methods: []grpc.MethodDesc{},
|
||||
Streams: []grpc.StreamDesc{
|
||||
{
|
||||
StreamName: "SayHello",
|
||||
Handler: _GreeterClientSideStream_SayHello_Handler,
|
||||
ClientStreams: true,
|
||||
},
|
||||
},
|
||||
Metadata: "helloworld.proto",
|
||||
}
|
||||
|
||||
// GreeterBidirectionalStreamClient is the client API for GreeterBidirectionalStream service.
|
||||
//
|
||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
|
||||
type GreeterBidirectionalStreamClient interface {
|
||||
// Sends a greeting
|
||||
SayHello(ctx context.Context, opts ...grpc.CallOption) (GreeterBidirectionalStream_SayHelloClient, error)
|
||||
}
|
||||
|
||||
type greeterBidirectionalStreamClient struct {
|
||||
cc grpc.ClientConnInterface
|
||||
}
|
||||
|
||||
func NewGreeterBidirectionalStreamClient(cc grpc.ClientConnInterface) GreeterBidirectionalStreamClient {
|
||||
return &greeterBidirectionalStreamClient{cc}
|
||||
}
|
||||
|
||||
func (c *greeterBidirectionalStreamClient) SayHello(ctx context.Context, opts ...grpc.CallOption) (GreeterBidirectionalStream_SayHelloClient, error) {
|
||||
stream, err := c.cc.NewStream(ctx, &_GreeterBidirectionalStream_serviceDesc.Streams[0], "/helloworld.GreeterBidirectionalStream/SayHello", opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
x := &greeterBidirectionalStreamSayHelloClient{stream}
|
||||
return x, nil
|
||||
}
|
||||
|
||||
type GreeterBidirectionalStream_SayHelloClient interface {
|
||||
Send(*HelloRequest) error
|
||||
Recv() (*HelloReply, error)
|
||||
grpc.ClientStream
|
||||
}
|
||||
|
||||
type greeterBidirectionalStreamSayHelloClient struct {
|
||||
grpc.ClientStream
|
||||
}
|
||||
|
||||
func (x *greeterBidirectionalStreamSayHelloClient) Send(m *HelloRequest) error {
|
||||
return x.ClientStream.SendMsg(m)
|
||||
}
|
||||
|
||||
func (x *greeterBidirectionalStreamSayHelloClient) Recv() (*HelloReply, error) {
|
||||
m := new(HelloReply)
|
||||
if err := x.ClientStream.RecvMsg(m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// GreeterBidirectionalStreamServer is the server API for GreeterBidirectionalStream service.
|
||||
// All implementations must embed UnimplementedGreeterBidirectionalStreamServer
|
||||
// for forward compatibility
|
||||
type GreeterBidirectionalStreamServer interface {
|
||||
// Sends a greeting
|
||||
SayHello(GreeterBidirectionalStream_SayHelloServer) error
|
||||
mustEmbedUnimplementedGreeterBidirectionalStreamServer()
|
||||
}
|
||||
|
||||
// UnimplementedGreeterBidirectionalStreamServer must be embedded to have forward compatible implementations.
|
||||
type UnimplementedGreeterBidirectionalStreamServer struct {
|
||||
}
|
||||
|
||||
func (UnimplementedGreeterBidirectionalStreamServer) SayHello(GreeterBidirectionalStream_SayHelloServer) error {
|
||||
return status.Errorf(codes.Unimplemented, "method SayHello not implemented")
|
||||
}
|
||||
func (UnimplementedGreeterBidirectionalStreamServer) mustEmbedUnimplementedGreeterBidirectionalStreamServer() {
|
||||
}
|
||||
|
||||
// UnsafeGreeterBidirectionalStreamServer may be embedded to opt out of forward compatibility for this service.
|
||||
// Use of this interface is not recommended, as added methods to GreeterBidirectionalStreamServer will
|
||||
// result in compilation errors.
|
||||
type UnsafeGreeterBidirectionalStreamServer interface {
|
||||
mustEmbedUnimplementedGreeterBidirectionalStreamServer()
|
||||
}
|
||||
|
||||
func RegisterGreeterBidirectionalStreamServer(s grpc.ServiceRegistrar, srv GreeterBidirectionalStreamServer) {
|
||||
s.RegisterService(&_GreeterBidirectionalStream_serviceDesc, srv)
|
||||
}
|
||||
|
||||
func _GreeterBidirectionalStream_SayHello_Handler(srv interface{}, stream grpc.ServerStream) error {
|
||||
return srv.(GreeterBidirectionalStreamServer).SayHello(&greeterBidirectionalStreamSayHelloServer{stream})
|
||||
}
|
||||
|
||||
type GreeterBidirectionalStream_SayHelloServer interface {
|
||||
Send(*HelloReply) error
|
||||
Recv() (*HelloRequest, error)
|
||||
grpc.ServerStream
|
||||
}
|
||||
|
||||
type greeterBidirectionalStreamSayHelloServer struct {
|
||||
grpc.ServerStream
|
||||
}
|
||||
|
||||
func (x *greeterBidirectionalStreamSayHelloServer) Send(m *HelloReply) error {
|
||||
return x.ServerStream.SendMsg(m)
|
||||
}
|
||||
|
||||
func (x *greeterBidirectionalStreamSayHelloServer) Recv() (*HelloRequest, error) {
|
||||
m := new(HelloRequest)
|
||||
if err := x.ServerStream.RecvMsg(m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
var _GreeterBidirectionalStream_serviceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "helloworld.GreeterBidirectionalStream",
|
||||
HandlerType: (*GreeterBidirectionalStreamServer)(nil),
|
||||
Methods: []grpc.MethodDesc{},
|
||||
Streams: []grpc.StreamDesc{
|
||||
{
|
||||
StreamName: "SayHello",
|
||||
Handler: _GreeterBidirectionalStream_SayHello_Handler,
|
||||
ServerStreams: true,
|
||||
ClientStreams: true,
|
||||
},
|
||||
},
|
||||
Metadata: "helloworld.proto",
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIICwzCCAaugAwIBAgIJANAsSlhuqzW/MA0GCSqGSIb3DQEBBQUAMBQxEjAQBgNV
|
||||
BAMTCWxvY2FsaG9zdDAeFw0yMDEwMzEwMjM4MTNaFw0zMDEwMjkwMjM4MTNaMBQx
|
||||
EjAQBgNVBAMTCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
|
||||
ggEBAKY8mFh0nvXa8V6Vx5TVVbfq9Zx81hiIKeXN977JaQGrPfCMRU61smaDJAzR
|
||||
MBEFjHT37Zj5sZr6qvQmt3Dfst8bNcFBsZ7s3bBknuevD3gPJBy1r5KFwSkPZekH
|
||||
T+0DF0Drq1/iIZQXE13udWzDnzxABUBOmIzEmn+rfp2j3xJJGLoO1O4Wu2YpXhtj
|
||||
yNJfswoztGGie6u61mAN7Rw4JQUkDKa3hAKkYtfOoGHZBs6Y0RIKZQikjxlVpMMc
|
||||
fCvUMHkY51GP8ycgZ8+0n/cjm13KkqfvoyRZ1rQqCd24YNIEl18t0FClpUAHkxD/
|
||||
aUPCqV3bqpFBbDPQcYybIGjfNIMCAwEAAaMYMBYwFAYDVR0RBA0wC4IJbG9jYWxo
|
||||
b3N0MA0GCSqGSIb3DQEBBQUAA4IBAQAd6F6tG/jcUu9pm/5XeH1Iof5PB9tFuBvv
|
||||
w9HFjBI5S7ItnTepneoOP26lx2iOE0GwkvrhseZcHkGDZPdncBn12PuCHUbcO2Ux
|
||||
bEDyDMsAHooKLvmAfAWeiOmeJAdFl8DOTgD/lTHK1n/mEwCUOtGozmfm/Y0pnfc7
|
||||
nMgbQ+yNsj1X7HVJHRUdOfOCGjiWofo6v7V7YyZJC2jpn3K7O4126jic2ibWYKQl
|
||||
fLLjgS0N5Wcun117e1mRYV41jtPOkeAvxJbJGf0IP7Os0VfG1UQhodwjkV4s8GcG
|
||||
naB5qC4Y508cB7Lq58kbGgZ0nfMCcpnrKobliIZVEpH7aa0Po1lm
|
||||
-----END CERTIFICATE-----
|
||||
@@ -0,0 +1,27 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpAIBAAKCAQEApjyYWHSe9drxXpXHlNVVt+r1nHzWGIgp5c33vslpAas98IxF
|
||||
TrWyZoMkDNEwEQWMdPftmPmxmvqq9Ca3cN+y3xs1wUGxnuzdsGSe568PeA8kHLWv
|
||||
koXBKQ9l6QdP7QMXQOurX+IhlBcTXe51bMOfPEAFQE6YjMSaf6t+naPfEkkYug7U
|
||||
7ha7ZileG2PI0l+zCjO0YaJ7q7rWYA3tHDglBSQMpreEAqRi186gYdkGzpjREgpl
|
||||
CKSPGVWkwxx8K9QweRjnUY/zJyBnz7Sf9yObXcqSp++jJFnWtCoJ3bhg0gSXXy3Q
|
||||
UKWlQAeTEP9pQ8KpXduqkUFsM9BxjJsgaN80gwIDAQABAoIBACDVNwHBhuPoKmQU
|
||||
ESdEO3nn3jraLS8LNbs9wwDbpvG9cK5iBg5VtLaqkCQ37NZv0h4IGdVs+7cwazNt
|
||||
si2JATsvlJ5m6z4IaoC8XuZDnTqJQwiomdTGti/16prr5s1ZHu6jnWWCtD8bj6et
|
||||
wWOJ/5lWy7K300l6S0mMBaX9B8IEfGR+YPGF/bfsIlf+9iuVulBjH5NPIozwl4c9
|
||||
VUafESEZX0DM/KDHqFvvWdMaw9e+3QNi+l0t3VJz6JuEcUfRztT6lbzYbRGBYqKr
|
||||
DmUNxYUThd717NJklxr9D+GMm5FnfrzY8vcJ16SILBQyDthsz1s6gdT0yeRFgjJf
|
||||
afDObAECgYEA1WQdayAeqT7sE0T0jHAWHdVhRd9sGUTZ4LjVuGbMjbJWJVUPn/fz
|
||||
ahBQh77xutmNCbDSUD0srT+zHtRRJUVhl+ojFS1CMyyL/UAfH24gDwUnXIyq/DPA
|
||||
heXKD8NwGoTPbHKjdmy+89qVbes3oxBc5tDKpzAuBoPY4iBMVwfqSmECgYEAx24Y
|
||||
i1lhxuG+g2YSjiIhmD/ytZSXamP3+KmnqWWXbEjJd2oYmCPr9yBCfdk9jCUXZJsg
|
||||
tYsRduMajTjmTNkYmGyZNVtmjXfrq/vaJgf7bZhhLOFxWoNmLlKQfmbuJW5fkV1b
|
||||
hY8oid+YbBob4xseFzKvm5j0aKwufi3v7XLUEWMCgYEAs7rqKFNaX9SWhDhc/Xhe
|
||||
uGwDzRU8eCAMnwEvSWyUN3iQpEr7qRHvXFM3cM47zdP0vcfHrDuKSLXRSVMssYa5
|
||||
h3l2aRzAmFeZ5Qk/7XoU2HHP0FzOmzN/oYeE5DgJUNyx1DbORS2cu8lMeNNX/ikH
|
||||
BoWvWpfy/BvK7dKkWd1Z0aECgYEAm1+QKcjqX5ty5UZ6AFhhGhAAVS2+Rfo6sHXl
|
||||
FRn8PjX7GFkFbkrWRUPR6eB9jhk7v3sIocgGRDytbAc/jfG5ss8xEhvyqxcZ+nUO
|
||||
QYEIhxsn4mKGAMHMsxxKTOB+e5UhScyVSFn/eGNGijpRLb/r0qD/pdcl3AMBefbq
|
||||
LXG//QcCgYBrYTCaKYDg/TSMt6fTSZgJRaui8PvvlLjZy63vrVCexVUibiCpbUt9
|
||||
sHe9gbSDPr/uCyNgKnZjDbVabmZboVYqHBDQnXPUYYx9dBSL2DzE3JXA6Q8RVnOY
|
||||
zwCPzVxYbE9h02ishAgc6k9w61XUaDnASmBZAnlxt9/8cB7+IZAlfg==
|
||||
-----END RSA PRIVATE KEY-----
|
||||
@@ -0,0 +1,50 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
pb "grpcexample/helloworld"
|
||||
|
||||
"github.com/kataras/iris/v12"
|
||||
"github.com/kataras/iris/v12/mvc"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
type Greeter struct {
|
||||
pb.UnimplementedGreeterBidirectionalStreamServer
|
||||
}
|
||||
|
||||
// SayHello implements the proto Bidirectional Stream Greeter service.
|
||||
func (g *Greeter) SayHello(stream pb.GreeterBidirectionalStream_SayHelloServer) error {
|
||||
for {
|
||||
in, err := stream.Recv()
|
||||
if err == io.EOF {
|
||||
return nil
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
println("Received input: " + in.Name)
|
||||
// On client side you can implement the 'read' operation too.
|
||||
stream.Send(&pb.HelloReply{Message: "Hello " + in.Name})
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
app := iris.New()
|
||||
|
||||
grpcServer := grpc.NewServer()
|
||||
|
||||
myService := &Greeter{}
|
||||
pb.RegisterGreeterBidirectionalStreamServer(grpcServer, myService)
|
||||
|
||||
rootApp := mvc.New(app)
|
||||
rootApp.Handle(myService, mvc.GRPC{
|
||||
Server: grpcServer, // Required.
|
||||
ServiceName: "helloworld.GreeterBidirectionalStream", // Required.
|
||||
Strict: true, // Set it to true on gRPC streaming.
|
||||
})
|
||||
|
||||
app.Run(iris.TLS(":443", "../server.crt", "../server.key"))
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
# gRPC Iris Example
|
||||
|
||||
## Generate TLS Keys
|
||||
|
||||
```sh
|
||||
$ openssl genrsa -out server.key 2048
|
||||
$ openssl req -new -x509 -sha256 -key server.key -out server.crt -days 3650
|
||||
```
|
||||
|
||||
## Install the protoc Go plugin
|
||||
|
||||
```sh
|
||||
$ go get -u github.com/golang/protobuf/protoc-gen-go
|
||||
```
|
||||
|
||||
## Generate proto
|
||||
|
||||
```sh
|
||||
$ protoc -I helloworld/ helloworld/helloworld.proto --go_out=plugins=grpc:helloworld
|
||||
```
|
||||
@@ -0,0 +1,52 @@
|
||||
// Package main implements a client for Greeter service.
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"log"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
pb "github.com/kataras/iris/v12/_examples/mvc/grpc-compatible/helloworld"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials"
|
||||
)
|
||||
|
||||
const (
|
||||
address = "localhost:443"
|
||||
defaultName = "world"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Set up a connection to the server.
|
||||
// cred, err := credentials.NewClientTLSFromFile("../server.crt", "localhost")
|
||||
// if err != nil {
|
||||
// log.Fatal(err)
|
||||
// }
|
||||
cred := credentials.NewTLS(&tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
Renegotiation: tls.RenegotiateNever,
|
||||
})
|
||||
|
||||
conn, err := grpc.Dial(address, grpc.WithTransportCredentials(cred), grpc.WithBlock())
|
||||
if err != nil {
|
||||
log.Fatalf("did not connect: %v", err)
|
||||
}
|
||||
defer conn.Close()
|
||||
c := pb.NewGreeterClient(conn)
|
||||
|
||||
// Contact the server and print out its response.
|
||||
name := defaultName
|
||||
if len(os.Args) > 1 {
|
||||
name = os.Args[1]
|
||||
}
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
|
||||
defer cancel()
|
||||
r, err := c.SayHello(ctx, &pb.HelloRequest{Name: name})
|
||||
if err != nil {
|
||||
log.Fatalf("could not greet: %v", err)
|
||||
}
|
||||
log.Printf("Greeting: %s", r.GetMessage())
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
# Helloworld gRPC Example
|
||||
|
||||
https://github.com/grpc/grpc-go/tree/master/examples/helloworld
|
||||
@@ -0,0 +1,315 @@
|
||||
// Copyright 2015 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.21.0
|
||||
// protoc v3.11.1
|
||||
// source: helloworld.proto
|
||||
|
||||
package helloworld
|
||||
|
||||
import (
|
||||
context "context"
|
||||
grpc "google.golang.org/grpc"
|
||||
codes "google.golang.org/grpc/codes"
|
||||
status "google.golang.org/grpc/status"
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
)
|
||||
|
||||
const (
|
||||
// Verify that this generated code is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
// The request message containing the user's name.
|
||||
type HelloRequest struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
|
||||
}
|
||||
|
||||
func (x *HelloRequest) Reset() {
|
||||
*x = HelloRequest{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_helloworld_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *HelloRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*HelloRequest) ProtoMessage() {}
|
||||
|
||||
func (x *HelloRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_helloworld_proto_msgTypes[0]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use HelloRequest.ProtoReflect.Descriptor instead.
|
||||
func (*HelloRequest) Descriptor() ([]byte, []int) {
|
||||
return file_helloworld_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
func (x *HelloRequest) GetName() string {
|
||||
if x != nil {
|
||||
return x.Name
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// The response message containing the greetings
|
||||
type HelloReply struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"`
|
||||
}
|
||||
|
||||
func (x *HelloReply) Reset() {
|
||||
*x = HelloReply{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_helloworld_proto_msgTypes[1]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *HelloReply) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*HelloReply) ProtoMessage() {}
|
||||
|
||||
func (x *HelloReply) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_helloworld_proto_msgTypes[1]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use HelloReply.ProtoReflect.Descriptor instead.
|
||||
func (*HelloReply) Descriptor() ([]byte, []int) {
|
||||
return file_helloworld_proto_rawDescGZIP(), []int{1}
|
||||
}
|
||||
|
||||
func (x *HelloReply) GetMessage() string {
|
||||
if x != nil {
|
||||
return x.Message
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
var File_helloworld_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_helloworld_proto_rawDesc = []byte{
|
||||
0x0a, 0x10, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x2e, 0x70, 0x72, 0x6f,
|
||||
0x74, 0x6f, 0x12, 0x0a, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x22, 0x22,
|
||||
0x0a, 0x0c, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12,
|
||||
0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61,
|
||||
0x6d, 0x65, 0x22, 0x26, 0x0a, 0x0a, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79,
|
||||
0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28,
|
||||
0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x32, 0x49, 0x0a, 0x07, 0x47, 0x72,
|
||||
0x65, 0x65, 0x74, 0x65, 0x72, 0x12, 0x3e, 0x0a, 0x08, 0x53, 0x61, 0x79, 0x48, 0x65, 0x6c, 0x6c,
|
||||
0x6f, 0x12, 0x18, 0x2e, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x2e, 0x48,
|
||||
0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x68, 0x65,
|
||||
0x6c, 0x6c, 0x6f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65,
|
||||
0x70, 0x6c, 0x79, 0x22, 0x00, 0x42, 0x30, 0x0a, 0x1b, 0x69, 0x6f, 0x2e, 0x67, 0x72, 0x70, 0x63,
|
||||
0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x73, 0x2e, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x77,
|
||||
0x6f, 0x72, 0x6c, 0x64, 0x42, 0x0f, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x57, 0x6f, 0x72, 0x6c, 0x64,
|
||||
0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
file_helloworld_proto_rawDescOnce sync.Once
|
||||
file_helloworld_proto_rawDescData = file_helloworld_proto_rawDesc
|
||||
)
|
||||
|
||||
func file_helloworld_proto_rawDescGZIP() []byte {
|
||||
file_helloworld_proto_rawDescOnce.Do(func() {
|
||||
file_helloworld_proto_rawDescData = protoimpl.X.CompressGZIP(file_helloworld_proto_rawDescData)
|
||||
})
|
||||
return file_helloworld_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_helloworld_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
|
||||
var file_helloworld_proto_goTypes = []interface{}{
|
||||
(*HelloRequest)(nil), // 0: helloworld.HelloRequest
|
||||
(*HelloReply)(nil), // 1: helloworld.HelloReply
|
||||
}
|
||||
var file_helloworld_proto_depIdxs = []int32{
|
||||
0, // 0: helloworld.Greeter.SayHello:input_type -> helloworld.HelloRequest
|
||||
1, // 1: helloworld.Greeter.SayHello:output_type -> helloworld.HelloReply
|
||||
1, // [1:2] is the sub-list for method output_type
|
||||
0, // [0:1] is the sub-list for method input_type
|
||||
0, // [0:0] is the sub-list for extension type_name
|
||||
0, // [0:0] is the sub-list for extension extendee
|
||||
0, // [0:0] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_helloworld_proto_init() }
|
||||
func file_helloworld_proto_init() {
|
||||
if File_helloworld_proto != nil {
|
||||
return
|
||||
}
|
||||
if !protoimpl.UnsafeEnabled {
|
||||
file_helloworld_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*HelloRequest); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_helloworld_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*HelloReply); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_helloworld_proto_rawDesc,
|
||||
NumEnums: 0,
|
||||
NumMessages: 2,
|
||||
NumExtensions: 0,
|
||||
NumServices: 1,
|
||||
},
|
||||
GoTypes: file_helloworld_proto_goTypes,
|
||||
DependencyIndexes: file_helloworld_proto_depIdxs,
|
||||
MessageInfos: file_helloworld_proto_msgTypes,
|
||||
}.Build()
|
||||
File_helloworld_proto = out.File
|
||||
file_helloworld_proto_rawDesc = nil
|
||||
file_helloworld_proto_goTypes = nil
|
||||
file_helloworld_proto_depIdxs = nil
|
||||
}
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ context.Context
|
||||
var _ grpc.ClientConnInterface
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the grpc package it is being compiled against.
|
||||
const _ = grpc.SupportPackageIsVersion6
|
||||
|
||||
// GreeterClient is the client API for Greeter service.
|
||||
//
|
||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
|
||||
type GreeterClient interface {
|
||||
// Sends a greeting
|
||||
SayHello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloReply, error)
|
||||
}
|
||||
|
||||
type greeterClient struct {
|
||||
cc grpc.ClientConnInterface
|
||||
}
|
||||
|
||||
func NewGreeterClient(cc grpc.ClientConnInterface) GreeterClient {
|
||||
return &greeterClient{cc}
|
||||
}
|
||||
|
||||
func (c *greeterClient) SayHello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloReply, error) {
|
||||
out := new(HelloReply)
|
||||
err := c.cc.Invoke(ctx, "/helloworld.Greeter/SayHello", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// GreeterServer is the server API for Greeter service.
|
||||
type GreeterServer interface {
|
||||
// Sends a greeting
|
||||
SayHello(context.Context, *HelloRequest) (*HelloReply, error)
|
||||
}
|
||||
|
||||
// UnimplementedGreeterServer can be embedded to have forward compatible implementations.
|
||||
type UnimplementedGreeterServer struct {
|
||||
}
|
||||
|
||||
func (*UnimplementedGreeterServer) SayHello(context.Context, *HelloRequest) (*HelloReply, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method SayHello not implemented")
|
||||
}
|
||||
|
||||
func RegisterGreeterServer(s *grpc.Server, srv GreeterServer) {
|
||||
s.RegisterService(&_Greeter_serviceDesc, srv)
|
||||
}
|
||||
|
||||
func _Greeter_SayHello_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(HelloRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(GreeterServer).SayHello(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/helloworld.Greeter/SayHello",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(GreeterServer).SayHello(ctx, req.(*HelloRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
var _Greeter_serviceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "helloworld.Greeter",
|
||||
HandlerType: (*GreeterServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "SayHello",
|
||||
Handler: _Greeter_SayHello_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "helloworld.proto",
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
// Copyright 2015 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
syntax = "proto3";
|
||||
|
||||
package helloworld;
|
||||
|
||||
// The greeting service definition.
|
||||
service Greeter {
|
||||
// Sends a greeting
|
||||
rpc SayHello (HelloRequest) returns (HelloReply) {}
|
||||
}
|
||||
|
||||
// The request message containing the user's name.
|
||||
message HelloRequest {
|
||||
string name = 1;
|
||||
}
|
||||
|
||||
// The response message containing the greetings
|
||||
message HelloReply {
|
||||
string message = 1;
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
pb "github.com/kataras/iris/v12/_examples/mvc/grpc-compatible/helloworld"
|
||||
)
|
||||
|
||||
func main() {
|
||||
b, err := os.ReadFile("../server.crt")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
cp := x509.NewCertPool()
|
||||
if !cp.AppendCertsFromPEM(b) {
|
||||
log.Fatal("credentials: failed to append certificates")
|
||||
}
|
||||
|
||||
transport := &http.Transport{
|
||||
TLSClientConfig: &tls.Config{
|
||||
RootCAs: cp,
|
||||
},
|
||||
}
|
||||
|
||||
client := http.Client{Transport: transport}
|
||||
buf := new(bytes.Buffer)
|
||||
err = json.NewEncoder(buf).Encode(pb.HelloRequest{Name: "world"})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
resp, err := client.Post("https://localhost/helloworld.Greeter/SayHello", "application/json", buf)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var reply pb.HelloReply
|
||||
err = json.NewDecoder(resp.Body).Decode(&reply)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
log.Printf("Greeting: %s", reply.GetMessage())
|
||||
}
|
||||
@@ -2,66 +2,94 @@ package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
|
||||
pb "github.com/kataras/iris/v12/_examples/mvc/grpc-compatible/helloworld"
|
||||
|
||||
"github.com/kataras/iris/v12"
|
||||
"github.com/kataras/iris/v12/mvc"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
// See https://github.com/kataras/iris/issues/1449
|
||||
// for more details but in-short you can convert Iris MVC to gRPC methods by
|
||||
// binding the `context.Context` from `iris.Context.Request().Context()` and gRPC input and output data.
|
||||
// Iris automatically binds the standard "context" context.Context to `iris.Context.Request().Context()`
|
||||
// and any other structure that is not mapping to a registered dependency
|
||||
// as a payload depends on the request, e.g XML, YAML, Query, Form, JSON.
|
||||
//
|
||||
// Useful to use gRPC services as Iris controllers fast and without wrappers.
|
||||
|
||||
func main() {
|
||||
app := newApp()
|
||||
app.Logger().SetLevel("debug")
|
||||
|
||||
// POST: http://localhost:8080/login
|
||||
// with request data: {"username": "makis"}
|
||||
// and expected output: {"message": "makis logged"}
|
||||
app.Listen(":8080")
|
||||
// The Iris server should ran under TLS (it's a gRPC requirement).
|
||||
// POST: https://localhost:443/helloworld.Greeter/SayHello
|
||||
// with request data: {"name": "John"}
|
||||
// and expected output: {"message": "Hello John"}
|
||||
app.Run(iris.TLS(":443", "server.crt", "server.key"))
|
||||
}
|
||||
|
||||
func newApp() *iris.Application {
|
||||
app := iris.New()
|
||||
// app.Configure(iris.WithLowercaseRouting) // OPTIONAL.
|
||||
|
||||
app.Get("/", func(ctx iris.Context) {
|
||||
ctx.HTML("<h1>Index Page</h1>")
|
||||
})
|
||||
|
||||
ctrl := &myController{}
|
||||
// Register gRPC server.
|
||||
grpcServer := grpc.NewServer()
|
||||
pb.RegisterGreeterServer(grpcServer, ctrl)
|
||||
|
||||
// serviceName := pb.File_helloworld_proto.Services().Get(0).FullName()
|
||||
|
||||
// Register MVC application controller for gRPC services.
|
||||
// You can bind as many mvc gRpc services in the same Party or app,
|
||||
// as the ServiceName differs.
|
||||
mvc.New(app).
|
||||
// Request-scope binding for context.Context-type controller's method or field.
|
||||
// (or import github.com/kataras/iris/v12/hero and hero.Register(...))
|
||||
Register(func(ctx iris.Context) context.Context {
|
||||
return ctx.Request().Context()
|
||||
}).
|
||||
// Bind loginRequest.
|
||||
// Register(func(ctx iris.Context) loginRequest {
|
||||
// var req loginRequest
|
||||
// ctx.ReadJSON(&req)
|
||||
// return req
|
||||
// }).
|
||||
// OR
|
||||
// Bind any other structure or pointer to a structure from request's
|
||||
// XML
|
||||
// YAML
|
||||
// Query
|
||||
// Form
|
||||
// JSON (default, if not client's "Content-Type" specified otherwise)
|
||||
Register(mvc.AutoBinding).
|
||||
Handle(&myController{})
|
||||
Register(new(myService)).
|
||||
Handle(ctrl, mvc.GRPC{
|
||||
Server: grpcServer, // Required.
|
||||
ServiceName: "helloworld.Greeter", // Required.
|
||||
Strict: false,
|
||||
})
|
||||
|
||||
return app
|
||||
}
|
||||
|
||||
type myController struct{}
|
||||
|
||||
type loginRequest struct {
|
||||
Username string `json:"username"`
|
||||
type service interface {
|
||||
DoSomething() error
|
||||
}
|
||||
|
||||
type loginResponse struct {
|
||||
Message string `json:"message"`
|
||||
type myService struct{}
|
||||
|
||||
func (s *myService) DoSomething() error {
|
||||
log.Println("service: DoSomething")
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *myController) PostLogin(ctx context.Context, input loginRequest) (loginResponse, error) {
|
||||
// [use of ctx to call a gRpc method or a database call...]
|
||||
return loginResponse{
|
||||
Message: input.Username + " logged",
|
||||
}, nil
|
||||
type myController struct {
|
||||
// Ctx iris.Context
|
||||
|
||||
SingletonDependency service
|
||||
}
|
||||
|
||||
// SayHello implements helloworld.GreeterServer.
|
||||
// See https://github.com/kataras/iris/issues/1449#issuecomment-625570442
|
||||
// for the comments below (https://github.com/iris-contrib/swagger).
|
||||
//
|
||||
// @Description greet service
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Success 200 {string} string "Hello {name}"
|
||||
// @Router /helloworld.Greeter/SayHello [post]
|
||||
func (c *myController) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
|
||||
err := c.SingletonDependency.DoSomething()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &pb.HelloReply{Message: "Hello " + in.GetName()}, nil
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ func TestGRPCCompatible(t *testing.T) {
|
||||
app := newApp()
|
||||
|
||||
e := httptest.New(t, app)
|
||||
e.POST("/login").WithJSON(map[string]string{"username": "makis"}).Expect().
|
||||
e.POST("/helloworld.Greeter/SayHello").WithJSON(map[string]string{"name": "makis"}).Expect().
|
||||
Status(httptest.StatusOK).
|
||||
JSON().Equal(map[string]string{"message": "makis logged"})
|
||||
JSON().IsEqual(map[string]string{"message": "Hello makis"})
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDKjCCAhICCQDpz77z0oyjCDANBgkqhkiG9w0BAQsFADBXMQswCQYDVQQGEwJH
|
||||
UjEPMA0GA1UECAwGQXRoZW5zMQ8wDQYDVQQHDAZBdGhlbnMxJjAkBgkqhkiG9w0B
|
||||
CQEWF2thdGFyYXMyMDA2QGhvdG1haWwuY29tMB4XDTIyMDMwMzExMjczM1oXDTMy
|
||||
MDIyOTExMjczM1owVzELMAkGA1UEBhMCR1IxDzANBgNVBAgMBkF0aGVuczEPMA0G
|
||||
A1UEBwwGQXRoZW5zMSYwJAYJKoZIhvcNAQkBFhdrYXRhcmFzMjAwNkBob3RtYWls
|
||||
LmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALu4Uqpcf0AnRDwW
|
||||
QGDh//v2wDNQ2EP/jn2Y4YxYJXtvWZ7dWcoX4fPA03mOAEmEPXWGxkhe8DYFh8PY
|
||||
0zpZW5sFY7ae0AcpcjvlyxjNvHsqjhnh7M1gKZuhfyxvvLv7afC5Gs64tyg+f4C3
|
||||
EqvwKHV+fjC8eKiASs4FsEEi89uv2AZdD9Vp0Zvbz6/1mRQQWBMpsPiIO7/WsaW4
|
||||
DPq/zUZVY15WaX5B0Fg57O5Uv8aHyV4A+AQN7XzadCpYBp8XUb/cVcN9Azj2F6d3
|
||||
WODyYT7y/RMuuih9HJQMxuAYPNB5UAkW6syTdfjbdyqbXe45z5iKPcotE4KaXYJf
|
||||
hTkj6osCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAsb3QGQUGNGs8zv2y4QmsDCB9
|
||||
+mkzwH9BpqSo40Zs19kT35nH0u//UuXiNG3U8d2WH1J7kk64cdrETuVay5TwW9jJ
|
||||
EagdJl4zAZbfLQuDUCI+cVpjbywbO//GXzi8Q/aO7viboU0OR+MPbabubtKE7f+X
|
||||
THIjnMj3OhFM4zzrFyEL8gsRMLzRRCV18wTsiCl9bi4bZ1Ssvr6MwqyXHs3uK+fx
|
||||
JpxQVb3MckoIgUKwwU4MlXwufDkOmslGFsRg4e1iiwlkJ0HgRdf5lSuDmnBjH4T+
|
||||
fWvRqCeHDTjOI09+7IAK1EF7rw4TZjHv+RbPl5+b1TJcanUC9EDg4z4/rR0XOg==
|
||||
-----END CERTIFICATE-----
|
||||
@@ -0,0 +1,27 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpQIBAAKCAQEAu7hSqlx/QCdEPBZAYOH/+/bAM1DYQ/+OfZjhjFgle29Znt1Z
|
||||
yhfh88DTeY4ASYQ9dYbGSF7wNgWHw9jTOllbmwVjtp7QBylyO+XLGM28eyqOGeHs
|
||||
zWApm6F/LG+8u/tp8Lkazri3KD5/gLcSq/AodX5+MLx4qIBKzgWwQSLz26/YBl0P
|
||||
1WnRm9vPr/WZFBBYEymw+Ig7v9axpbgM+r/NRlVjXlZpfkHQWDns7lS/xofJXgD4
|
||||
BA3tfNp0KlgGnxdRv9xVw30DOPYXp3dY4PJhPvL9Ey66KH0clAzG4Bg80HlQCRbq
|
||||
zJN1+Nt3Kptd7jnPmIo9yi0Tgppdgl+FOSPqiwIDAQABAoIBAQCg0XM4cc+uVTV2
|
||||
yJVUqqjT4fucusjb0EbxQJUR174ctjMwD2/J25X+bhZ9z3JdiQXh9plODM97aFd8
|
||||
J/glx8Hb180p+Xo8eHxd5iqNUEwFtFpSwCNPeu+KXduGZR9qaCPFT78wlDyNJKW0
|
||||
zqIXXMI8jiZreDtiF65+O49Y7im97Lqwhw8fEABcr3rpOpjK79nPHkf0X3CKj1TC
|
||||
IhW5Av4j41uv8+1BgObjVkeYFHK4O+vRWiSId9OiXzZT9ZaG56ntGOap9KuugTym
|
||||
2ngbIBm2DdUDmbY3vL9eUzhfY75VIjFrgnf/EYN+7kymbymo3vnv5OmB3OByX7iw
|
||||
bXTmxQgxAoGBAOLLx6EzcQJ83g85Gtd3WNiNW414ynDPyIB/XAEcyygZGYqrVfsH
|
||||
HfGhxI787WqzjIZxl987HTPOkrX5Xc/GhHNeTVa1+neyUUqx46DxessU3/s53Vg4
|
||||
X1AmnmL6+Wl1qrlTJiUQINDyOZkHsbycR8okDlrzSS1rwO90wW6K5snDAoGBANPk
|
||||
a/NH2t+KrAboPtXZk2DCUMkCoyDhzXnPU4HARO0BRa3Qhul8keV8u54MtNsej1y+
|
||||
TcatUQYpuT4Pgpl1kdtyHrLNiEhd0ThBgy5RmS/rLk9XnuWgfeth5bGxbEIlHXMN
|
||||
HvoxL34UhGxwWFCHKr5vV2OYVcPLkbdDBhLneweZAoGAaL6tCGp1uyxocqdxGipo
|
||||
wjsnGYO8G7YbaB1qJKljurU88qqHH1T+I2cPHOr7y9f5Au7bsaHfrtmtMJZnGVsa
|
||||
OR5Ioc+SSk309YaLFv3wNHMDr0feTqxaeO4dIKHBJ0/M9aLNbzivr1DwARlooS+c
|
||||
iGN2rdLG7U9i4DUQUTmdtXkCgYEAllEJM8DZyJN7jjrbuKFtJ8sxvCeeygjl12/4
|
||||
8acQPoIUiEXSL3krlv1xq6Gf+4ImeciXLEZvoEuhGiGuqGb7Xg4LMRUVhSDo91ui
|
||||
UA2a+p+AbtDd7FB6g60jYXdYMWRbC+9W9m5GHs83UiYwwI/jBs291O2QiiGz8aoe
|
||||
ePK2GKECgYEAg7PYp1uOWEuA39gqrKmuLA5f/i2bGgBrajk90m+9kgNwZ9RqeZJG
|
||||
V97hRwO86EwBBqhMUjaFv/zwSjKhCZUE8vOt5DF4sH5Ppa9j/kqrzsZjlKjajj/U
|
||||
tH1iFSXz/+PwQVeNssfjFMUPwWWsGSN5pvqx/5Ru/DIvPLHOg3YbEQc=
|
||||
-----END RSA PRIVATE KEY-----
|
||||
@@ -1,15 +1,16 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/kataras/iris/v12"
|
||||
"github.com/kataras/iris/v12/mvc"
|
||||
"strings"
|
||||
|
||||
"github.com/kataras/iris/v12"
|
||||
"github.com/kataras/iris/v12/middleware/logger"
|
||||
"github.com/kataras/iris/v12/middleware/recover"
|
||||
"github.com/kataras/iris/v12/mvc"
|
||||
)
|
||||
|
||||
// This example is equivalent to the
|
||||
// https://github.com/kataras/iris/blob/master/_examples/hello-world/main.go
|
||||
// https://github.com/kataras/iris/blob/main/_examples/hello-world/main.go
|
||||
//
|
||||
// It seems that additional code you
|
||||
// have to write doesn't worth it
|
||||
@@ -40,6 +41,16 @@ func newApp() *iris.Application {
|
||||
|
||||
// Serve a controller based on the root Router, "/".
|
||||
mvc.New(app).Handle(new(ExampleController))
|
||||
// Add custom path func
|
||||
mvc.New(app).SetCustomPathWordFunc(func(path, w string, wordIndex int) string {
|
||||
|
||||
if wordIndex == 0 {
|
||||
w = strings.ToLower(w)
|
||||
}
|
||||
path += w
|
||||
return path
|
||||
|
||||
}).Handle(new(ExampleControllerCustomPath))
|
||||
return app
|
||||
}
|
||||
|
||||
@@ -50,7 +61,7 @@ func main() {
|
||||
// http://localhost:8080/ping
|
||||
// http://localhost:8080/hello
|
||||
// http://localhost:8080/custom_path
|
||||
app.Run(iris.Addr(":8080"))
|
||||
app.Listen(":8080")
|
||||
}
|
||||
|
||||
// ExampleController serves the "/", "/ping" and "/hello".
|
||||
@@ -80,6 +91,13 @@ func (c *ExampleController) GetHello() interface{} {
|
||||
return map[string]string{"message": "Hello Iris!"}
|
||||
}
|
||||
|
||||
// GetHelloWorld serves
|
||||
// Method: GET
|
||||
// Resource: http://localhost:8080/hello/world
|
||||
func (c *ExampleController) GetHelloWorld() interface{} {
|
||||
return map[string]string{"message": "Hello Iris! DefaultPath"}
|
||||
}
|
||||
|
||||
// BeforeActivation called once, before the controller adapted to the main application
|
||||
// and of course before the server ran.
|
||||
// After version 9 you can also add custom routes for a specific controller's methods.
|
||||
@@ -105,6 +123,15 @@ func (c *ExampleController) CustomHandlerWithoutFollowingTheNamingGuide() string
|
||||
return "hello from the custom handler without following the naming guide"
|
||||
}
|
||||
|
||||
type ExampleControllerCustomPath struct{}
|
||||
|
||||
// GetHelloWorld serves
|
||||
// Method: GET
|
||||
// Resource: http://localhost:8080/helloWorld
|
||||
func (c *ExampleControllerCustomPath) GetHelloWorld() interface{} {
|
||||
return map[string]string{"message": "Hello Iris! CustomPath"}
|
||||
}
|
||||
|
||||
// GetUserBy serves
|
||||
// Method: GET
|
||||
// Resource: http://localhost:8080/user/{username:string}
|
||||
|
||||
@@ -10,14 +10,14 @@ func TestMVCHelloWorld(t *testing.T) {
|
||||
e := httptest.New(t, newApp())
|
||||
|
||||
e.GET("/").Expect().Status(httptest.StatusOK).
|
||||
ContentType("text/html", "utf-8").Body().Equal("<h1>Welcome</h1>")
|
||||
ContentType("text/html", "utf-8").Body().IsEqual("<h1>Welcome</h1>")
|
||||
|
||||
e.GET("/ping").Expect().Status(httptest.StatusOK).
|
||||
Body().Equal("pong")
|
||||
Body().IsEqual("pong")
|
||||
|
||||
e.GET("/hello").Expect().Status(httptest.StatusOK).
|
||||
JSON().Object().Value("message").Equal("Hello Iris!")
|
||||
|
||||
e.GET("/custom_path").Expect().Status(httptest.StatusOK).
|
||||
Body().Equal("hello from the custom handler without following the naming guide")
|
||||
Body().IsEqual("hello from the custom handler without following the naming guide")
|
||||
}
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 28 KiB |
@@ -0,0 +1,56 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/kataras/iris/v12/_examples/mvc/login-mvc-single-responsibility/user"
|
||||
|
||||
"github.com/kataras/iris/v12"
|
||||
"github.com/kataras/iris/v12/mvc"
|
||||
"github.com/kataras/iris/v12/sessions"
|
||||
)
|
||||
|
||||
func main() {
|
||||
app := iris.New()
|
||||
// You got full debug messages, useful when using MVC and you want to make
|
||||
// sure that your code is aligned with the Iris' MVC Architecture.
|
||||
app.Logger().SetLevel("debug")
|
||||
|
||||
app.RegisterView(iris.HTML("./views", ".html").Layout("shared/layout.html"))
|
||||
|
||||
app.HandleDir("/public", iris.Dir("./public"))
|
||||
|
||||
userRouter := app.Party("/user")
|
||||
{
|
||||
manager := sessions.New(sessions.Config{
|
||||
Cookie: "sessioncookiename",
|
||||
Expires: 24 * time.Hour,
|
||||
})
|
||||
userRouter.Use(manager.Handler())
|
||||
mvc.Configure(userRouter, configureUserMVC)
|
||||
}
|
||||
|
||||
// http://localhost:8080/user/register
|
||||
// http://localhost:8080/user/login
|
||||
// http://localhost:8080/user/me
|
||||
// http://localhost:8080/user/logout
|
||||
// http://localhost:8080/user/1
|
||||
app.Listen(":8080", configure)
|
||||
}
|
||||
|
||||
func configureUserMVC(userApp *mvc.Application) {
|
||||
userApp.Register(
|
||||
user.NewDataSource(),
|
||||
)
|
||||
userApp.Handle(new(user.Controller))
|
||||
}
|
||||
|
||||
func configure(app *iris.Application) {
|
||||
app.Configure(
|
||||
iris.WithOptimizations,
|
||||
iris.WithFireMethodNotAllowed,
|
||||
iris.WithLowercaseRouting,
|
||||
iris.WithPathIntelligence,
|
||||
iris.WithTunneling,
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/* Bordered form */
|
||||
form {
|
||||
border: 3px solid #f1f1f1;
|
||||
}
|
||||
|
||||
/* Full-width inputs */
|
||||
input[type=text], input[type=password] {
|
||||
width: 100%;
|
||||
padding: 12px 20px;
|
||||
margin: 8px 0;
|
||||
display: inline-block;
|
||||
border: 1px solid #ccc;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* Set a style for all buttons */
|
||||
button {
|
||||
background-color: #4CAF50;
|
||||
color: white;
|
||||
padding: 14px 20px;
|
||||
margin: 8px 0;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* Add a hover effect for buttons */
|
||||
button:hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
/* Extra style for the cancel button (red) */
|
||||
.cancelbtn {
|
||||
width: auto;
|
||||
padding: 10px 18px;
|
||||
background-color: #f44336;
|
||||
}
|
||||
|
||||
/* Center the container */
|
||||
|
||||
/* Add padding to containers */
|
||||
.container {
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
/* The "Forgot password" text */
|
||||
span.psw {
|
||||
float: right;
|
||||
padding-top: 16px;
|
||||
}
|
||||
|
||||
/* Change styles for span and cancel button on extra small screens */
|
||||
@media screen and (max-width: 300px) {
|
||||
span.psw {
|
||||
display: block;
|
||||
float: none;
|
||||
}
|
||||
.cancelbtn {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
package user
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/kataras/iris/v12"
|
||||
"github.com/kataras/iris/v12/mvc"
|
||||
"github.com/kataras/iris/v12/sessions"
|
||||
)
|
||||
|
||||
const sessionIDKey = "UserID"
|
||||
|
||||
// paths
|
||||
var (
|
||||
PathLogin = mvc.Response{Path: "/user/login"}
|
||||
PathLogout = mvc.Response{Path: "/user/logout"}
|
||||
)
|
||||
|
||||
// AuthController is the user authentication controller, a custom shared controller.
|
||||
type AuthController struct {
|
||||
// context is auto-binded if struct depends on this,
|
||||
// in this controller we don't we do everything with mvc-style,
|
||||
// and that's neither the 30% of its features.
|
||||
// Ctx iris.Context
|
||||
|
||||
Source *DataSource
|
||||
Session *sessions.Session
|
||||
|
||||
// the whole controller is request-scoped because we already depend on Session, so
|
||||
// this will be new for each new incoming request, BeginRequest sets that based on the session.
|
||||
UserID int64
|
||||
}
|
||||
|
||||
// BeginRequest saves login state to the context, the user id.
|
||||
func (c *AuthController) BeginRequest(ctx iris.Context) {
|
||||
c.UserID, _ = c.Session.GetInt64(sessionIDKey)
|
||||
}
|
||||
|
||||
// EndRequest is here just to complete the BaseController
|
||||
// in order to be tell iris to call the `BeginRequest` before the main method.
|
||||
func (c *AuthController) EndRequest(ctx iris.Context) {}
|
||||
|
||||
func (c *AuthController) fireError(err error) mvc.View {
|
||||
return mvc.View{
|
||||
Code: iris.StatusBadRequest,
|
||||
Name: "shared/error.html",
|
||||
Data: iris.Map{"Title": "User Error", "Message": strings.ToUpper(err.Error())},
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AuthController) redirectTo(id int64) mvc.Response {
|
||||
return mvc.Response{Path: "/user/" + strconv.Itoa(int(id))}
|
||||
}
|
||||
|
||||
func (c *AuthController) createOrUpdate(firstname, username, password string) (user Model, err error) {
|
||||
username = strings.Trim(username, " ")
|
||||
if username == "" || password == "" || firstname == "" {
|
||||
return user, errors.New("empty firstname, username or/and password")
|
||||
}
|
||||
|
||||
userToInsert := Model{
|
||||
Firstname: firstname,
|
||||
Username: username,
|
||||
password: password,
|
||||
} // password is hashed by the Source.
|
||||
|
||||
newUser, err := c.Source.InsertOrUpdate(userToInsert)
|
||||
if err != nil {
|
||||
return user, err
|
||||
}
|
||||
|
||||
return newUser, nil
|
||||
}
|
||||
|
||||
func (c *AuthController) isLoggedIn() bool {
|
||||
// we don't search by session, we have the user id
|
||||
// already by the `BeginRequest` middleware.
|
||||
return c.UserID > 0
|
||||
}
|
||||
|
||||
func (c *AuthController) verify(username, password string) (user Model, err error) {
|
||||
if username == "" || password == "" {
|
||||
return user, errors.New("please fill both username and password fields")
|
||||
}
|
||||
|
||||
u, found := c.Source.GetByUsername(username)
|
||||
if !found {
|
||||
// if user found with that username not found at all.
|
||||
return user, errors.New("user with that username does not exist")
|
||||
}
|
||||
|
||||
if ok, err := ValidatePassword(password, u.HashedPassword); err != nil || !ok {
|
||||
// if user found but an error occurred or the password is not valid.
|
||||
return user, errors.New("please try to login with valid credentials")
|
||||
}
|
||||
|
||||
return u, nil
|
||||
}
|
||||
|
||||
// if logged in then destroy the session
|
||||
// and redirect to the login page
|
||||
// otherwise redirect to the registration page.
|
||||
func (c *AuthController) logout() mvc.Response {
|
||||
if c.isLoggedIn() {
|
||||
c.Session.Destroy()
|
||||
}
|
||||
return PathLogin
|
||||
}
|
||||
@@ -0,0 +1,189 @@
|
||||
package user
|
||||
|
||||
import (
|
||||
"github.com/kataras/iris/v12"
|
||||
"github.com/kataras/iris/v12/mvc"
|
||||
)
|
||||
|
||||
var (
|
||||
// About Code: iris.StatusSeeOther ->
|
||||
// When redirecting from POST to GET request you -should- use this HTTP status code,
|
||||
// however there're some (complicated) alternatives if you
|
||||
// search online or even the HTTP RFC.
|
||||
// "See Other" RFC 7231
|
||||
pathMyProfile = mvc.Response{Path: "/user/me", Code: iris.StatusSeeOther}
|
||||
pathRegister = mvc.Response{Path: "/user/register"}
|
||||
)
|
||||
|
||||
// Controller is responsible to handle the following requests:
|
||||
// GET /user/register
|
||||
// POST /user/register
|
||||
// GET /user/login
|
||||
// POST /user/login
|
||||
// GET /user/me
|
||||
// GET /user/{id:int64}
|
||||
// All HTTP Methods /user/logout
|
||||
type Controller struct {
|
||||
AuthController
|
||||
}
|
||||
|
||||
type formValue func(string) string
|
||||
|
||||
// BeforeActivation called once before the server start
|
||||
// and before the controller's registration, here you can add
|
||||
// dependencies, to this controller and only, that the main caller may skip.
|
||||
func (c *Controller) BeforeActivation(b mvc.BeforeActivation) {
|
||||
// bind the context's `FormValue` as well in order to be
|
||||
// acceptable on the controller or its methods' input arguments (NEW feature as well).
|
||||
b.Dependencies().Register(func(ctx iris.Context) formValue { return ctx.FormValue })
|
||||
}
|
||||
|
||||
type page struct {
|
||||
Title string
|
||||
}
|
||||
|
||||
// GetRegister handles GET:/user/register.
|
||||
// mvc.Result can accept any struct which contains a `Dispatch(ctx iris.Context)` method.
|
||||
// Both mvc.Response and mvc.View are mvc.Result.
|
||||
func (c *Controller) GetRegister() mvc.Result {
|
||||
if c.isLoggedIn() {
|
||||
return c.logout()
|
||||
}
|
||||
|
||||
// You could just use it as a variable to win some time in serve-time,
|
||||
// this is an exersise for you :)
|
||||
return mvc.View{
|
||||
Name: pathRegister.Path + ".html",
|
||||
Data: page{"User Registration"},
|
||||
}
|
||||
}
|
||||
|
||||
// PostRegister handles POST:/user/register.
|
||||
func (c *Controller) PostRegister(form formValue) mvc.Result {
|
||||
// we can either use the `c.Ctx.ReadForm` or read values one by one.
|
||||
var (
|
||||
firstname = form("firstname")
|
||||
username = form("username")
|
||||
password = form("password")
|
||||
)
|
||||
|
||||
user, err := c.createOrUpdate(firstname, username, password)
|
||||
if err != nil {
|
||||
return c.fireError(err)
|
||||
}
|
||||
|
||||
// setting a session value was never easier.
|
||||
c.Session.Set(sessionIDKey, user.ID)
|
||||
// succeed, nothing more to do here, just redirect to the /user/me.
|
||||
return pathMyProfile
|
||||
}
|
||||
|
||||
// with these static views,
|
||||
// you can use variables-- that are initialized before server start
|
||||
// so you can win some time on serving.
|
||||
// You can do it else where as well but I let them as pracise for you,
|
||||
// essentially you can understand by just looking below.
|
||||
var userLoginView = mvc.View{
|
||||
Name: PathLogin.Path + ".html",
|
||||
Data: page{"User Login"},
|
||||
}
|
||||
|
||||
// GetLogin handles GET:/user/login.
|
||||
func (c *Controller) GetLogin() mvc.Result {
|
||||
if c.isLoggedIn() {
|
||||
return c.logout()
|
||||
}
|
||||
return userLoginView
|
||||
}
|
||||
|
||||
// PostLogin handles POST:/user/login.
|
||||
func (c *Controller) PostLogin(form formValue) mvc.Result {
|
||||
var (
|
||||
username = form("username")
|
||||
password = form("password")
|
||||
)
|
||||
|
||||
user, err := c.verify(username, password)
|
||||
if err != nil {
|
||||
return c.fireError(err)
|
||||
}
|
||||
|
||||
c.Session.Set(sessionIDKey, user.ID)
|
||||
return pathMyProfile
|
||||
}
|
||||
|
||||
// AnyLogout handles any method on path /user/logout.
|
||||
func (c *Controller) AnyLogout() {
|
||||
c.logout()
|
||||
}
|
||||
|
||||
// GetMe handles GET:/user/me.
|
||||
func (c *Controller) GetMe() mvc.Result {
|
||||
id, err := c.Session.GetInt64(sessionIDKey)
|
||||
if err != nil || id <= 0 {
|
||||
// when not already logged in, redirect to login.
|
||||
return PathLogin
|
||||
}
|
||||
|
||||
u, found := c.Source.GetByID(id)
|
||||
if !found {
|
||||
// if the session exists but for some reason the user doesn't exist in the "database"
|
||||
// then logout him and redirect to the register page.
|
||||
return c.logout()
|
||||
}
|
||||
|
||||
// set the model and render the view template.
|
||||
return mvc.View{
|
||||
Name: pathMyProfile.Path + ".html",
|
||||
Data: iris.Map{
|
||||
"Title": "Profile of " + u.Username,
|
||||
"User": u,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Controller) renderNotFound(id int64) mvc.View {
|
||||
return mvc.View{
|
||||
Code: iris.StatusNotFound,
|
||||
Name: "user/notfound.html",
|
||||
Data: iris.Map{
|
||||
"Title": "User Not Found",
|
||||
"ID": id,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Dispatch completes the `mvc.Result` interface
|
||||
// in order to be able to return a type of `Model`
|
||||
// as mvc.Result.
|
||||
// If this function didn't exist then
|
||||
// we should explicit set the output result to that Model or to an interface{}.
|
||||
func (u Model) Dispatch(ctx iris.Context) {
|
||||
ctx.JSON(u)
|
||||
}
|
||||
|
||||
// GetBy handles GET:/user/{id:int64},
|
||||
// i.e http://localhost:8080/user/1
|
||||
func (c *Controller) GetBy(userID int64) mvc.Result {
|
||||
// we have /user/{id}
|
||||
// fetch and render user json.
|
||||
user, found := c.Source.GetByID(userID)
|
||||
if !found {
|
||||
// not user found with that ID.
|
||||
return c.renderNotFound(userID)
|
||||
}
|
||||
|
||||
// Q: how the hell Model can be return as mvc.Result?
|
||||
// A: I told you before on some comments and the docs,
|
||||
// any struct that has a `Dispatch(ctx iris.Context)`
|
||||
// can be returned as an mvc.Result(see ~20 lines above),
|
||||
// therefore we are able to combine many type of results in the same method.
|
||||
// For example, here, we return either an mvc.View to render a not found custom template
|
||||
// either a user which returns the Model as JSON via its Dispatch.
|
||||
//
|
||||
// We could also return just a struct value that is not an mvc.Result,
|
||||
// if the output result of the `GetBy` was that struct's type or an interface{}
|
||||
// and iris would render that with JSON as well, but here we can't do that without complete the `Dispatch`
|
||||
// function, because we may return an mvc.View which is an mvc.Result.
|
||||
return user
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
package user
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// IDGenerator would be our user ID generator
|
||||
// but here we keep the order of users by their IDs
|
||||
// so we will use numbers that can be easly written
|
||||
// to the browser to get results back from the REST API.
|
||||
// var IDGenerator = func() string {
|
||||
// return uuid.NewV4().String()
|
||||
// }
|
||||
|
||||
// DataSource is our data store example.
|
||||
type DataSource struct {
|
||||
Users map[int64]Model
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
// NewDataSource returns a new user data source.
|
||||
func NewDataSource() *DataSource {
|
||||
return &DataSource{
|
||||
Users: make(map[int64]Model),
|
||||
}
|
||||
}
|
||||
|
||||
// GetBy receives a query function
|
||||
// which is fired for every single user model inside
|
||||
// our imaginary database.
|
||||
// When that function returns true then it stops the iteration.
|
||||
//
|
||||
// It returns the query's return last known boolean value
|
||||
// and the last known user model
|
||||
// to help callers to reduce the loc.
|
||||
//
|
||||
// But be carefully, the caller should always check for the "found"
|
||||
// because it may be false but the user model has actually real data inside it.
|
||||
//
|
||||
// It's actually a simple but very clever prototype function
|
||||
// I'm think of and using everywhere since then,
|
||||
// hope you find it very useful too.
|
||||
func (d *DataSource) GetBy(query func(Model) bool) (user Model, found bool) {
|
||||
d.mu.RLock()
|
||||
for _, user = range d.Users {
|
||||
found = query(user)
|
||||
if found {
|
||||
break
|
||||
}
|
||||
}
|
||||
d.mu.RUnlock()
|
||||
return
|
||||
}
|
||||
|
||||
// GetByID returns a user model based on its ID.
|
||||
func (d *DataSource) GetByID(id int64) (Model, bool) {
|
||||
return d.GetBy(func(u Model) bool {
|
||||
return u.ID == id
|
||||
})
|
||||
}
|
||||
|
||||
// GetByUsername returns a user model based on the Username.
|
||||
func (d *DataSource) GetByUsername(username string) (Model, bool) {
|
||||
return d.GetBy(func(u Model) bool {
|
||||
return u.Username == username
|
||||
})
|
||||
}
|
||||
|
||||
func (d *DataSource) getLastID() (lastID int64) {
|
||||
d.mu.RLock()
|
||||
for id := range d.Users {
|
||||
if id > lastID {
|
||||
lastID = id
|
||||
}
|
||||
}
|
||||
d.mu.RUnlock()
|
||||
|
||||
return lastID
|
||||
}
|
||||
|
||||
// InsertOrUpdate adds or updates a user to the (memory) storage.
|
||||
func (d *DataSource) InsertOrUpdate(user Model) (Model, error) {
|
||||
// no matter what we will update the password hash
|
||||
// for both update and insert actions.
|
||||
hashedPassword, err := GeneratePassword(user.password)
|
||||
if err != nil {
|
||||
return user, err
|
||||
}
|
||||
user.HashedPassword = hashedPassword
|
||||
|
||||
// update
|
||||
if id := user.ID; id > 0 {
|
||||
_, found := d.GetByID(id)
|
||||
if !found {
|
||||
return user, errors.New("ID should be zero or a valid one that maps to an existing User")
|
||||
}
|
||||
d.mu.Lock()
|
||||
d.Users[id] = user
|
||||
d.mu.Unlock()
|
||||
return user, nil
|
||||
}
|
||||
|
||||
// insert
|
||||
id := d.getLastID() + 1
|
||||
user.ID = id
|
||||
d.mu.Lock()
|
||||
user.CreatedAt = time.Now()
|
||||
d.Users[id] = user
|
||||
d.mu.Unlock()
|
||||
|
||||
return user, nil
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package user
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
// Model is our User example model.
|
||||
type Model struct {
|
||||
ID int64 `json:"id"`
|
||||
Firstname string `json:"firstname"`
|
||||
Username string `json:"username"`
|
||||
// password is the client-given password
|
||||
// which will not be stored anywhere in the server.
|
||||
// It's here only for actions like registration and update password,
|
||||
// because we caccept a Model instance
|
||||
// inside the `DataSource#InsertOrUpdate` function.
|
||||
password string
|
||||
HashedPassword []byte `json:"-"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
}
|
||||
|
||||
// GeneratePassword will generate a hashed password for us based on the
|
||||
// user's input.
|
||||
func GeneratePassword(userPassword string) ([]byte, error) {
|
||||
return bcrypt.GenerateFromPassword([]byte(userPassword), bcrypt.DefaultCost)
|
||||
}
|
||||
|
||||
// ValidatePassword will check if passwords are matched.
|
||||
func ValidatePassword(userPassword string, hashed []byte) (bool, error) {
|
||||
if err := bcrypt.CompareHashAndPassword(hashed, []byte(userPassword)); err != nil {
|
||||
return false, err
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
<h1>Error.</h1>
|
||||
<h2>An error occurred while processing your request.</h2>
|
||||
|
||||
<h3>{{.Message}}</h3>
|
||||
@@ -0,0 +1,12 @@
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>{{.Title}}</title>
|
||||
<link rel="stylesheet" type="text/css" href="/public/css/site.css" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
{{ yield . }}
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,11 @@
|
||||
<form action="/user/login" method="POST">
|
||||
<div class="container">
|
||||
<label><b>Username</b></label>
|
||||
<input type="text" placeholder="Enter Username" name="username" required>
|
||||
|
||||
<label><b>Password</b></label>
|
||||
<input type="password" placeholder="Enter Password" name="password" required>
|
||||
|
||||
<button type="submit">Login</button>
|
||||
</div>
|
||||
</form>
|
||||
@@ -0,0 +1,3 @@
|
||||
<p>
|
||||
Welcome back <strong>{{.User.Firstname}}</strong>!
|
||||
</p>
|
||||
@@ -0,0 +1,3 @@
|
||||
<p>
|
||||
User with ID <strong>{{.ID}}</strong> does not exist.
|
||||
</p>
|
||||
@@ -0,0 +1,14 @@
|
||||
<form action="/user/register" method="POST">
|
||||
<div class="container">
|
||||
<label><b>Firstname</b></label>
|
||||
<input type="text" placeholder="Enter Firstname" name="firstname" required>
|
||||
|
||||
<label><b>Username</b></label>
|
||||
<input type="text" placeholder="Enter Username" name="username" required>
|
||||
|
||||
<label><b>Password</b></label>
|
||||
<input type="password" placeholder="Enter Password" name="password" required>
|
||||
|
||||
<button type="submit">Register</button>
|
||||
</div>
|
||||
</form>
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
// Keep note that the tags for public-use (for our web app)
|
||||
// should be kept in other file like "web/viewmodels/user.go"
|
||||
// which could wrap by embedding the datamodels.User or
|
||||
// define completely new fields instead but for the shake
|
||||
// define completely new fields instead but for the sake
|
||||
// of the example, we will use this datamodel
|
||||
// as the only one User model in our application.
|
||||
type User struct {
|
||||
|
||||
@@ -21,10 +21,10 @@ const (
|
||||
MySQL
|
||||
)
|
||||
|
||||
// LoadUsers returns all users(empty map) from the memory, for the shake of simplicty.
|
||||
// LoadUsers returns all users(empty map) from the memory, for the sake of simplicty.
|
||||
func LoadUsers(engine Engine) (map[int64]datamodels.User, error) {
|
||||
if engine != Memory {
|
||||
return nil, errors.New("for the shake of simplicity we're using a simple map as the data source")
|
||||
return nil, errors.New("for the sake of simplicity we're using a simple map as the data source")
|
||||
}
|
||||
|
||||
return make(map[int64]datamodels.User), nil
|
||||
|
||||
@@ -28,12 +28,15 @@ func main() {
|
||||
Reload(true)
|
||||
app.RegisterView(tmpl)
|
||||
|
||||
app.HandleDir("/public", "./web/public")
|
||||
app.HandleDir("/public", iris.Dir("./web/public"))
|
||||
|
||||
app.OnAnyErrorCode(func(ctx iris.Context) {
|
||||
ctx.ViewData("Message", ctx.Values().
|
||||
GetStringDefault("message", "The page you're looking for doesn't exist"))
|
||||
ctx.View("shared/error.html")
|
||||
if err := ctx.View("shared/error.html"); err != nil {
|
||||
ctx.HTML("<h3>%s</h3>", err.Error())
|
||||
return
|
||||
}
|
||||
})
|
||||
|
||||
// ---- Serve our controllers. ----
|
||||
@@ -76,12 +79,8 @@ func main() {
|
||||
// http://localhost:8080/user/me
|
||||
// http://localhost:8080/user/logout
|
||||
// basic auth: "admin", "password", see "./middleware/basicauth.go" source file.
|
||||
app.Run(
|
||||
// Starts the web server at localhost:8080
|
||||
iris.Addr("localhost:8080"),
|
||||
// Ignores err server closed log when CTRL/CMD+C pressed.
|
||||
iris.WithoutServerError(iris.ErrServerClosed),
|
||||
// Enables faster json serialization and more.
|
||||
iris.WithOptimizations,
|
||||
)
|
||||
|
||||
// Starts the web server at localhost:8080
|
||||
// Enables faster json serialization and more.
|
||||
app.Listen(":8080", iris.WithOptimizations)
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ func (s *userService) GetByID(id int64) (datamodels.User, bool) {
|
||||
})
|
||||
}
|
||||
|
||||
// GetByUsernameAndPassword returns a user based on its username and passowrd,
|
||||
// GetByUsernameAndPassword returns a user based on its username and password,
|
||||
// used for authentication.
|
||||
func (s *userService) GetByUsernameAndPassword(username, userPassword string) (datamodels.User, bool) {
|
||||
if username == "" || userPassword == "" {
|
||||
|
||||
@@ -9,9 +9,9 @@ import (
|
||||
|
||||
// UsersController is our /users API controller.
|
||||
// GET /users | get all
|
||||
// GET /users/{id:long} | get by id
|
||||
// PUT /users/{id:long} | update by id
|
||||
// DELETE /users/{id:long} | delete by id
|
||||
// GET /users/{id:int64} | get by id
|
||||
// PUT /users/{id:int64} | update by id
|
||||
// DELETE /users/{id:int64} | delete by id
|
||||
// Requires basic authentication.
|
||||
type UsersController struct {
|
||||
// Optionally: context is auto-binded by Iris on each request,
|
||||
@@ -30,14 +30,16 @@ type UsersController struct {
|
||||
// curl -i -u admin:password http://localhost:8080/users
|
||||
//
|
||||
// The correct way if you have sensitive data:
|
||||
// func (c *UsersController) Get() (results []viewmodels.User) {
|
||||
// data := c.Service.GetAll()
|
||||
//
|
||||
// for _, user := range data {
|
||||
// results = append(results, viewmodels.User{user})
|
||||
// }
|
||||
// return
|
||||
// }
|
||||
// func (c *UsersController) Get() (results []viewmodels.User) {
|
||||
// data := c.Service.GetAll()
|
||||
//
|
||||
// for _, user := range data {
|
||||
// results = append(results, viewmodels.User{user})
|
||||
// }
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// otherwise just return the datamodels.
|
||||
func (c *UsersController) Get() (results []datamodels.User) {
|
||||
return c.Service.GetAll()
|
||||
|
||||
@@ -5,8 +5,6 @@ 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",
|
||||
},
|
||||
var BasicAuth = basicauth.Default(map[string]string{
|
||||
"admin": "password",
|
||||
})
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
</head>
|
||||
|
||||
<body>
|
||||
{{ yield }}
|
||||
{{ yield . }}
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -14,20 +14,22 @@ var cacheHandler = cache.Handler(10 * time.Second)
|
||||
|
||||
func main() {
|
||||
app := iris.New()
|
||||
app.Logger().SetLevel("debug")
|
||||
|
||||
mvc.Configure(app, configure)
|
||||
|
||||
// http://localhost:8080
|
||||
// http://localhost:8080/other
|
||||
//
|
||||
// refresh every 10 seconds and you'll see different time output.
|
||||
app.Run(iris.Addr(":8080"))
|
||||
app.Listen(":8080")
|
||||
}
|
||||
|
||||
func configure(m *mvc.Application) {
|
||||
m.Router.Use(cacheHandler)
|
||||
m.Handle(&exampleController{
|
||||
timeFormat: "Mon, Jan 02 2006 15:04:05",
|
||||
})
|
||||
} /* ,mvc.IgnoreEmbedded --- Can be used to ignore any embedded struct method handlers */)
|
||||
}
|
||||
|
||||
type exampleController struct {
|
||||
|
||||
@@ -9,9 +9,11 @@ else as well, otherwise I am going with the first one:
|
||||
|
||||
```go
|
||||
// 1
|
||||
mvc.Configure(app.Party("/user"), func(m *mvc.Application) {
|
||||
m.Router.Use(cache.Handler(10*time.Second))
|
||||
})
|
||||
|
||||
mvc.Configure(app.Party("/user"), func(m *mvc.Application) {
|
||||
m.Router.Use(cache.Handler(10*time.Second))
|
||||
})
|
||||
|
||||
```
|
||||
|
||||
```go
|
||||
@@ -32,10 +34,12 @@ mvc.Configure(userRouter, ...)
|
||||
```go
|
||||
// 4
|
||||
// same:
|
||||
app.PartyFunc("/user", func(r iris.Party){
|
||||
r.Use(cache.Handler(10*time.Second))
|
||||
mvc.Configure(r, ...)
|
||||
})
|
||||
|
||||
app.PartyFunc("/user", func(r iris.Party){
|
||||
r.Use(cache.Handler(10*time.Second))
|
||||
mvc.Configure(r, ...)
|
||||
})
|
||||
|
||||
```
|
||||
|
||||
If you want to use a middleware for a single route,
|
||||
@@ -48,16 +52,18 @@ then you just call it on the method:
|
||||
var myMiddleware := myMiddleware.New(...) // this should return an iris/context.Handler
|
||||
|
||||
type UserController struct{}
|
||||
func (c *UserController) GetSomething(ctx iris.Context) {
|
||||
// ctx.Proceed checks if myMiddleware called `ctx.Next()`
|
||||
// inside it and returns true if so, otherwise false.
|
||||
nextCalled := ctx.Proceed(myMiddleware)
|
||||
if !nextCalled {
|
||||
return
|
||||
}
|
||||
|
||||
// else do the job here, it's allowed
|
||||
}
|
||||
func (c *UserController) GetSomething(ctx iris.Context) {
|
||||
// ctx.Proceed checks if myMiddleware called `ctx.Next()`
|
||||
// inside it and returns true if so, otherwise false.
|
||||
nextCalled := ctx.Proceed(myMiddleware)
|
||||
if !nextCalled {
|
||||
return
|
||||
}
|
||||
|
||||
// else do the job here, it's allowed
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
And last, if you want to add a middleware on a specific method
|
||||
@@ -84,7 +90,7 @@ func main() {
|
||||
m := mvc.New(app)
|
||||
m.Handle(&exampleController{})
|
||||
|
||||
app.Run(iris.Addr(":8080"))
|
||||
app.Listen(":8080")
|
||||
}
|
||||
|
||||
type exampleController struct{}
|
||||
|
||||
@@ -1,27 +1,10 @@
|
||||
/*Package main is a simple example of the behavior change of the execution flow of the handlers,
|
||||
normally we need the `ctx.Next()` to call the next handler in a route's handler chain,
|
||||
but with the new `ExecutionRules` we can change this default behavior.
|
||||
Please read below before continue.
|
||||
/*
|
||||
Package main shows how to add done handlers in an MVC application without
|
||||
the necessity of `ctx.Next()` inside the controller's methods.
|
||||
|
||||
The `Party#SetExecutionRules` alters the execution flow of the route handlers outside of the handlers themselves.
|
||||
|
||||
For example, if for some reason the desired result is the (done or all) handlers to be executed no matter what
|
||||
even if no `ctx.Next()` is called in the previous handlers, including the begin(`Use`),
|
||||
the main(`Handle`) and the done(`Done`) handlers themselves, then:
|
||||
Party#SetExecutionRules(iris.ExecutionRules {
|
||||
Begin: iris.ExecutionOptions{Force: true},
|
||||
Main: iris.ExecutionOptions{Force: true},
|
||||
Done: iris.ExecutionOptions{Force: true},
|
||||
})
|
||||
|
||||
Note that if `true` then the only remained way to "break" the handler chain is by `ctx.StopExecution()` now that `ctx.Next()` does not matter.
|
||||
|
||||
These rules are per-party, so if a `Party` creates a child one then the same rules will be applied to that as well.
|
||||
Reset of these rules (before `Party#Handle`) can be done with `Party#SetExecutionRules(iris.ExecutionRules{})`.
|
||||
|
||||
The most common scenario for its use can be found inside Iris MVC Applications;
|
||||
when we want the `Done` handlers of that specific mvc app's `Party`
|
||||
to be executed but we don't want to add `ctx.Next()` on the `exampleController#EndRequest`*/
|
||||
When we want the `Done` handlers of that specific mvc app's `Party`
|
||||
to be executed but we don't want to add `ctx.Next()` on the `exampleController#EndRequest`
|
||||
*/
|
||||
package main
|
||||
|
||||
import (
|
||||
@@ -33,25 +16,19 @@ func main() {
|
||||
app := iris.New()
|
||||
app.Get("/", func(ctx iris.Context) { ctx.Redirect("/example") })
|
||||
|
||||
// example := app.Party("/example")
|
||||
// example.SetExecutionRules && mvc.New(example) or...
|
||||
m := mvc.New(app.Party("/example"))
|
||||
exampleRouter := app.Party("/example")
|
||||
{
|
||||
exampleRouter.SetExecutionRules(iris.ExecutionRules{
|
||||
Done: iris.ExecutionOptions{Force: true},
|
||||
})
|
||||
|
||||
// IMPORTANT
|
||||
// All options can be filled with Force:true, they all play nice together.
|
||||
m.Router.SetExecutionRules(iris.ExecutionRules{
|
||||
// Begin: <- from `Use[all]` to `Handle[last]` future route handlers, execute all, execute all even if `ctx.Next()` is missing.
|
||||
// Main: <- all `Handle` future route handlers, execute all >> >>.
|
||||
Done: iris.ExecutionOptions{Force: true}, // <- from `Handle[last]` to `Done[all]` future route handlers, execute all >> >>.
|
||||
})
|
||||
m.Router.Done(doneHandler)
|
||||
// m.Router.Done(...)
|
||||
// ...
|
||||
//
|
||||
exampleRouter.Done(doneHandler)
|
||||
|
||||
m.Handle(&exampleController{})
|
||||
m := mvc.New(exampleRouter)
|
||||
m.Handle(&exampleController{})
|
||||
}
|
||||
|
||||
app.Run(iris.Addr(":8080"))
|
||||
app.Listen(":8080")
|
||||
}
|
||||
|
||||
func doneHandler(ctx iris.Context) {
|
||||
@@ -64,7 +41,8 @@ func (c *exampleController) Get() string {
|
||||
return "From Main Handler"
|
||||
// Note that here we don't binding the `Context`, and we don't call its `Next()`
|
||||
// function in order to call the `doneHandler`,
|
||||
// this is done automatically for us because we changed the execution rules with the `SetExecutionRules`.
|
||||
// this is done automatically for us because we changed the execution rules with the
|
||||
// `SetExecutionRules`.
|
||||
//
|
||||
// Therefore the final output is:
|
||||
// From Main Handler
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
# docker build -t app .
|
||||
# docker run --rm -it -p 8080:8080 app:latest
|
||||
FROM golang:latest AS builder
|
||||
RUN apt-get update
|
||||
ENV GO111MODULE=on \
|
||||
CGO_ENABLED=0 \
|
||||
GOOS=linux \
|
||||
GOARCH=amd64
|
||||
WORKDIR /go/src/app
|
||||
COPY go.mod .
|
||||
RUN go mod download
|
||||
COPY . .
|
||||
RUN go install
|
||||
|
||||
FROM scratch
|
||||
COPY --from=builder /go/bin/app .
|
||||
ENTRYPOINT ["./app"]
|
||||
@@ -0,0 +1,377 @@
|
||||
# Quick start
|
||||
|
||||
The following guide is just a simple example of usage of some of the **Iris MVC** features. You are not limited to that data structure or code flow.
|
||||
|
||||
Create a folder, let's assume its path is `app`. The structure should look like that:
|
||||
|
||||
```
|
||||
│ main.go
|
||||
│ go.mod
|
||||
│ go.sum
|
||||
└───environment
|
||||
│ environment.go
|
||||
└───model
|
||||
│ request.go
|
||||
│ response.go
|
||||
└───database
|
||||
│ database.go
|
||||
│ mysql.go
|
||||
│ sqlite.go
|
||||
└───service
|
||||
│ greet_service.go
|
||||
└───controller
|
||||
greet_controller.go
|
||||
```
|
||||
|
||||
Navigate to that `app` folder and execute the following command:
|
||||
|
||||
```sh
|
||||
$ go init app
|
||||
$ go get github.com/kataras/iris/v12@latest
|
||||
```
|
||||
|
||||
## Environment
|
||||
|
||||
Let's start by defining the available environments that our web-application can behave on.
|
||||
|
||||
We'll just work on two available environments, the "development" and the "production", as they define the two most common scenarios. The `ReadEnv` will read from the `Env` type of a system's environment variable (see `main.go` in the end of the page).
|
||||
|
||||
Create a `environment/environment.go` file and put the following contents:
|
||||
|
||||
```go
|
||||
package environment
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
PROD Env = "production"
|
||||
DEV Env = "development"
|
||||
)
|
||||
|
||||
type Env string
|
||||
|
||||
func (e Env) String() string {
|
||||
return string(e)
|
||||
}
|
||||
|
||||
func ReadEnv(key string, def Env) Env {
|
||||
v := Getenv(key, def.String())
|
||||
if v == "" {
|
||||
return def
|
||||
}
|
||||
|
||||
env := Env(strings.ToLower(v))
|
||||
switch env {
|
||||
case PROD, DEV: // allowed.
|
||||
default:
|
||||
panic("unexpected environment " + v)
|
||||
}
|
||||
|
||||
return env
|
||||
}
|
||||
|
||||
func Getenv(key string, def string) string {
|
||||
if v := os.Getenv(key); v != "" {
|
||||
return v
|
||||
}
|
||||
|
||||
return def
|
||||
}
|
||||
```
|
||||
|
||||
## Database
|
||||
|
||||
We will use two database management systems, the `MySQL` and the `SQLite`. The first one for "production" use and the other for "development".
|
||||
|
||||
Create a `database/database.go` file and copy-paste the following:
|
||||
|
||||
```go
|
||||
package database
|
||||
|
||||
import "app/environment"
|
||||
|
||||
type DB interface {
|
||||
Exec(q string) error
|
||||
}
|
||||
|
||||
func NewDB(env environment.Env) DB {
|
||||
switch env {
|
||||
case environment.PROD:
|
||||
return &mysql{}
|
||||
case environment.DEV:
|
||||
return &sqlite{}
|
||||
default:
|
||||
panic("unknown environment")
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Let's simulate our MySQL and SQLite `DB` instances. Create a `database/mysql.go` file which looks like the following one:
|
||||
|
||||
```go
|
||||
package database
|
||||
|
||||
import "fmt"
|
||||
|
||||
type mysql struct{}
|
||||
|
||||
func (db *mysql) Exec(q string) error {
|
||||
return fmt.Errorf("mysql: not implemented <%s>", q)
|
||||
}
|
||||
```
|
||||
|
||||
And a `database/sqlite.go` file.
|
||||
|
||||
```go
|
||||
package database
|
||||
|
||||
type sqlite struct{}
|
||||
|
||||
func (db *sqlite) Exec(q string) error { return nil }
|
||||
```
|
||||
|
||||
The `DB` depends on the `Environment.
|
||||
|
||||
> A practical and operational database example, including Docker images, can be found at the following guide: https://github.com/kataras/iris/tree/main/_examples/database/mysql
|
||||
|
||||
## Service
|
||||
|
||||
We'll need a service that will communicate with a database instance in behalf of our Controller(s).
|
||||
|
||||
In our case we will only need a single service, the Greet Service.
|
||||
|
||||
For the sake of the example, let's use two implementations of a greet service based on the `Environment`. The `GreetService` interface contains a single method of `Say(input string) (output string, err error)`. Create a `./service/greet_service.go` file and write the following code:
|
||||
|
||||
```go
|
||||
package service
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"app/database"
|
||||
"app/environment"
|
||||
)
|
||||
|
||||
// GreetService example service.
|
||||
type GreetService interface {
|
||||
Say(input string) (string, error)
|
||||
}
|
||||
|
||||
// NewGreetService returns a service backed with a "db" based on "env".
|
||||
func NewGreetService(env environment.Env, db database.DB) GreetService {
|
||||
service := &greeter{db: db, prefix: "Hello"}
|
||||
|
||||
switch env {
|
||||
case environment.PROD:
|
||||
return service
|
||||
case environment.DEV:
|
||||
return &greeterWithLogging{service}
|
||||
default:
|
||||
panic("unknown environment")
|
||||
}
|
||||
}
|
||||
|
||||
type greeter struct {
|
||||
prefix string
|
||||
db database.DB
|
||||
}
|
||||
|
||||
func (s *greeter) Say(input string) (string, error) {
|
||||
if err := s.db.Exec("simulate a query..."); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
result := s.prefix + " " + input
|
||||
return result, nil
|
||||
}
|
||||
|
||||
type greeterWithLogging struct {
|
||||
*greeter
|
||||
}
|
||||
|
||||
func (s *greeterWithLogging) Say(input string) (string, error) {
|
||||
result, err := s.greeter.Say(input)
|
||||
fmt.Printf("result: %s\nerror: %v\n", result, err)
|
||||
return result, err
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
The `greeter` will be used on "production" and the `greeterWithLogging` on "development". The `GreetService` depends on the `Environment` and the `DB`.
|
||||
|
||||
## Models
|
||||
|
||||
Continue by creating our HTTP request and response models.
|
||||
|
||||
Create a `model/request.go` file and copy-paste the following code:
|
||||
|
||||
```go
|
||||
package model
|
||||
|
||||
type Request struct {
|
||||
Name string `url:"name"`
|
||||
}
|
||||
```
|
||||
|
||||
Same for the `model/response.go` file.
|
||||
|
||||
```go
|
||||
package model
|
||||
|
||||
type Response struct {
|
||||
Message string `json:"msg"`
|
||||
}
|
||||
```
|
||||
|
||||
The server will accept a URL Query Parameter of `name` (e.g. `/greet?name=kataras`) and will reply back with a JSON message.
|
||||
|
||||
## Controller
|
||||
|
||||
MVC Controllers are responsible for controlling the flow of the application execution. When you make a request (means request a page) to MVC Application, a controller is responsible for returning the response to that request.
|
||||
|
||||
We will only need the `GreetController` for our mini web-application. Create a file at `controller/greet_controller.go` which looks like that:
|
||||
|
||||
```go
|
||||
package controller
|
||||
|
||||
import (
|
||||
"app/model"
|
||||
"app/service"
|
||||
)
|
||||
|
||||
type GreetController struct {
|
||||
Service service.GreetService
|
||||
// Ctx iris.Context
|
||||
}
|
||||
|
||||
func (c *GreetController) Get(req model.Request) (model.Response, error) {
|
||||
message, err := c.Service.Say(req.Name)
|
||||
if err != nil {
|
||||
return model.Response{}, err
|
||||
}
|
||||
|
||||
return model.Response{Message: message}, nil
|
||||
}
|
||||
```
|
||||
|
||||
The `GreetController` depends on the `GreetService`. It serves the `GET: /greet` index path through its `Get` method. The `Get` method accepts a `model.Request` which contains a single field name of `Name` which will be extracted from the `URL Query Parameter 'name'` (because it's a `GET` requst and its `url:"name"` struct field).
|
||||
|
||||
## Wrap up
|
||||
|
||||
```sh
|
||||
+-------------------+
|
||||
| Env (DEV, PROD) |
|
||||
+---------+---------+
|
||||
| | |
|
||||
| | |
|
||||
| | |
|
||||
DEV | | | PROD
|
||||
-------------------+---------------------+ | +----------------------+-------------------
|
||||
| | |
|
||||
| | |
|
||||
+---+-----+ +----------------v------------------+ +----+----+
|
||||
| sqlite | | NewDB(Env) DB | | mysql |
|
||||
+---+-----+ +----------------+---+--------------+ +----+----+
|
||||
| | | |
|
||||
| | | |
|
||||
| | | |
|
||||
+--------------+-----+ +-------------------v---v-----------------+ +----+------+
|
||||
| greeterWithLogging | | NewGreetService(Env, DB) GreetService | | greeter |
|
||||
+--------------+-----+ +---------------------------+-------------+ +----+------+
|
||||
| | |
|
||||
| | |
|
||||
| +-----------------------------------------+ |
|
||||
| | GreetController | | |
|
||||
| | | | |
|
||||
| | - Service GreetService <-- | |
|
||||
| | | |
|
||||
| +-------------------+---------------------+ |
|
||||
| | |
|
||||
| | |
|
||||
| | |
|
||||
| +-----------+-----------+ |
|
||||
| | HTTP Request | |
|
||||
| +-----------------------+ |
|
||||
| | /greet?name=kataras | |
|
||||
| +-----------+-----------+ |
|
||||
| | |
|
||||
+------------------+--------+ +------------+------------+ +-------+------------------+
|
||||
| model.Response (JSON) | | Response (JSON, error) | | Bad Request |
|
||||
+---------------------------+ +-------------------------+ +--------------------------+
|
||||
| { | | mysql: not implemented |
|
||||
| "msg": "Hello kataras" | +--------------------------+
|
||||
| } |
|
||||
+---------------------------+
|
||||
```
|
||||
|
||||
Now it's the time to wrap all the above into our `main.go` file. Copy-paste the following code:
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"app/controller"
|
||||
"app/database"
|
||||
"app/environment"
|
||||
"app/service"
|
||||
|
||||
"github.com/kataras/iris/v12"
|
||||
"github.com/kataras/iris/v12/mvc"
|
||||
)
|
||||
|
||||
func main() {
|
||||
app := iris.New()
|
||||
app.Get("/ping", pong).Describe("healthcheck")
|
||||
|
||||
mvc.Configure(app.Party("/greet"), setup)
|
||||
|
||||
// http://localhost:8080/greet?name=kataras
|
||||
app.Listen(":8080", iris.WithLogLevel("debug"))
|
||||
}
|
||||
|
||||
func pong(ctx iris.Context) {
|
||||
ctx.WriteString("pong")
|
||||
}
|
||||
|
||||
func setup(app *mvc.Application) {
|
||||
// Register Dependencies.
|
||||
app.Register(
|
||||
environment.DEV, // DEV, PROD
|
||||
database.NewDB, // sqlite, mysql
|
||||
service.NewGreetService, // greeterWithLogging, greeter
|
||||
)
|
||||
|
||||
// Register Controllers.
|
||||
app.Handle(new(controller.GreetController))
|
||||
}
|
||||
```
|
||||
|
||||
The `mvc.Application.Register` method registers one more dependencies, dependencies can depend on previously registered dependencies too. Thats the reason we pass, first, the `Environment(DEV)`, then the `NewDB` which depends on that `Environment`, following by the `NewGreetService` function which depends on both the `Environment(DEV)` and the `DB`.
|
||||
|
||||
The `mvc.Application.Handle` registers a new controller, which depends on the `GreetService`, for the targeted sub-router of `Party("/greet")`.
|
||||
|
||||
## Run
|
||||
|
||||
Install [Go](https://golang.org/dl) and run the application with:
|
||||
|
||||
```sh
|
||||
go run main.go
|
||||
```
|
||||
|
||||
<details><summary>Docker</summary>
|
||||
|
||||
Download the [Dockerfile](https://raw.githubusercontent.com/kataras/iris/9b93c0dbb491dcedf49c91e89ca13bab884d116f/_examples/mvc/overview/Dockerfile) and [docker-compose.yml](https://raw.githubusercontent.com/kataras/iris/9b93c0dbb491dcedf49c91e89ca13bab884d116f/_examples/mvc/overview/docker-compose.yml) files to the `app` folder.
|
||||
|
||||
Install [Docker](https://www.docker.com/) and execute the following command:
|
||||
|
||||
```sh
|
||||
$ docker-compose up
|
||||
```
|
||||
</details>
|
||||
|
||||
Visit http://localhost:8080?name=kataras.
|
||||
|
||||
Optionally, replace the `main.go`'s `app.Register(environment.DEV` with `environment.PROD`, restart the application and refresh. You will see that a new database (`sqlite`) and another service of (`greeterWithLogging`) will be binded to the `GreetController`. With **a single change** you achieve to completety change the result.
|
||||
@@ -0,0 +1,23 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"app/model"
|
||||
"app/service"
|
||||
)
|
||||
|
||||
// GreetController handles the index.
|
||||
type GreetController struct {
|
||||
Service service.GreetService
|
||||
// Ctx iris.Context
|
||||
}
|
||||
|
||||
// Get serves [GET] /.
|
||||
// Query: name
|
||||
func (c *GreetController) Get(req model.Request) (model.Response, error) {
|
||||
message, err := c.Service.Say(req.Name)
|
||||
if err != nil {
|
||||
return model.Response{}, err
|
||||
}
|
||||
|
||||
return model.Response{Message: message}, nil
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package database
|
||||
|
||||
import "app/environment"
|
||||
|
||||
// DB example database interface.
|
||||
type DB interface {
|
||||
Exec(q string) error
|
||||
}
|
||||
|
||||
// NewDB returns a database based on "env".
|
||||
func NewDB(env environment.Env) DB {
|
||||
switch env {
|
||||
case environment.PROD:
|
||||
return &mysql{}
|
||||
case environment.DEV:
|
||||
return &sqlite{}
|
||||
default:
|
||||
panic("unknown environment")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package database
|
||||
|
||||
import "fmt"
|
||||
|
||||
type mysql struct{}
|
||||
|
||||
func (db *mysql) Exec(q string) error {
|
||||
// simulate an error response.
|
||||
return fmt.Errorf("mysql: not implemented <%s>", q)
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package database
|
||||
|
||||
type sqlite struct{}
|
||||
|
||||
func (db *sqlite) Exec(q string) error { return nil }
|
||||
@@ -0,0 +1,16 @@
|
||||
version: '3.1'
|
||||
|
||||
services:
|
||||
app:
|
||||
build: .
|
||||
ports:
|
||||
- 8080:8080
|
||||
environment:
|
||||
PORT: 8080
|
||||
ENVIRONMENT: development
|
||||
restart: on-failure
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:8080/ping"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 5
|
||||
@@ -0,0 +1,50 @@
|
||||
package environment
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Available environments example.
|
||||
const (
|
||||
PROD Env = "production"
|
||||
DEV Env = "development"
|
||||
)
|
||||
|
||||
// Env is the environment type.
|
||||
type Env string
|
||||
|
||||
// String just returns the string representation of the Env.
|
||||
func (e Env) String() string {
|
||||
return string(e)
|
||||
}
|
||||
|
||||
// ReadEnv returns the environment of the system environment variable of "key".
|
||||
// Returns the "def" if not found.
|
||||
// Reports a panic message if the environment variable found
|
||||
// but the Env is unknown.
|
||||
func ReadEnv(key string, def Env) Env {
|
||||
v := Getenv(key, def.String())
|
||||
if v == "" {
|
||||
return def
|
||||
}
|
||||
|
||||
env := Env(strings.ToLower(v))
|
||||
switch env {
|
||||
case PROD, DEV: // allowed.
|
||||
default:
|
||||
panic("unexpected environment " + v)
|
||||
}
|
||||
|
||||
return env
|
||||
}
|
||||
|
||||
// Getenv returns the value of a system environment variable "key".
|
||||
// Defaults to "def" if not found.
|
||||
func Getenv(key string, def string) string {
|
||||
if v := os.Getenv(key); v != "" {
|
||||
return v
|
||||
}
|
||||
|
||||
return def
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
module app
|
||||
|
||||
go 1.22
|
||||
|
||||
require github.com/kataras/iris/v12 v12.2.11-0.20240424221303-df7737ed577a
|
||||
|
||||
require (
|
||||
github.com/BurntSushi/toml v1.3.2 // indirect
|
||||
github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 // indirect
|
||||
github.com/CloudyKit/jet/v6 v6.2.0 // indirect
|
||||
github.com/Joker/jade v1.1.3 // indirect
|
||||
github.com/Shopify/goreferrer v0.0.0-20220729165902-8cddb4f5de06 // indirect
|
||||
github.com/andybalholm/brotli v1.1.0 // indirect
|
||||
github.com/aymerick/douceur v0.2.0 // indirect
|
||||
github.com/blang/semver/v4 v4.0.0 // indirect
|
||||
github.com/fatih/structs v1.1.0 // indirect
|
||||
github.com/flosch/pongo2/v4 v4.0.2 // indirect
|
||||
github.com/gobwas/httphead v0.1.0 // indirect
|
||||
github.com/gobwas/pool v0.2.1 // indirect
|
||||
github.com/gobwas/ws v1.3.2 // indirect
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
github.com/gomarkdown/markdown v0.0.0-20240328165702-4d01890c35c0 // indirect
|
||||
github.com/google/go-cmp v0.5.9 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/gorilla/css v1.0.0 // indirect
|
||||
github.com/gorilla/websocket v1.5.1 // indirect
|
||||
github.com/iris-contrib/schema v0.0.6 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/kataras/blocks v0.0.8 // indirect
|
||||
github.com/kataras/golog v0.1.11 // indirect
|
||||
github.com/kataras/neffos v0.0.24-0.20240408172741-99c879ba0ede // indirect
|
||||
github.com/kataras/pio v0.0.13 // indirect
|
||||
github.com/kataras/sitemap v0.0.6 // indirect
|
||||
github.com/kataras/tunnel v0.0.4 // indirect
|
||||
github.com/klauspost/compress v1.17.7 // indirect
|
||||
github.com/mailgun/raymond/v2 v2.0.48 // indirect
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
github.com/mediocregopher/radix/v3 v3.8.1 // indirect
|
||||
github.com/microcosm-cc/bluemonday v1.0.26 // indirect
|
||||
github.com/nats-io/nats.go v1.34.1 // indirect
|
||||
github.com/nats-io/nkeys v0.4.7 // indirect
|
||||
github.com/nats-io/nuid v1.0.1 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/schollz/closestmatch v2.1.0+incompatible // indirect
|
||||
github.com/sirupsen/logrus v1.8.1 // indirect
|
||||
github.com/tdewolff/minify/v2 v2.20.19 // indirect
|
||||
github.com/tdewolff/parse/v2 v2.7.12 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect
|
||||
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
|
||||
github.com/yosssi/ace v0.0.5 // indirect
|
||||
golang.org/x/crypto v0.22.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20240404231335-c0f41cb1a7a0 // indirect
|
||||
golang.org/x/net v0.24.0 // indirect
|
||||
golang.org/x/sys v0.19.0 // indirect
|
||||
golang.org/x/text v0.14.0 // indirect
|
||||
golang.org/x/time v0.5.0 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
|
||||
google.golang.org/protobuf v1.33.0 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
Generated
+200
@@ -0,0 +1,200 @@
|
||||
github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8=
|
||||
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
||||
github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 h1:sR+/8Yb4slttB4vD+b9btVEnWgL3Q00OBTzVT8B9C0c=
|
||||
github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno=
|
||||
github.com/CloudyKit/jet/v6 v6.2.0 h1:EpcZ6SR9n28BUGtNJSvlBqf90IpjeFr36Tizxhn/oME=
|
||||
github.com/CloudyKit/jet/v6 v6.2.0/go.mod h1:d3ypHeIRNo2+XyqnGA8s+aphtcVpjP5hPwP/Lzo7Ro4=
|
||||
github.com/Joker/hpp v1.0.0 h1:65+iuJYdRXv/XyN62C1uEmmOx3432rNG/rKlX6V7Kkc=
|
||||
github.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY=
|
||||
github.com/Joker/jade v1.1.3 h1:Qbeh12Vq6BxURXT1qZBRHsDxeURB8ztcL6f3EXSGeHk=
|
||||
github.com/Joker/jade v1.1.3/go.mod h1:T+2WLyt7VH6Lp0TRxQrUYEs64nRc83wkMQrfeIQKduM=
|
||||
github.com/Shopify/goreferrer v0.0.0-20220729165902-8cddb4f5de06 h1:KkH3I3sJuOLP3TjA/dfr4NAY8bghDwnXiU7cTKxQqo0=
|
||||
github.com/Shopify/goreferrer v0.0.0-20220729165902-8cddb4f5de06/go.mod h1:7erjKLwalezA0k99cWs5L11HWOAPNjdUZ6RxH1BXbbM=
|
||||
github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=
|
||||
github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
|
||||
github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M=
|
||||
github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY=
|
||||
github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
|
||||
github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
|
||||
github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM=
|
||||
github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=
|
||||
github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
|
||||
github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=
|
||||
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
|
||||
github.com/flosch/pongo2/v4 v4.0.2 h1:gv+5Pe3vaSVmiJvh/BZa82b7/00YUGm0PIyVVLop0Hw=
|
||||
github.com/flosch/pongo2/v4 v4.0.2/go.mod h1:B5ObFANs/36VwxxlgKpdchIJHMvHB562PW+BWPhwZD8=
|
||||
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
|
||||
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
|
||||
github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU=
|
||||
github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM=
|
||||
github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og=
|
||||
github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
|
||||
github.com/gobwas/ws v1.3.2 h1:zlnbNHxumkRvfPWgfXu8RBwyNR1x8wh9cf5PTOCqs9Q=
|
||||
github.com/gobwas/ws v1.3.2/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY=
|
||||
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
|
||||
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/gomarkdown/markdown v0.0.0-20240328165702-4d01890c35c0 h1:4gjrh/PN2MuWCCElk8/I4OCKRKWCCo2zEct3VKCbibU=
|
||||
github.com/gomarkdown/markdown v0.0.0-20240328165702-4d01890c35c0/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA=
|
||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
|
||||
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY=
|
||||
github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c=
|
||||
github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY=
|
||||
github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=
|
||||
github.com/imkira/go-interpol v1.1.0 h1:KIiKr0VSG2CUW1hl1jpiyuzuJeKUUpC8iM1AIE7N1Vk=
|
||||
github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA=
|
||||
github.com/iris-contrib/httpexpect/v2 v2.15.2 h1:T9THsdP1woyAqKHwjkEsbCnMefsAFvk8iJJKokcJ3Go=
|
||||
github.com/iris-contrib/httpexpect/v2 v2.15.2/go.mod h1:JLDgIqnFy5loDSUv1OA2j0mb6p/rDhiCqigP22Uq9xE=
|
||||
github.com/iris-contrib/schema v0.0.6 h1:CPSBLyx2e91H2yJzPuhGuifVRnZBBJ3pCOMbOvPZaTw=
|
||||
github.com/iris-contrib/schema v0.0.6/go.mod h1:iYszG0IOsuIsfzjymw1kMzTL8YQcCWlm65f3wX8J5iA=
|
||||
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||
github.com/kataras/blocks v0.0.8 h1:MrpVhoFTCR2v1iOOfGng5VJSILKeZZI+7NGfxEh3SUM=
|
||||
github.com/kataras/blocks v0.0.8/go.mod h1:9Jm5zx6BB+06NwA+OhTbHW1xkMOYxahnqTN5DveZ2Yg=
|
||||
github.com/kataras/golog v0.1.11 h1:dGkcCVsIpqiAMWTlebn/ZULHxFvfG4K43LF1cNWSh20=
|
||||
github.com/kataras/golog v0.1.11/go.mod h1:mAkt1vbPowFUuUGvexyQ5NFW6djEgGyxQBIARJ0AH4A=
|
||||
github.com/kataras/iris/v12 v12.2.11-0.20240424221303-df7737ed577a h1:uaDFlkh2LzTPtoOk/1e6jy/ChoM5/FxTgTiEehlJuD0=
|
||||
github.com/kataras/iris/v12 v12.2.11-0.20240424221303-df7737ed577a/go.mod h1:uMAeX8OqG9vqdhyrIPv8Lajo/wXTtAF43wchP9WHt2w=
|
||||
github.com/kataras/neffos v0.0.24-0.20240408172741-99c879ba0ede h1:ZnSJQ+ri9x46Yz15wHqSb93Q03yY12XMVLUFDJJ0+/g=
|
||||
github.com/kataras/neffos v0.0.24-0.20240408172741-99c879ba0ede/go.mod h1:i0dtcTbpnw1lqIbojYtGtZlu6gDWPxJ4Xl2eJ6oQ1bE=
|
||||
github.com/kataras/pio v0.0.13 h1:x0rXVX0fviDTXOOLOmr4MUxOabu1InVSTu5itF8CXCM=
|
||||
github.com/kataras/pio v0.0.13/go.mod h1:k3HNuSw+eJ8Pm2lA4lRhg3DiCjVgHlP8hmXApSej3oM=
|
||||
github.com/kataras/sitemap v0.0.6 h1:w71CRMMKYMJh6LR2wTgnk5hSgjVNB9KL60n5e2KHvLY=
|
||||
github.com/kataras/sitemap v0.0.6/go.mod h1:dW4dOCNs896OR1HmG+dMLdT7JjDk7mYBzoIRwuj5jA4=
|
||||
github.com/kataras/tunnel v0.0.4 h1:sCAqWuJV7nPzGrlb0os3j49lk2JhILT0rID38NHNLpA=
|
||||
github.com/kataras/tunnel v0.0.4/go.mod h1:9FkU4LaeifdMWqZu7o20ojmW4B7hdhv2CMLwfnHGpYw=
|
||||
github.com/klauspost/compress v1.17.7 h1:ehO88t2UGzQK66LMdE8tibEd1ErmzZjNEqWkjLAKQQg=
|
||||
github.com/klauspost/compress v1.17.7/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/mailgun/raymond/v2 v2.0.48 h1:5dmlB680ZkFG2RN/0lvTAghrSxIESeu9/2aeDqACtjw=
|
||||
github.com/mailgun/raymond/v2 v2.0.48/go.mod h1:lsgvL50kgt1ylcFJYZiULi5fjPBkkhNfj4KA0W54Z18=
|
||||
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
|
||||
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
|
||||
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mediocregopher/radix/v3 v3.8.1 h1:rOkHflVuulFKlwsLY01/M2cM2tWCjDoETcMqKbAWu1M=
|
||||
github.com/mediocregopher/radix/v3 v3.8.1/go.mod h1:8FL3F6UQRXHXIBSPUs5h0RybMF8i4n7wVopoX3x7Bv8=
|
||||
github.com/microcosm-cc/bluemonday v1.0.26 h1:xbqSvqzQMeEHCqMi64VAs4d8uy6Mequs3rQ0k/Khz58=
|
||||
github.com/microcosm-cc/bluemonday v1.0.26/go.mod h1:JyzOCs9gkyQyjs+6h10UEVSe02CGwkhd72Xdqh78TWs=
|
||||
github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=
|
||||
github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=
|
||||
github.com/nats-io/nats.go v1.34.1 h1:syWey5xaNHZgicYBemv0nohUPPmaLteiBEUT6Q5+F/4=
|
||||
github.com/nats-io/nats.go v1.34.1/go.mod h1:Ubdu4Nh9exXdSz0RVWRFBbRfrbSxOYd26oF0wkWclB8=
|
||||
github.com/nats-io/nkeys v0.4.7 h1:RwNJbbIdYCoClSDNY7QVKZlyb/wfT6ugvFCiKy6vDvI=
|
||||
github.com/nats-io/nkeys v0.4.7/go.mod h1:kqXRgRDPlGy7nGaEDMuYzmiJCIAAWDK0IMBtDmGD0nc=
|
||||
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
|
||||
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/sanity-io/litter v1.5.5 h1:iE+sBxPBzoK6uaEP5Lt3fHNgpKcHXc/A2HGETy0uJQo=
|
||||
github.com/sanity-io/litter v1.5.5/go.mod h1:9gzJgR2i4ZpjZHsKvUXIRQVk7P+yM3e+jAF7bU2UI5U=
|
||||
github.com/schollz/closestmatch v2.1.0+incompatible h1:Uel2GXEpJqOWBrlyI+oY9LTiyyjYS17cCYRqP13/SHk=
|
||||
github.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g=
|
||||
github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=
|
||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
||||
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
|
||||
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/tdewolff/minify/v2 v2.20.19 h1:tX0SR0LUrIqGoLjXnkIzRSIbKJ7PaNnSENLD4CyH6Xo=
|
||||
github.com/tdewolff/minify/v2 v2.20.19/go.mod h1:ulkFoeAVWMLEyjuDz1ZIWOA31g5aWOawCFRp9R/MudM=
|
||||
github.com/tdewolff/parse/v2 v2.7.12 h1:tgavkHc2ZDEQVKy1oWxwIyh5bP4F5fEh/JmBwPP/3LQ=
|
||||
github.com/tdewolff/parse/v2 v2.7.12/go.mod h1:3FbJWZp3XT9OWVN3Hmfp0p/a08v4h8J9W1aghka0soA=
|
||||
github.com/tdewolff/test v1.0.11-0.20231101010635-f1265d231d52/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE=
|
||||
github.com/tdewolff/test v1.0.11-0.20240106005702-7de5f7df4739 h1:IkjBCtQOOjIn03u/dMQK9g+Iw9ewps4mCl1nB8Sscbo=
|
||||
github.com/tdewolff/test v1.0.11-0.20240106005702-7de5f7df4739/go.mod h1:XPuWBzvdUzhCuxWO1ojpXsyzsA5bFoS3tO/Q3kFuTG8=
|
||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||
github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8=
|
||||
github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok=
|
||||
github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=
|
||||
github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
|
||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
|
||||
github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
|
||||
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
|
||||
github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0 h1:6fRhSjgLCkTD3JnJxvaJ4Sj+TYblw757bqYgZaOq5ZY=
|
||||
github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI=
|
||||
github.com/yosssi/ace v0.0.5 h1:tUkIP/BLdKqrlrPwcmH0shwEEhTRHoGnc1wFIWmaBUA=
|
||||
github.com/yosssi/ace v0.0.5/go.mod h1:ALfIzm2vT7t5ZE7uoIZqF3TQ7SAOyupFZnkrF5id+K0=
|
||||
github.com/yudai/gojsondiff v1.0.0 h1:27cbfqXLVEJ1o8I6v3y9lg8Ydm53EKqHXAOMxEGlCOA=
|
||||
github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg=
|
||||
github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 h1:BHyfKlQyqbsFN5p3IfnEUduWvb9is428/nNb5L3U01M=
|
||||
github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM=
|
||||
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
|
||||
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
|
||||
golang.org/x/exp v0.0.0-20240404231335-c0f41cb1a7a0 h1:985EYyeCOxTpcgOTJpflJUwOeEz0CQOdPt73OzpE9F8=
|
||||
golang.org/x/exp v0.0.0-20240404231335-c0f41cb1a7a0/go.mod h1:/lliqkxwWAhPjf5oSOIJup2XcqJaw8RGS6k3TGEc7GI=
|
||||
golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
|
||||
golang.org/x/net v0.0.0-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
|
||||
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
|
||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
|
||||
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
|
||||
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
|
||||
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b h1:QRR6H1YWRnHb4Y/HeNFCTJLFVxaq6wH4YuVdsUOr75U=
|
||||
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
|
||||
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
moul.io/http2curl/v2 v2.3.0 h1:9r3JfDzWPcbIklMOs2TnIFzDYvfAZvjeavG6EzP7jYs=
|
||||
moul.io/http2curl/v2 v2.3.0/go.mod h1:RW4hyBjTWSYDOxapodpNEtX0g5Eb16sxklBqmd2RHcE=
|
||||
@@ -1,13 +1,10 @@
|
||||
// file: main.go
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/kataras/iris/v12/_examples/mvc/overview/datasource"
|
||||
"github.com/kataras/iris/v12/_examples/mvc/overview/repositories"
|
||||
"github.com/kataras/iris/v12/_examples/mvc/overview/services"
|
||||
"github.com/kataras/iris/v12/_examples/mvc/overview/web/controllers"
|
||||
"github.com/kataras/iris/v12/_examples/mvc/overview/web/middleware"
|
||||
"app/controller"
|
||||
"app/database"
|
||||
"app/environment"
|
||||
"app/service"
|
||||
|
||||
"github.com/kataras/iris/v12"
|
||||
"github.com/kataras/iris/v12/mvc"
|
||||
@@ -17,44 +14,77 @@ func main() {
|
||||
app := iris.New()
|
||||
app.Logger().SetLevel("debug")
|
||||
|
||||
// Load the template files.
|
||||
app.RegisterView(iris.HTML("./web/views", ".html"))
|
||||
app.Get("/ping", pong).Describe("healthcheck")
|
||||
|
||||
// Serve our controllers.
|
||||
mvc.New(app.Party("/hello")).Handle(new(controllers.HelloController))
|
||||
// You can also split the code you write to configure an mvc.Application
|
||||
// using the `mvc.Configure` method, as shown below.
|
||||
mvc.Configure(app.Party("/movies"), movies)
|
||||
mvc.Configure(app.Party("/greet"), setup)
|
||||
|
||||
// http://localhost:8080/hello
|
||||
// http://localhost:8080/hello/iris
|
||||
// http://localhost:8080/movies
|
||||
// http://localhost:8080/movies/1
|
||||
app.Run(
|
||||
// Start the web server at localhost:8080
|
||||
iris.Addr("localhost:8080"),
|
||||
// skip err server closed when CTRL/CMD+C pressed:
|
||||
iris.WithoutServerError(iris.ErrServerClosed),
|
||||
// enables faster json serialization and more:
|
||||
iris.WithOptimizations,
|
||||
// http://localhost:8080/greet?name=kataras
|
||||
addr := ":" + environment.Getenv("PORT", "8080")
|
||||
app.Listen(addr)
|
||||
}
|
||||
|
||||
func pong(ctx iris.Context) {
|
||||
ctx.WriteString("pong")
|
||||
}
|
||||
|
||||
/*
|
||||
+-------------------+
|
||||
| Env (DEV, PROD) |
|
||||
+---------+---------+
|
||||
| | |
|
||||
| | |
|
||||
| | |
|
||||
DEV | | | PROD
|
||||
|
||||
-------------------+---------------------+ | +----------------------+-------------------
|
||||
|
||||
| | |
|
||||
| | |
|
||||
+---+-----+ +----------------v------------------+ +----+----+
|
||||
| sqlite | | NewDB(Env) DB | | mysql |
|
||||
+---+-----+ +----------------+---+--------------+ +----+----+
|
||||
| | | |
|
||||
| | | |
|
||||
| | | |
|
||||
+--------------+-----+ +-------------------v---v-----------------+ +----+------+
|
||||
| greeterWithLogging | | NewGreetService(Env, DB) GreetService | | greeter |
|
||||
+--------------+-----+ +---------------------------+-------------+ +----+------+
|
||||
| | |
|
||||
| | |
|
||||
| +-----------------------------------------+ |
|
||||
| | GreetController | | |
|
||||
| | | | |
|
||||
| | - Service GreetService <-- | |
|
||||
| | | |
|
||||
| +-------------------+---------------------+ |
|
||||
| | |
|
||||
| | |
|
||||
| | |
|
||||
| +-----------+-----------+ |
|
||||
| | HTTP Request | |
|
||||
| +-----------------------+ |
|
||||
| | /greet?name=kataras | |
|
||||
| +-----------+-----------+ |
|
||||
| | |
|
||||
|
||||
+------------------+--------+ +------------+------------+ +-------+------------------+
|
||||
| model.Response (JSON) | | Response (JSON, error) | | Bad Request |
|
||||
+---------------------------+ +-------------------------+ +--------------------------+
|
||||
| { | | mysql: not implemented |
|
||||
| "msg": "Hello kataras" | +--------------------------+
|
||||
| } |
|
||||
+---------------------------+
|
||||
*/
|
||||
func setup(app *mvc.Application) {
|
||||
// Register Dependencies.
|
||||
// Tip: A dependency can depend on other dependencies too.
|
||||
env := environment.ReadEnv("ENVIRONMENT", environment.DEV)
|
||||
app.Register(
|
||||
env, // DEV, PROD
|
||||
database.NewDB, // sqlite, mysql
|
||||
service.NewGreetService, // greeterWithLogging, greeter
|
||||
)
|
||||
}
|
||||
|
||||
// note the mvc.Application, it's not iris.Application.
|
||||
func movies(app *mvc.Application) {
|
||||
// Add the basic authentication(admin:password) middleware
|
||||
// for the /movies based requests.
|
||||
app.Router.Use(middleware.BasicAuth)
|
||||
|
||||
// Create our movie repository with some (memory) data from the datasource.
|
||||
repo := repositories.NewMovieRepository(datasource.Movies)
|
||||
// Create our movie service, we will bind it to the movie app's dependencies.
|
||||
movieService := services.NewMovieService(repo)
|
||||
app.Register(movieService)
|
||||
|
||||
// serve our movies controller.
|
||||
// Note that you can serve more than one controller
|
||||
// you can also create child mvc apps using the `movies.Party(relativePath)` or `movies.Clone(app.Party(...))`
|
||||
// if you want.
|
||||
app.Handle(new(controllers.MovieController))
|
||||
|
||||
// Register Controllers.
|
||||
app.Handle(new(controller.GreetController))
|
||||
}
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
package model
|
||||
|
||||
// Request example incoming request.
|
||||
type Request struct {
|
||||
Name string `json:"name" url:"name"`
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package model
|
||||
|
||||
// Response example server's response.
|
||||
type Response struct {
|
||||
Message string `json:"msg"`
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"app/database"
|
||||
"app/environment"
|
||||
)
|
||||
|
||||
// GreetService example service.
|
||||
type GreetService interface {
|
||||
Say(input string) (string, error)
|
||||
}
|
||||
|
||||
// NewGreetService returns a service backed with a "db" based on "env".
|
||||
func NewGreetService(env environment.Env, db database.DB) GreetService {
|
||||
service := &greeter{db: db, prefix: "Hello"}
|
||||
|
||||
switch env {
|
||||
case environment.PROD:
|
||||
return service
|
||||
case environment.DEV:
|
||||
return &greeterWithLogging{service}
|
||||
default:
|
||||
panic("unknown environment")
|
||||
}
|
||||
}
|
||||
|
||||
type greeter struct {
|
||||
prefix string
|
||||
db database.DB
|
||||
}
|
||||
|
||||
func (s *greeter) Say(input string) (string, error) {
|
||||
if err := s.db.Exec("simulate a query..."); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
result := s.prefix + " " + input
|
||||
return result, nil
|
||||
}
|
||||
|
||||
type greeterWithLogging struct {
|
||||
*greeter
|
||||
}
|
||||
|
||||
func (s *greeterWithLogging) Say(input string) (string, error) {
|
||||
result, err := s.greeter.Say(input)
|
||||
fmt.Printf("result: %s\nerror: %v\n", result, err)
|
||||
return result, err
|
||||
}
|
||||
@@ -4,7 +4,7 @@ package main
|
||||
/*
|
||||
There is no MVC naming pattern for such these things,you can imagine the limitations of that.
|
||||
Instead you can use the `BeforeActivation` on your controller to add more advanced routing features
|
||||
(https://github.com/kataras/iris/tree/master/_examples/routing).
|
||||
(https://github.com/kataras/iris/tree/main/_examples/routing).
|
||||
|
||||
You can also create your own macro,
|
||||
i.e: /{file:json} or macro function of a specific parameter type i.e: (/{file:string json()}).
|
||||
@@ -24,13 +24,13 @@ func main() {
|
||||
|
||||
// http://localhost:8080/module/xxx.json (OK)
|
||||
// http://localhost:8080/module/xxx.xml (Not Found)
|
||||
app.Run(iris.Addr(":8080"))
|
||||
app.Listen(":8080")
|
||||
}
|
||||
|
||||
type myController struct{}
|
||||
|
||||
func (m *myController) BeforeActivation(b mvc.BeforeActivation) {
|
||||
// b.Dependencies().Add/Remove
|
||||
// b.Dependencies().Register
|
||||
// b.Router().Use/UseGlobal/Done // and any standard API call you already know
|
||||
|
||||
// 1-> Method
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ package datamodels
|
||||
// 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.
|
||||
// for the sake of simplicty.
|
||||
type Movie struct {
|
||||
ID int64 `json:"id"`
|
||||
Name string `json:"name"`
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
|
||||
package datasource
|
||||
|
||||
import "github.com/kataras/iris/v12/_examples/mvc/overview/datamodels"
|
||||
import "github.com/kataras/iris/v12/_examples/mvc/repository/datamodels"
|
||||
|
||||
// Movies is our imaginary data source.
|
||||
var Movies = map[int64]datamodels.Movie{
|
||||
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |
@@ -0,0 +1,53 @@
|
||||
// file: main.go
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/kataras/iris/v12/_examples/mvc/repository/datasource"
|
||||
"github.com/kataras/iris/v12/_examples/mvc/repository/repositories"
|
||||
"github.com/kataras/iris/v12/_examples/mvc/repository/services"
|
||||
"github.com/kataras/iris/v12/_examples/mvc/repository/web/controllers"
|
||||
"github.com/kataras/iris/v12/_examples/mvc/repository/web/middleware"
|
||||
|
||||
"github.com/kataras/iris/v12"
|
||||
"github.com/kataras/iris/v12/mvc"
|
||||
)
|
||||
|
||||
func main() {
|
||||
app := iris.New()
|
||||
app.Logger().SetLevel("debug")
|
||||
|
||||
// Load the template files.
|
||||
app.RegisterView(iris.HTML("./web/views", ".html"))
|
||||
|
||||
// Serve our controllers.
|
||||
mvc.New(app.Party("/hello")).Handle(new(controllers.HelloController))
|
||||
// You can also split the code you write to configure an mvc.Application
|
||||
// using the `mvc.Configure` method, as shown below.
|
||||
mvc.Configure(app.Party("/movies"), movies)
|
||||
|
||||
// http://localhost:8080/hello
|
||||
// http://localhost:8080/hello/iris
|
||||
// http://localhost:8080/movies
|
||||
// http://localhost:8080/movies/1
|
||||
app.Listen(":8080", iris.WithOptimizations)
|
||||
}
|
||||
|
||||
// note the mvc.Application, it's not iris.Application.
|
||||
func movies(app *mvc.Application) {
|
||||
// Add the basic authentication(admin:password) middleware
|
||||
// for the /movies based requests.
|
||||
app.Router.Use(middleware.BasicAuth)
|
||||
|
||||
// Create our movie repository with some (memory) data from the datasource.
|
||||
repo := repositories.NewMovieRepository(datasource.Movies)
|
||||
// Create our movie service, we will bind it to the movie app's dependencies.
|
||||
movieService := services.NewMovieService(repo)
|
||||
app.Register(movieService)
|
||||
|
||||
// serve our movies controller.
|
||||
// Note that you can serve more than one controller
|
||||
// you can also create child mvc apps using the `movies.Party(relativePath)` or `movies.Clone(app.Party(...))`
|
||||
// if you want.
|
||||
app.Handle(new(controllers.MovieController))
|
||||
}
|
||||
+19
-19
@@ -1,20 +1,20 @@
|
||||
# Domain Models
|
||||
|
||||
There should be the domain/business-level models.
|
||||
|
||||
Example:
|
||||
|
||||
```go
|
||||
import "github.com/kataras/iris/v12/_examples/mvc/overview/datamodels"
|
||||
|
||||
type Movie struct {
|
||||
datamodels.Movie
|
||||
}
|
||||
|
||||
func (m Movie) Validate() (Movie, error) {
|
||||
/* do some checks and return an error if that Movie is not valid */
|
||||
}
|
||||
```
|
||||
|
||||
However, we will use the "datamodels" as the only one models package because
|
||||
# Domain Models
|
||||
|
||||
There should be the domain/business-level models.
|
||||
|
||||
Example:
|
||||
|
||||
```go
|
||||
import "github.com/kataras/iris/v12/_examples/mvc/repository/datamodels"
|
||||
|
||||
type Movie struct {
|
||||
datamodels.Movie
|
||||
}
|
||||
|
||||
func (m Movie) Validate() (Movie, error) {
|
||||
/* do some checks and return an error if that Movie is not valid */
|
||||
}
|
||||
```
|
||||
|
||||
However, we will use the "datamodels" as the only one models package because
|
||||
Movie structure we don't need any extra functionality or validation inside it.
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
# Repositories
|
||||
|
||||
# Repositories
|
||||
|
||||
The package which has direct access to the "datasource" and can manipulate data directly.
|
||||
+1
-1
@@ -6,7 +6,7 @@ import (
|
||||
"errors"
|
||||
"sync"
|
||||
|
||||
"github.com/kataras/iris/v12/_examples/mvc/overview/datamodels"
|
||||
"github.com/kataras/iris/v12/_examples/mvc/repository/datamodels"
|
||||
)
|
||||
|
||||
// Query represents the visitor and action queries.
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
# Service Layer
|
||||
|
||||
# Service Layer
|
||||
|
||||
The package which has access to call functions from the "repositories" and "models" ("datamodels" only in that simple example). It should contain the domain logic.
|
||||
+2
-2
@@ -3,8 +3,8 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"github.com/kataras/iris/v12/_examples/mvc/overview/datamodels"
|
||||
"github.com/kataras/iris/v12/_examples/mvc/overview/repositories"
|
||||
"github.com/kataras/iris/v12/_examples/mvc/repository/datamodels"
|
||||
"github.com/kataras/iris/v12/_examples/mvc/repository/repositories"
|
||||
)
|
||||
|
||||
// MovieService handles some of the CRUID operations of the movie datamodel.
|
||||
+11
-9
@@ -5,8 +5,8 @@ package controllers
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/kataras/iris/v12/_examples/mvc/overview/datamodels"
|
||||
"github.com/kataras/iris/v12/_examples/mvc/overview/services"
|
||||
"github.com/kataras/iris/v12/_examples/mvc/repository/datamodels"
|
||||
"github.com/kataras/iris/v12/_examples/mvc/repository/services"
|
||||
|
||||
"github.com/kataras/iris/v12"
|
||||
)
|
||||
@@ -23,14 +23,16 @@ type MovieController struct {
|
||||
// curl -i http://localhost:8080/movies
|
||||
//
|
||||
// The correct way if you have sensitive data:
|
||||
// func (c *MovieController) Get() (results []viewmodels.Movie) {
|
||||
// data := c.Service.GetAll()
|
||||
//
|
||||
// for _, movie := range data {
|
||||
// results = append(results, viewmodels.Movie{movie})
|
||||
// }
|
||||
// return
|
||||
// }
|
||||
// func (c *MovieController) Get() (results []viewmodels.Movie) {
|
||||
// data := c.Service.GetAll()
|
||||
//
|
||||
// for _, movie := range data {
|
||||
// results = append(results, viewmodels.Movie{movie})
|
||||
// }
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// otherwise just return the datamodels.
|
||||
func (c *MovieController) Get() (results []datamodels.Movie) {
|
||||
return c.Service.GetAll()
|
||||
+2
-4
@@ -5,8 +5,6 @@ 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",
|
||||
},
|
||||
var BasicAuth = basicauth.Default(map[string]string{
|
||||
"admin": "password",
|
||||
})
|
||||
+55
-55
@@ -1,56 +1,56 @@
|
||||
# View Models
|
||||
|
||||
There should be the view models, the structure that the client will be able to see.
|
||||
|
||||
Example:
|
||||
|
||||
```go
|
||||
import (
|
||||
"github.com/kataras/iris/v12/_examples/mvc/overview/datamodels"
|
||||
|
||||
"github.com/kataras/iris/v12"
|
||||
"github.com/kataras/iris/v12/context"
|
||||
)
|
||||
|
||||
type Movie struct {
|
||||
datamodels.Movie
|
||||
}
|
||||
|
||||
func (m Movie) IsValid() bool {
|
||||
/* do some checks and return true if it's valid... */
|
||||
return m.ID > 0
|
||||
}
|
||||
```
|
||||
|
||||
Iris is able to convert any custom data Structure into an HTTP Response Dispatcher,
|
||||
so theoretically, something like the following is permitted if it's really necessary;
|
||||
|
||||
```go
|
||||
// Dispatch completes the `kataras/iris/mvc#Result` interface.
|
||||
// Sends a `Movie` as a controlled http response.
|
||||
// If its ID is zero or less then it returns a 404 not found error
|
||||
// else it returns its json representation,
|
||||
// (just like the controller's functions do for custom types by default).
|
||||
//
|
||||
// Don't overdo it, the application's logic should not be here.
|
||||
// It's just one more step of validation before the response,
|
||||
// simple checks can be added here.
|
||||
//
|
||||
// It's just a showcase,
|
||||
// imagine the potentials this feature gives when designing a bigger application.
|
||||
//
|
||||
// This is called where the return value from a controller's method functions
|
||||
// is type of `Movie`.
|
||||
// For example the `controllers/movie_controller.go#GetBy`.
|
||||
func (m Movie) Dispatch(ctx iris.Context) {
|
||||
if !m.IsValid() {
|
||||
ctx.NotFound()
|
||||
return
|
||||
}
|
||||
ctx.JSON(m, context.JSON{Indent: " "})
|
||||
}
|
||||
```
|
||||
|
||||
However, we will use the "datamodels" as the only one models package because
|
||||
Movie structure doesn't contain any sensitive data, clients are able to see all of its fields
|
||||
# View Models
|
||||
|
||||
There should be the view models, the structure that the client will be able to see.
|
||||
|
||||
Example:
|
||||
|
||||
```go
|
||||
import (
|
||||
"github.com/kataras/iris/v12/_examples/mvc/repository/datamodels"
|
||||
|
||||
"github.com/kataras/iris/v12"
|
||||
"github.com/kataras/iris/v12/context"
|
||||
)
|
||||
|
||||
type Movie struct {
|
||||
datamodels.Movie
|
||||
}
|
||||
|
||||
func (m Movie) IsValid() bool {
|
||||
/* do some checks and return true if it's valid... */
|
||||
return m.ID > 0
|
||||
}
|
||||
```
|
||||
|
||||
Iris is able to convert any custom data Structure into an HTTP Response Dispatcher,
|
||||
so theoretically, something like the following is permitted if it's really necessary;
|
||||
|
||||
```go
|
||||
// Dispatch completes the `kataras/iris/mvc#Result` interface.
|
||||
// Sends a `Movie` as a controlled http response.
|
||||
// If its ID is zero or less then it returns a 404 not found error
|
||||
// else it returns its json representation,
|
||||
// (just like the controller's functions do for custom types by default).
|
||||
//
|
||||
// Don't overdo it, the application's logic should not be here.
|
||||
// It's just one more step of validation before the response,
|
||||
// simple checks can be added here.
|
||||
//
|
||||
// It's just a showcase,
|
||||
// imagine the potentials this feature gives when designing a bigger application.
|
||||
//
|
||||
// This is called where the return value from a controller's method functions
|
||||
// is type of `Movie`.
|
||||
// For example the `controllers/movie_controller.go#GetBy`.
|
||||
func (m Movie) Dispatch(ctx iris.Context) {
|
||||
if !m.IsValid() {
|
||||
ctx.NotFound()
|
||||
return
|
||||
}
|
||||
ctx.JSON(m, context.JSON{Indent: " "})
|
||||
}
|
||||
```
|
||||
|
||||
However, we will use the "datamodels" as the only one models package because
|
||||
Movie structure doesn't contain any sensitive data, clients are able to see all of its fields
|
||||
and we don't need any extra functionality or validation inside it.
|
||||
+11
-11
@@ -1,12 +1,12 @@
|
||||
<!-- file: web/views/hello/index.html -->
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>{{.Title}} - My App</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<p>{{.MyMessage}}</p>
|
||||
</body>
|
||||
|
||||
<!-- file: web/views/hello/index.html -->
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>{{.Title}} - My App</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<p>{{.MyMessage}}</p>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
+11
-11
@@ -1,12 +1,12 @@
|
||||
<!-- file: web/views/hello/name.html -->
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>{{.}}' Portfolio - My App</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>Hello {{.}}</h1>
|
||||
</body>
|
||||
|
||||
<!-- file: web/views/hello/name.html -->
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>{{.}}' Portfolio - My App</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>Hello {{.}}</h1>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,77 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/kataras/iris/v12"
|
||||
"github.com/kataras/iris/v12/mvc"
|
||||
)
|
||||
|
||||
// https://github.com/kataras/iris/issues/1706
|
||||
// When you need that type of logic behind a request input,
|
||||
// e.g. set default values, the right way to do that is
|
||||
// to register a request-scoped dependency for that type.
|
||||
// We have the `Context.ReadQuery(pointer)`
|
||||
// which you can use to bind a struct value, that value can have default values.
|
||||
// Here is how you could do that:
|
||||
func main() {
|
||||
app := iris.New()
|
||||
|
||||
mvcApp := mvc.New(app)
|
||||
{
|
||||
mvcApp.Register(paramsDependency)
|
||||
|
||||
mvcApp.Handle(new(controller))
|
||||
}
|
||||
|
||||
// http://localhost:8080/records?phone=random&order_by=DESC
|
||||
// http://localhost:8080/records?phone=random
|
||||
app.Listen(":8080")
|
||||
}
|
||||
|
||||
type params struct {
|
||||
CallID string `url:"phone"`
|
||||
ComDir int `url:"dir"`
|
||||
CaseUserID string `url:"on"`
|
||||
StartYear int `url:"sy"`
|
||||
EndYear int `url:"ey"`
|
||||
OrderBy string `url:"order_by"`
|
||||
Offset int `url:"offset"`
|
||||
}
|
||||
|
||||
// As we've read in the previous examples, the paramsDependency
|
||||
// describes a request-scoped dependency.
|
||||
// It should accept the iris context (or any previously registered or builtin dependency)
|
||||
// and it should return the value which will be binded to the
|
||||
// controller's methods (or fields) - see `GetRecords`.
|
||||
var paramsDependency = func(ctx iris.Context) params {
|
||||
p := params{
|
||||
OrderBy: "ASC", // default value.
|
||||
}
|
||||
// Bind the URL values to the "p":
|
||||
ctx.ReadQuery(&p)
|
||||
// Or bind a specific URL value by giving a default value:
|
||||
// p.OrderBy = ctx.URLParamDefault("order_by", "ASC")
|
||||
//
|
||||
// OR make checks for default values after ReadXXX,
|
||||
// e.g. if p.OrderBy == "" {...}
|
||||
|
||||
/* More options to read a request:
|
||||
// Bind the JSON request body to the "p":
|
||||
ctx.ReadJSON(&p)
|
||||
// Bind the Form to the "p":
|
||||
ctx.ReadForm(&p)
|
||||
// Bind any, based on the client's content-type header:
|
||||
ctx.ReadBody(&p)
|
||||
// Bind the http requests to a struct value:
|
||||
ctx.ReadHeader(&h)
|
||||
*/
|
||||
|
||||
return p
|
||||
}
|
||||
|
||||
type controller struct{}
|
||||
|
||||
func (c *controller) GetRecords(stru params) string {
|
||||
return fmt.Sprintf("%#+v\n", stru)
|
||||
}
|
||||
@@ -11,16 +11,14 @@ import (
|
||||
|
||||
// VisitController handles the root route.
|
||||
type VisitController struct {
|
||||
// the current request session,
|
||||
// its initialization happens by the dependency function that we've added to the `visitApp`.
|
||||
// the current request session, automatically binded.
|
||||
Session *sessions.Session
|
||||
|
||||
// A time.time which is binded from the MVC,
|
||||
// order of binded fields doesn't matter.
|
||||
// A time.time which is binded from the MVC application manually.
|
||||
StartTime time.Time
|
||||
}
|
||||
|
||||
// Get handles
|
||||
// Get handles index
|
||||
// Method: GET
|
||||
// Path: http://localhost:8080
|
||||
func (c *VisitController) Get() string {
|
||||
@@ -35,24 +33,14 @@ func (c *VisitController) Get() string {
|
||||
|
||||
func newApp() *iris.Application {
|
||||
app := iris.New()
|
||||
// Configure sessions manager as we used to.
|
||||
sess := sessions.New(sessions.Config{Cookie: "mysession_cookie_name"})
|
||||
app.Use(sess.Handler())
|
||||
|
||||
visitApp := mvc.New(app)
|
||||
visitApp.Register(time.Now())
|
||||
// The `VisitController.Session` is automatically binded to the current `sessions.Session`.
|
||||
|
||||
visitApp := mvc.New(app.Party("/"))
|
||||
// bind the current *session.Session, which is required, to the `VisitController.Session`
|
||||
// and the time.Now() to the `VisitController.StartTime`.
|
||||
visitApp.Register(
|
||||
// if dependency is a function which accepts
|
||||
// a Context and returns a single value
|
||||
// then the result type of this function is resolved by the controller
|
||||
// and on each request it will call the function with its Context
|
||||
// and set the result(the *sessions.Session here) to the controller's field.
|
||||
//
|
||||
// If dependencies are registered without field or function's input arguments as
|
||||
// consumers then those dependencies are being ignored before the server ran,
|
||||
// so you can bind many dependecies and use them in different controllers.
|
||||
sess.Start,
|
||||
time.Now(),
|
||||
)
|
||||
visitApp.Handle(new(VisitController))
|
||||
|
||||
return app
|
||||
@@ -61,10 +49,10 @@ func newApp() *iris.Application {
|
||||
func main() {
|
||||
app := newApp()
|
||||
|
||||
// 1. open the browser
|
||||
// 1. Prepare a client, e.g. your browser
|
||||
// 2. navigate to http://localhost:8080
|
||||
// 3. refresh the page some times
|
||||
// 4. close the browser
|
||||
// 5. re-open the browser (if it wasn't in private mode) and re-play 2.
|
||||
app.Run(iris.Addr(":8080"))
|
||||
app.Listen(":8080")
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ func main() {
|
||||
mvc.New(app.Party("/")).Handle(&globalVisitorsController{visits: 0})
|
||||
|
||||
// http://localhost:8080
|
||||
app.Run(iris.Addr(":8080"))
|
||||
app.Listen(":8080")
|
||||
}
|
||||
|
||||
type globalVisitorsController struct {
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/kataras/iris/v12"
|
||||
"github.com/kataras/iris/v12/mvc"
|
||||
)
|
||||
|
||||
// Optional deprecated X-API-XXX headers for version 1.
|
||||
var opts = mvc.DeprecationOptions{
|
||||
WarnMessage: "deprecated, see <this link>",
|
||||
DeprecationDate: time.Now().UTC(),
|
||||
DeprecationInfo: "a bigger version is available, see <this link> for more information",
|
||||
}
|
||||
|
||||
func main() {
|
||||
app := newApp()
|
||||
|
||||
// See main_test.go for request examples.
|
||||
app.Listen(":8080")
|
||||
}
|
||||
|
||||
func newApp() *iris.Application {
|
||||
app := iris.New()
|
||||
|
||||
dataRouter := app.Party("/data")
|
||||
{
|
||||
m := mvc.New(dataRouter)
|
||||
|
||||
m.Handle(new(v1Controller), mvc.Version("1.0.0"), mvc.Deprecated(opts))
|
||||
m.Handle(new(v2Controller), mvc.Version("2.3.0"))
|
||||
m.Handle(new(v3Controller), mvc.Version(">=3.0.0 <4.0.0"))
|
||||
m.Handle(new(noVersionController)) // or if missing it will respond with 501 version not found.
|
||||
}
|
||||
|
||||
return app
|
||||
}
|
||||
|
||||
type v1Controller struct{}
|
||||
|
||||
func (c *v1Controller) Get() string {
|
||||
return "data (v1.x)"
|
||||
}
|
||||
|
||||
type v2Controller struct{}
|
||||
|
||||
func (c *v2Controller) Get() string {
|
||||
return "data (v2.x)"
|
||||
}
|
||||
|
||||
type v3Controller struct{}
|
||||
|
||||
func (c *v3Controller) Get() string {
|
||||
return "data (v3.x)"
|
||||
}
|
||||
|
||||
type noVersionController struct{}
|
||||
|
||||
func (c *noVersionController) Get() string {
|
||||
return "data"
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/kataras/iris/v12"
|
||||
"github.com/kataras/iris/v12/httptest"
|
||||
"github.com/kataras/iris/v12/versioning"
|
||||
)
|
||||
|
||||
func TestVersionedController(t *testing.T) {
|
||||
app := newApp()
|
||||
|
||||
e := httptest.New(t, app)
|
||||
e.GET("/data").WithHeader(versioning.AcceptVersionHeaderKey, "1.0.0").Expect().
|
||||
Status(iris.StatusOK).Body().IsEqual("data (v1.x)")
|
||||
e.GET("/data").WithHeader(versioning.AcceptVersionHeaderKey, "2.3.0").Expect().
|
||||
Status(iris.StatusOK).Body().IsEqual("data (v2.x)")
|
||||
e.GET("/data").WithHeader(versioning.AcceptVersionHeaderKey, "3.1.0").Expect().
|
||||
Status(iris.StatusOK).Body().IsEqual("data (v3.x)")
|
||||
|
||||
// Test invalid version or no version at all.
|
||||
e.GET("/data").WithHeader(versioning.AcceptVersionHeaderKey, "4.0.0").Expect().
|
||||
Status(iris.StatusOK).Body().IsEqual("data")
|
||||
e.GET("/data").Expect().
|
||||
Status(iris.StatusOK).Body().IsEqual("data")
|
||||
|
||||
// Test Deprecated (v1)
|
||||
ex := e.GET("/data").WithHeader(versioning.AcceptVersionHeaderKey, "1.0.0").Expect()
|
||||
ex.Status(iris.StatusOK).Body().IsEqual("data (v1.x)")
|
||||
ex.Header("X-API-Warn").Equal(opts.WarnMessage)
|
||||
expectedDateStr := opts.DeprecationDate.Format(app.ConfigurationReadOnly().GetTimeFormat())
|
||||
ex.Header("X-API-Deprecation-Date").Equal(expectedDateStr)
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user