mirror of
https://github.com/kataras/iris.git
synced 2025-12-17 09:57:01 +00:00
New: gRPC MVC features, new WithLowercaseRouting option and add some new context methods
read HISTORY.md Former-commit-id: 30a16cceb11f754aa32923058abeda1e736350e7
This commit is contained in:
@@ -2,6 +2,7 @@ package mvc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
@@ -81,6 +82,9 @@ type ControllerActivator struct {
|
||||
|
||||
// true if this controller listens and serves to websocket events.
|
||||
servesWebsocket bool
|
||||
|
||||
// true to skip the internal "activate".
|
||||
activated bool
|
||||
}
|
||||
|
||||
// NameOf returns the package name + the struct type's name,
|
||||
@@ -96,6 +100,14 @@ func NameOf(v interface{}) string {
|
||||
}
|
||||
|
||||
func newControllerActivator(app *Application, controller interface{}) *ControllerActivator {
|
||||
if controller == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if c, ok := controller.(*ControllerActivator); ok {
|
||||
return c
|
||||
}
|
||||
|
||||
typ := reflect.TypeOf(controller)
|
||||
|
||||
c := &ControllerActivator{
|
||||
@@ -225,6 +237,11 @@ func (c *ControllerActivator) isReservedMethod(name string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (c *ControllerActivator) markAsWebsocket() {
|
||||
c.servesWebsocket = true
|
||||
c.attachInjector()
|
||||
}
|
||||
|
||||
func (c *ControllerActivator) attachInjector() {
|
||||
if c.injector == nil {
|
||||
partyCountParams := macro.CountParams(c.app.Router.GetRelPath(), *c.app.Router.Macros())
|
||||
@@ -232,12 +249,18 @@ func (c *ControllerActivator) attachInjector() {
|
||||
}
|
||||
}
|
||||
|
||||
func (c *ControllerActivator) markAsWebsocket() {
|
||||
c.servesWebsocket = true
|
||||
c.attachInjector()
|
||||
// Activated can be called to skip the internal method parsing.
|
||||
func (c *ControllerActivator) Activated() bool {
|
||||
b := c.activated
|
||||
c.activated = true
|
||||
return b
|
||||
}
|
||||
|
||||
func (c *ControllerActivator) activate() {
|
||||
if c.Activated() {
|
||||
return
|
||||
}
|
||||
|
||||
c.parseMethods()
|
||||
}
|
||||
|
||||
@@ -314,10 +337,16 @@ func (c *ControllerActivator) handleMany(method, path, funcName string, override
|
||||
return nil
|
||||
}
|
||||
|
||||
wd, _ := os.Getwd()
|
||||
|
||||
for _, r := range routes {
|
||||
// change the main handler's name in order to respect the controller's and give
|
||||
// a proper debug message.
|
||||
// change the main handler's name and file:line
|
||||
// in order to respect the controller's and give
|
||||
// a proper debug/log message.
|
||||
r.MainHandlerName = fmt.Sprintf("%s.%s", c.fullName, funcName)
|
||||
if m, ok := c.Type.MethodByName(funcName); ok {
|
||||
r.SourceFileName, r.SourceLineNumber = context.HandlerFileLineRel(m.Func, wd)
|
||||
}
|
||||
}
|
||||
|
||||
// add this as a reserved method name in order to
|
||||
|
||||
66
mvc/grpc.go
Normal file
66
mvc/grpc.go
Normal file
@@ -0,0 +1,66 @@
|
||||
package mvc
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"path"
|
||||
|
||||
"github.com/kataras/iris/v12/context"
|
||||
)
|
||||
|
||||
// GRPC registers a controller which serves gRPC clients.
|
||||
// It accepts the controller ptr to a struct value,
|
||||
// the gRPCServer itself, and a strict option which is explained below.
|
||||
//
|
||||
// The differences by a common controller are:
|
||||
// HTTP verb: only POST (Party.AllowMethods can be used for more),
|
||||
// method parsing is disabled: path is the function name as it is,
|
||||
// if 'strictMode' option is true then this controller will only serve gRPC-based clients
|
||||
// and fires 404 on common HTTP clients,
|
||||
// otherwise HTTP clients can send and receive JSON (protos contain json struct fields by-default).
|
||||
type GRPC struct {
|
||||
// Server is required and should be gRPC Server derives from google's grpc package.
|
||||
Server http.Handler
|
||||
// ServiceName is required and should be the name of the service (used to build the gRPC route path),
|
||||
// e.g. "helloworld.Greeter".
|
||||
// For a controller's method of "SayHello" and ServiceName "helloworld.Greeter",
|
||||
// both gRPC and common HTTP request path is: "/helloworld.Greeter/SayHello".
|
||||
//
|
||||
// Tip: the ServiceName can be fetched through proto's file descriptor, e.g.
|
||||
// serviceName := pb.File_helloworld_proto.Services().Get(0).FullName().
|
||||
ServiceName string
|
||||
|
||||
// When Strict option is true then this controller will only serve gRPC-based clients
|
||||
// and fires 404 on common HTTP clients.
|
||||
Strict bool
|
||||
}
|
||||
|
||||
// Apply parses the controller's methods and registers gRPC handlers to the application.
|
||||
func (g GRPC) Apply(c *ControllerActivator) {
|
||||
defer c.Activated()
|
||||
|
||||
pre := func(ctx context.Context) {
|
||||
if ctx.IsGRPC() { // gRPC, consumes and produces protobuf.
|
||||
g.Server.ServeHTTP(ctx.ResponseWriter(), ctx.Request())
|
||||
ctx.StopExecution()
|
||||
return
|
||||
}
|
||||
|
||||
if g.Strict {
|
||||
ctx.NotFound()
|
||||
} else {
|
||||
// Allow common HTTP clients, consumes and produces JSON.
|
||||
ctx.Next()
|
||||
}
|
||||
}
|
||||
|
||||
for i := 0; i < c.Type.NumMethod(); i++ {
|
||||
m := c.Type.Method(i)
|
||||
path := path.Join(g.ServiceName, m.Name)
|
||||
if route := c.Handle(http.MethodPost, path, m.Name, pre); route != nil {
|
||||
route.Description = "gRPC"
|
||||
if g.Strict {
|
||||
route.Description = "-only"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
24
mvc/mvc.go
24
mvc/mvc.go
@@ -111,6 +111,15 @@ func (app *Application) Register(dependencies ...interface{}) *Application {
|
||||
return app
|
||||
}
|
||||
|
||||
// Option is an interface which does contain a single `Apply` method that accepts
|
||||
// a `ControllerActivator`. It can be passed on `Application.Handle` method to
|
||||
// mdoify the behavior right after the `BeforeActivation` state.
|
||||
//
|
||||
// See `GRPC` package-level structure too.
|
||||
type Option interface {
|
||||
Apply(*ControllerActivator)
|
||||
}
|
||||
|
||||
// Handle serves a controller for the current mvc application's Router.
|
||||
// It accept any custom struct which its functions will be transformed
|
||||
// to routes.
|
||||
@@ -154,9 +163,12 @@ func (app *Application) Register(dependencies ...interface{}) *Application {
|
||||
// Result or (Result, error)
|
||||
// where Get is an HTTP Method func.
|
||||
//
|
||||
// Default behavior can be changed through second, variadic, variable "options",
|
||||
// e.g. Handle(controller, GRPC {Server: grpcServer, Strict: true})
|
||||
//
|
||||
// Examples at: https://github.com/kataras/iris/tree/master/_examples/mvc
|
||||
func (app *Application) Handle(controller interface{}) *Application {
|
||||
app.handle(controller)
|
||||
func (app *Application) Handle(controller interface{}, options ...Option) *Application {
|
||||
app.handle(controller, options...)
|
||||
return app
|
||||
}
|
||||
|
||||
@@ -195,7 +207,7 @@ func (app *Application) GetNamespaces() websocket.Namespaces {
|
||||
return websocket.JoinConnHandlers(app.websocketControllers...).GetNamespaces()
|
||||
}
|
||||
|
||||
func (app *Application) handle(controller interface{}) *ControllerActivator {
|
||||
func (app *Application) handle(controller interface{}, options ...Option) *ControllerActivator {
|
||||
// initialize the controller's activator, nothing too magical so far.
|
||||
c := newControllerActivator(app, controller)
|
||||
|
||||
@@ -208,6 +220,12 @@ func (app *Application) handle(controller interface{}) *ControllerActivator {
|
||||
before.BeforeActivation(c)
|
||||
}
|
||||
|
||||
for _, opt := range options {
|
||||
if opt != nil {
|
||||
opt.Apply(c)
|
||||
}
|
||||
}
|
||||
|
||||
c.activate()
|
||||
|
||||
if after, okAfter := controller.(interface {
|
||||
|
||||
Reference in New Issue
Block a user