mux icon indicating copy to clipboard operation
mux copied to clipboard

[question]

Open Fyria30 opened this issue 1 year ago • 9 comments

Hello guyes, i would like to know how to make internal redirect. What i mean is: Let's say i have some middlware and two handler. One fo /first/{key} - handler1 and /second/{value} --handler2

So let's assume the chaing is middleware + handler: LogMiddle->AuthMiddle->handler1 and the same is for second one.

Now i want to call handler2 from handler1 avoiding middleware and changingrequest

I try to do somethink like that:

func handler1 (w http.ResponseWriter, req *http.Request){
newPath:= "/second"+ mux.Vars(req)["key"] 
req.URL.Path = newPath

match := mux.RouteMatch{}
isMatched := Router.Match(req, &match)
if isMatched {
 		match.Route.GetHandler().ServeHTTP(w, req)
}
}

So it matches /second/{key} But inside the handler2 when i try val, _ := mux.Vars(req)["value"] i have nothing. (Vars still has only "key" variable)

So i'm searching a way to do it write without external redirection. (Try to avoid auth middleware).

The best example is to creete several handlers, and one of them is: /main/{urlToCall} and then, this main handler takes urlToCall and choose the right one. Is there some proper way?

Fyria30 avatar Sep 15 '22 18:09 Fyria30

I'm not able to understand your use-case. In my opinion your use-case is incorrect because — let's assume we got two routes

1. /first/{key} — FirstHandler
2. /second/{value} — SecondHandler

Since these two routes are different then I dont' think route /first/{key} will give any useful information regarding /second/{value} .

Let's assume for a moment if it works somehow and you're constructing routing (route2) based on the route 1 /first/{key} information then inside your FirstHandler right thing to do is redirect. If there is no such things and you don't want to implement redirect then why not to directly call SecondHandler inside the FirstHandler? Something like below

func FirstHandler(w http.ResponseWriter, r *http.Request) {
	SecondHandler(w, r)
}

func SecondHandler(w http.ResponseWriter, r *http.Request) {
	w.Write([]byte("Gorilla!\n"))
}

Middleware are applied globally on the router level. If there are several URLs like mentioned /main/{urlToCall} possibly this can we worked using nested routes. I would recommend to go through the comment https://github.com/gorilla/mux/issues/445#issuecomment-463220765

If this comment answers what you're looking for, I request you to close the issue.

Thank you

amustaque97 avatar Sep 15 '22 20:09 amustaque97

Hi amustaque97!

Thank you for your reply! Let me be more clear.

Let's assume i have a route which serves presigned url. /presigned/{urlStructure} {urlStructure} key contains info about url which should be called, about call rules, timestamp and so on. So base on {urlStructure} i check if everythink is ok in terms of access and then i want to choose right route to call presigned path.

Let's assume for a moment if it works somehow and you're constructing routing (route2) based on the route 1 /first/{key} information then inside your FirstHandler right thing to do is redirect

I cannot use redirect directly because there is auth procedure in middleware and i have no opportunity to get token for that , so i need to avoid calling middlware it's why i do not use direct redirect.

so let's go futher, now i have all info about presigned url and want to call it directly.

If there is no such things and you don't want to implement redirect then why not to directly call SecondHandler inside the FirstHandler? Something like below

It would be a way and it's something what i try to do but: Mux.Vars has been calculated for first path and i would like to recalculate if for new path, but i do not know how. (That's what i met alredy in this approach. if there is a way to recalculate mux.Vars in terms of changed request, please let me know, it would solve my case)

func handler1 (w http.ResponseWriter, req *http.Request){
newPath:= "/second"+ mux.Vars(req)["key"] 
req.URL.Path = newPath

match := mux.RouteMatch{}
isMatched := Router.Match(req, &match)
if isMatched {
 		match.Route.GetHandler().ServeHTTP(w, req) <---- there are mux.Vars for old request, it's not has been recalculating for new request, and it's my problem. 
    }
}

Fyria30 avatar Sep 16 '22 07:09 Fyria30

Guys, any advice? Would be really glad to any advice in this

Fyria30 avatar Sep 21 '22 12:09 Fyria30

If the second handler being called is only ever going to be called from another handler, then why is it a handler in the first place? If my understanding is correct, handler2 is only ever called from handler1 (not seeing the verbatim code makes me draw this assumption). So, if that is the case, why make handler2 an HTTP handler? Why not another function that can be called with the necessary request data parsed from handler1 then given to handler2.

andrewpillar avatar Sep 21 '22 12:09 andrewpillar

Andrewpillar, thank you for reply!

If the second handler being called is only ever going to be called from another handler

The second handler can be called directly (as http handler), or indirectly from another handler.

