Iris: how to pass context acquired in `WrapRouter` to `http.HandlerFunc`

Created on 29 Oct 2019  路  2Comments  路  Source: kataras/iris

I need some help,
The following example code:

    app.WrapRouter(func(w http.ResponseWriter, r *http.Request, router http.HandlerFunc) {
        path := r.URL.Path
        pathSlice := strings.Split(path, "/")
        needRemovePart := pathSlice[len(pathSlice)-1]
        newPath := strings.Join(pathSlice[:len(pathSlice)-1], "/")
        r.URL.Path = newPath
        ctx := app.ContextPool.Acquire(w, r)
        ctx.Values().Set("removePath", needRemovePart)
        router(w, r)
        return
    })

I want to truncate the last part needRemovePart in url path, then store into context ctx.Values().Set("removePath", needRemovePart) for future middleware use. The new url path newPath continue to match the route.

But the result is not what I expected,
ctx := app.ContextPool.Acquire(w, r) include BeginRequest will return a new Context,
https://github.com/kataras/iris/blob/d6cbfc4cc63482ea7356c2db41b6c507c6761cfb/context/pool.go#L33-L39

https://github.com/kataras/iris/blob/d6cbfc4cc63482ea7356c2db41b6c507c6761cfb/context/context.go#L1085-L1093

The controller whole flow also include BeginRequest and use a new Context, How to use same one?

question

Most helpful comment

Hello @Ostaer and sorry for the delay. You have two ways to do it:

1. The router has the ServeHTTPC method to handle requests based on an already-created Context.

https://github.com/kataras/iris/blob/46b761ba4661ff771e5de9819d852dc933f74eb1/core/router/router.go#L156-L159

Example:

app.WrapRouter(func(w http.ResponseWriter, r *http.Request, router http.HandlerFunc) {
    path := r.URL.Path
    pathSlice := strings.Split(path, "/")
    needRemovePart := pathSlice[len(pathSlice)-1]
    newPath := strings.Join(pathSlice[:len(pathSlice)-1], "/")
    r.URL.Path = newPath

    // BEGIN
    ctx := app.ContextPool.Acquire(w, r)
    ctx.Values().Set(removedPathCtxKey, needRemovePart)
    app.ServeHTTPC(ctx) // <---
    app.ContextPool.Release(ctx) // <---
    // END
})


app.Get("/remove", func(ctx iris.Context) {
    removedPathPart := ctx.Values().Get(removedPathCtxKey)

    ctx.Writef("As Path: %s (registered route path: %s)\nRemoved Path Part was: /%s\n", ctx.Path(), ctx.GetCurrentRoute().Path(), removedPathPart)
})

2. You can always use the std net/http request's values (when you rely on third-parties that must run before router) to store such data and retrieve it with a middleware which will store the data(in your case the, "removedPath") to ctx.Values() if you want so. Example:

package main

import (
    "context"
    "net/http"
    "strings"

    "github.com/kataras/iris/v12"
)

func main() {
app := iris.New()

// BEGIN
const removedPathCtxKey = "removedPath"

app.WrapRouter(func(w http.ResponseWriter, r *http.Request, router http.HandlerFunc) {
    path := r.URL.Path
    pathSlice := strings.Split(path, "/")
    needRemovePart := pathSlice[len(pathSlice)-1]
    newPath := strings.Join(pathSlice[:len(pathSlice)-1], "/")
    r.URL.Path = newPath
    router(w, r.WithContext(context.WithValue(context.TODO(), removedPathCtxKey, needRemovePart)))
})

// Use `app.UseGlobal` or `app.Use`, we use `app.UseGlobal` here
// to ensure that this middleware is always in front of
// any other middleware (but after the router itself) e no matter what .
app.UseGlobal(func(ctx iris.Context) {
    ctx.Values().Set(removedPathCtxKey, ctx.Request().Context().Value(removedPathCtxKey))
    ctx.Next()
})
// END

app.Get("/remove", func(ctx iris.Context) {
    removedPathPart := ctx.Values().Get(removedPathCtxKey)

    ctx.Writef("As Path: %s (registered route path: %s)\nRemoved Path Part was: /%s\n",
    ctx.Path(), ctx.GetCurrentRoute().Path(), removedPathPart)
})

// GET: http://localhost:8080/remove/lastpart
app.Run(iris.Addr(":8080"))
}

