Iris: How can I get register session dependency in middleware?

Created on 25 Jun 2018  ·  7Comments  ·  Source: kataras/iris

Problem

I need to use session in auth middleware and controller both, so I start session twice. How to do start once and share the same session instance in middleware and controller.

Code

package web

import (
    "github.com/kataras/iris"
    "fmt"
    "github.com/spf13/viper"
    "github.com/kataras/iris/mvc"
    "github.com/kataras/iris/middleware/logger"
    "github.com/kataras/iris/middleware/recover"
)

func RunApp() {
    app := iris.New()
    app.Use(recover.New())
    app.Use(logger.New())

    sesFactory := NewSessionFactory()
    ses := sesFactory.GetSession()
    defer sesFactory.GetSessionDB().Close()
    iris.RegisterOnInterrupt(func() {
        sesFactory.GetSessionDB().Close()
    })

    // start session once in middleware
    app.Use(CreateAuthMiddleware(ses))

    ctlFactory := NewControllerFactory()
    // start session twice when Register
    mvc.New(app.Party("/")).Register(ses.Start).Handle(
        ctlFactory.NewIndex())
    mvc.New(app.Party("/oauth/")).Register(ses.Start).Handle(
        ctlFactory.NewOAuth())
    mvc.New(app.Party("/account/")).Register(ses.Start).Handle(
        ctlFactory.NewAccount())
    mvc.New(app.Party("/tumblr/")).Register(ses.Start).Handle(
        ctlFactory.NewTumblr())

    fmt.Println("load views from", viper.GetString("web.viewDir"))
    app.RegisterView(iris.HTML(viper.GetString("web.viewDir"), ".gohtml"))

    fmt.Println("web service listen on", viper.GetString("web.listenAddr"))
    app.Run(iris.Addr(viper.GetString("web.listenAddr")))
}

question

All 7 comments

The sess.Start will be the same for the WHOLE REQUEST automatically, you don't have to do anything for that, call sess.Start 10000 times in the same request, it will give you the same session instance, so you don't have to worry about "sharing the same instance on the middleware/handlers, sess.Start will give you the same" :)

Now, if you want to use the MVC's dependency injection manager but you want to use middleware/handlers instead whole structs, Iris can do that as well using the hero package, more details can be found at: https://github.com/kataras/iris/blob/master/HISTORY.md#hero

Example:

package main

import (
    "github.com/kataras/iris"
    "github.com/kataras/iris/hero"
)

func main() {

    app := iris.New()

    // 1
    helloHandler := hero.Handler(hello)
    app.Get("/{to:string}", helloHandler)

    // 2
    hero.Register(&myTestService{
        prefix: "Service: Hello",
    })

    helloServiceHandler := hero.Handler(helloService)
    app.Get("/service/{to:string}", helloServiceHandler)

    // 3
    hero.Register(func(ctx iris.Context) (form LoginForm) {
        // it binds the "form" with a
        // x-www-form-urlencoded form data and returns it.
        ctx.ReadForm(&form)
        return
    })

    loginHandler := hero.Handler(login)
    app.Post("/login", loginHandler)

    // http://localhost:8080/your_name
    // http://localhost:8080/service/your_name
    app.Run(iris.Addr(":8080"))
}

func hello(to string) string {
    return "Hello " + to
}

type Service interface {
    SayHello(to string) string
}

type myTestService struct {
    prefix string
}

func (s *myTestService) SayHello(to string) string {
    return s.prefix + " " + to
}

func helloService(to string, service Service) string {
    return service.SayHello(to)
}

type LoginForm struct {
    Username string `form:"username"`
    Password string `form:"password"`
}

func login(form LoginForm) string {
    return "Hello " + form.Username
}

A bigger example can be found at: https://github.com/kataras/iris/tree/master/_examples/hero/overview

Thanks,
Gerasimos Maropoulos

@kataras Got it!Although calling sess.Start multiple times is ok, but there still have a problem. The Set-Cookie header send two times when first create a session, this is exactly where I found the problem.

Can I just pass the session instance started in middleware to handler? Such as using context values?

No it doesn't, if cookie is there it will get it, and it will not send a Set-Cookie twice in the same request (the cookie header is also saved in request and response headers for that reason). You see that because you use postman and it may needs configuration?

About your second question, the examples I give you shows how to that exactly you ask now as well, please read them carefully again.

package main

import (
    "github.com/kataras/iris"
    "github.com/kataras/iris/hero"
        "github.com/kataras/iris/sessions"
)

func main() {
    app := iris.New()
        hero.Register(sess.Start) // sess.Start is a func which returns a *sessions.Session, it's a dynamic dependency depending on the request handler because it's a function (iris is smart), so on each "user" the handlers `set` and `get` will have their own *sessions.Session, the same for the same user.

    getHandler := hero.Handler(get)
    app.Get("/get", getHandler)

    setHandler := hero.Handler(set)
    app.Get("/set", setHandler)
}

func set(s *sessions.Session) error {
    s.Set("name", "a value")
    return nil
}

func get(s *sessions.Session) string {
    return "Hello: "+s.GetString("name")
}

@jaggerwang Yes... sorry about that I forgot to tell you to set the sessions.Config{ AllowReclaim: true} in order to be able to "reclaim" (to add the cookie on the request headers (so it can be available by the same request lifecycle) and on deletion delete on both request and response headers, see here <- this makes the cookie/and the session available immediately.

Thanks a lot! After set AllowReclaim to true, the duplicated Set-Cookie header disappeared.

Yeah, sorry for the delay, I added that on the examples as well, thank you for your contribution @jaggerwang, happy coding!

Was this page helpful?
0 / 5 - 0 ratings