Assume it's as a service with a lot of routes and the service also has the "God route" which could allow access to any route avoiding auth procedure in middleware

  1. request (/first/{key}) -> middlware -> firstHandler(w http.ResponseWriter, req *http.Request) -> getting {key} -> call needed process function - fine
  2. request (/God/{routeToCall})-> GodHandler(w http.ResponseWriter, req *http.Request) -> getting {routeToCall} -> (assume routeToCall contains "call first request with newKey" ) -> construct new request (/first/newKey) and here i should direclty call firstHandler(w http.ResponseWriter, req *http.Request) and that's what i'm doing with the new request avoiding middleware and then i cannot get {key} from request inside first handler, there is still only {routeToCall} variable. So i try to find right way to do it.

Fyria30 avatar Sep 21 '22 14:09 Fyria30

Assume it's as a service with a lot of routes and the service also has the "God route" which could allow access to any route avoiding auth procedure in middleware

Without wider context it will be hard to give a definitive answer. But based on this alone it sounds like you want to implement some kind of superuser route that will allow a superuser to view the page as another user? If this is the case, then the best approach would be fiddling around with the cookie/session data containing this information to allow for this superuser functionality.

Again, without wider context, or an explicit example that pertains to your code, I don't think anyone could provide a good solution.

andrewpillar avatar Sep 21 '22 16:09 andrewpillar

Without wider context it will be hard to give a definitive answer. But based on this alone it sounds like you want to implement some kind of superuser route that will allow a superuser to view the page as another user?

Something like that, it's presigned url. So when we call "God rout" we put signed {urlParams} where insude are acess rules, time till when it's accessable, the target rout itself and so on. So cookie/sessio not a way i think. The user get presigned url, and then can give it somewhere to make some procedure in service

No way to restruct mux.Vars(req) for new request?

Fyria30 avatar Sep 21 '22 17:09 Fyria30

No way to restruct mux.Vars(req) for new request?

I'm not getting time because of release at my office. I actually wanted to check method mux.BuildVarsFunc before adding any comment here.

amustaque97 avatar Sep 21 '22 18:09 amustaque97

I actually wanted to check method mux.BuildVarsFunc before adding any comment here.

Waiting for that, thank you !

Fyria30 avatar Sep 22 '22 08:09 Fyria30

@Fyria30, thank you so much for waiting so long. I'm afraid I don't think there is any other way to achieve internal redirection. You can follow your current implementation.

Thank you for your patience. Have a nice day ahead.

amustaque97 avatar Oct 02 '22 16:10 amustaque97

@amustaque97 Thank you for your time! But the problem is i have no any implementation now. So BuidVarsFunc can not be used for this task?

Fyria30 avatar Oct 02 '22 19:10 Fyria30

@amustaque97 Thank you for your time! But the problem is i have no any implementation now. So BuidVarsFunc can not be used for this task?

No, I don't think so.

amustaque97 avatar Oct 02 '22 19:10 amustaque97

@amustaque97 Did not find better way but it works fine. So i added two public methods in the mux packet to have opportunity to rebuild request vars.


func requestWithVars(r *http.Request, vars map[string]string) *http.Request {
	ctx := context.WithValue(r.Context(), varsKey, vars)
	return r.WithContext(ctx)
}

func requestWithRoute(r *http.Request, route *Route) *http.Request {
	ctx := context.WithValue(r.Context(), routeKey, route)
	return r.WithContext(ctx)
}

func RequestWithVars(r *http.Request, vars map[string]string) *http.Request {
	return requestWithVars(r, vars)
}
func RequestWithRoute(r *http.Request, route *Route) *http.Request {
	return requestWithRoute(r, route)
}

And then i use it like:

...
func test (router *mux.Router, w http.ResponseWriter, req *http.Request) error{
        requestedkey, _ := mux.Vars(req)["requestedkey"]
	newPath := "/anotherpath/" + requestedkey
	req.URL.Path = newPath
        req.RequestURI = newPath

        var match mux.RouteMatch
	isMatched := router.Match(req, &match)
	if isMatched {
		req = mux.RequestWithVars(req, match.Vars)
		req = mux.RequestWithRoute(req, match.Route)
		match.Route.GetHandler().ServeHTTP(w, req)
		return
	} else {
		return
	}
...

Do not you want MR to make those methods public also? Would it make any sense for you ?

Fyria30 avatar Oct 06 '22 20:10 Fyria30

Hi @Fyria30, sorry for the late reply. Even I implemented similar changes in my local to achieve your requirements. I'm afraid to tell you that this is a rare case and it is not safe to update vars inside the handler. So I would say we should close this issue 🙂

amustaque97 avatar Oct 10 '22 16:10 amustaque97