Output:

1386

The 2. solution has a performance cost because it clones the request on request.WithContext as we already know from Go Authors and net/http developer(s).

Thanks,
Gerasimos Maropoulos

All 2 comments

Hello @Ostaer and sorry for the delay. You have two ways to do it:

1. The router has the ServeHTTPC method to handle requests based on an already-created Context.

https://github.com/kataras/iris/blob/46b761ba4661ff771e5de9819d852dc933f74eb1/core/router/router.go#L156-L159

Example:

app.WrapRouter(func(w http.ResponseWriter, r *http.Request, router http.HandlerFunc) {
    path := r.URL.Path
    pathSlice := strings.Split(path, "/")
    needRemovePart := pathSlice[len(pathSlice)-1]
    newPath := strings.Join(pathSlice[:len(pathSlice)-1], "/")
    r.URL.Path = newPath

    // BEGIN
    ctx := app.ContextPool.Acquire(w, r)
    ctx.Values().Set(removedPathCtxKey, needRemovePart)
    app.ServeHTTPC(ctx) // <---
    app.ContextPool.Release(ctx) // <---
    // END
})


app.Get("/remove", func(ctx iris.Context) {
    removedPathPart := ctx.Values().Get(removedPathCtxKey)

    ctx.Writef("As Path: %s (registered route path: %s)\nRemoved Path Part was: /%s\n", ctx.Path(), ctx.GetCurrentRoute().Path(), removedPathPart)
})

2. You can always use the std net/http request's values (when you rely on third-parties that must run before router) to store such data and retrieve it with a middleware which will store the data(in your case the, "removedPath") to ctx.Values() if you want so. Example:

package main

import (
    "context"
    "net/http"
    "strings"

    "github.com/kataras/iris/v12"
)

func main() {
app := iris.New()

// BEGIN
const removedPathCtxKey = "removedPath"

app.WrapRouter(func(w http.ResponseWriter, r *http.Request, router http.HandlerFunc) {
    path := r.URL.Path
    pathSlice := strings.Split(path, "/")
    needRemovePart := pathSlice[len(pathSlice)-1]
    newPath := strings.Join(pathSlice[:len(pathSlice)-1], "/")
    r.URL.Path = newPath
    router(w, r.WithContext(context.WithValue(context.TODO(), removedPathCtxKey, needRemovePart)))
})

// Use `app.UseGlobal` or `app.Use`, we use `app.UseGlobal` here
// to ensure that this middleware is always in front of
// any other middleware (but after the router itself) e no matter what .
app.UseGlobal(func(ctx iris.Context) {
    ctx.Values().Set(removedPathCtxKey, ctx.Request().Context().Value(removedPathCtxKey))
    ctx.Next()
})
// END

app.Get("/remove", func(ctx iris.Context) {
    removedPathPart := ctx.Values().Get(removedPathCtxKey)

    ctx.Writef("As Path: %s (registered route path: %s)\nRemoved Path Part was: /%s\n",
    ctx.Path(), ctx.GetCurrentRoute().Path(), removedPathPart)
})

// GET: http://localhost:8080/remove/lastpart
app.Run(iris.Addr(":8080"))
}

Output:

1386

The 2. solution has a performance cost because it clones the request on request.WithContext as we already know from Go Authors and net/http developer(s).

Thanks,
Gerasimos Maropoulos

The ServeHTTPC method is suitable for me, Thanks.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

zfanmy picture zfanmy  路  3Comments

Dexus picture Dexus  路  4Comments

chiquitawow picture chiquitawow  路  5Comments

stefanwuthrich picture stefanwuthrich  路  4Comments

shenkaige picture shenkaige  路  5Comments