I have a very simple Go server code setup with mux and when I use curl with GET request params (localhost:8080/suggestions/?locale=en), I get 301 status code (Move permanently). But when there's no get parameters, it's working just fine.
func main() {
router := mux.NewRouter().StrictSlash(true)
router.HandleFunc("/suggestions", handleSuggestions).Methods("GET")
log.Fatal(http.ListenAndServe("localhost:8080", router))
}
Can somebody shed me a light on this.Thanks
go doc mux.StrictSlash states:
func (r *Router) StrictSlash(value bool) *Router
StrictSlash defines the trailing slash behavior for new routes. The initial
value is false.
When true, if the route path is "/path/", accessing "/path" will redirect to
the former and vice versa. In other words, your application will always see
the path as specified in the route.
When false, if the route path is "/path", accessing "/path/" will not match
this route and vice versa.
Special case: when a route sets a path prefix using the PathPrefix() method,
strict slash is ignored for that route because the redirect behavior can't
be determined from a prefix alone. However, any subrouters created from that
route inherit the original StrictSlash setting.
So to avoid the redirects you can either mux.NewRouter().StrictSlash(false) which is equivalent to mux.NewRouter() or use a URL with a trailing slash i.e. router.HandleFunc("/suggestions/", handleSuggestions).Methods("GET")
That's simply because you registered the path /suggestions (note: there is no trailing slash), and you call the URL localhost:8080/suggestions/?locale=en (there is a trailing slash after /suggestions).
You router detects that there's a registered path which would match the requested path without the trailing slash (based on your Router.StrictSlash() policy), so it sends a redirect which when followed would lead you to a valid, registered path.
Simply use a URL without trailing slash after suggestions:
localhost:8080/suggestions?locale=en
Related
Quick question. We have two apps. Ports 3001 and 3002. Our domain is www.domain.com.
What we want to have it once person enters www.domain.com/pathname we want them to be redirected into another app's specific path.
How to do it?
We already came up to this in my nginx
location /pathname/ {
proxy_pass http://127.0.0.1:3002/;
}
It nearly works. However, our app under 3002 works on path /#/pathname.
We can access it by typing www.domain.com/pathname/#/pathname. We want to access same link by typing www.domain.com/pathname.
How to shorten it? What do I miss?
(upd) Just redirect /pathname to /pathname/#/pathname
According to your comment, you want just redirect from /pathname to /pathname/#/pathname
Try these combined directives:
rewrite to append # and fragment identifier
and proxy_pass to reverse proxy to the app.
E.g.:
location /short_path_name/ {
rewrite ^ /pathname/#/$uri permanent;
break;
}
location /pathname/ {
proxy_pass http://127.0.0.1:3002/;
}
And use www.domain.com/short_path_name/ link for your app.
Unfortunately, nginx can't see the fragment identifier
Unfortunately, you can't. Because server never get the fragment identifier from browser.
The fragment identifier functions differently to the rest of the URI: its processing is exclusively client-sided with no participation from the web server
Naming a bit amusing, but it has a long history. See TBL (1997): Fragment Identifiers on URIs:
The URI reference is a thing you build by taking a URI for an information object, adding a "#" sign and then a Fragement identifier. (The last term is historical, so try not to thinl of it necessarily identifying a fragment).
Workarounds
There are workarounds, e.g. encode hashtag symbol into %23 but I'm not sure is it your way.
Handle request arguments with nginx
Note: rewriting url, nginx can preserve request arguments if you add ? at the end of rewrite directive.
See Nginx rewrite manual:
If a replacement string includes the new request arguments, the previous request arguments are appended after them. If this is undesired, putting a question mark at the end of a replacement string avoids having them appended, for example:
rewrite ^/users/(.*)$ /show?user=$1? last;
I'm in the process of upgrading Symfony from 3.4 to 4.3 and I have a situation in which every route is matched with controller and method correctly, but then the request reaches RedirectableCompiledUrlMatcher and replaces correct parameters with
_controller: Symfony\Bundle\FrameworkBundle\Controller\RedirectController::urlRedirectAction
That triggers all sorts of other stuff like invoking param converters, hitting firewalls, and other routing related stuff which it isn't supposed to because the matched route isn't correct.
Debugging 3.4 project continues without replacing the correct parameters.
My question is whether this is now the correct request flow (i.e. every route has to pass urlRedirectAction) and I need to configure other stuff or is there any way I can avoid invoking, I guess, RedirectableCompiledUrlMatcher ?
Is it possible that this happens because RedirectableUrlMatcher is the default matcher for \Symfony\Component\Routing\Router and how come it is the default one? Any chance to replace that with ordinary UrlMatcher like it is in the 3.4?
It's exactly this line vendor/symfony/routing/Matcher/Dumper/CompiledUrlMatcherTrait.php:63 where I have $ret matched correctly to my controller and $this->redirect() is being called which replaces my controller with Symfony RedirectController.
Trait is part of RedirectableCompiledUrlMatcher class
Symfony 4 changed the routing so that routes with a trailing slash are considered equivalent to routes without one for GET and HEAD requests (cf. https://symfony.com/doc/4.3/routing.html#redirecting-urls-with-trailing-slashes).
If you have route definitions for both versions, the first one will be matched. The RedirectableUrlMatcherInterface will create a redirect to this route if you use it the other way.
Example 1
# routes.yaml
foo:
path: /foo
controller: App\Controller\FooController::fooAction
foo_trail:
path: /foo/
controller: App\Controller\FooController::fooAction
GET /foo/ will redirect to GET /foo, GET /foo will match normally.
Example 2
# routes.yaml
foo_trail:
path: /foo/
controller: App\Controller\FooController::fooAction
foo:
path: /foo
controller: App\Controller\FooController::fooAction
GET /foo will redirect to GET /foo/, GET /foo/ will match normally.
This redirect for the missing/additional trailing slash is what line 63 in CompiledUrlMatcherTrait does (i.e. GET /foo/ in example 1). If the route can be matched exactly (i.e. GET /foo in example 1), this redirect should not be reached and the matcher should return in line 39.
So for your specific routing configuration, the questions are:
Are you relying on /foo and /foo/ being different? That won't be possible with the default matcher anymore (for GET or HEAD requests).
Are you requesting /foo with /foo/ or vice versa? This shouldn't be a problem, but causes a redirect that might cause problems somewhere else.
If your problem remains, please provide a relevant excerpt of your routing config with an example of requested and redirected URL.
I have a rewrite rule that looks like the following
rewrite ^/([a-zA-Z0-9_]+)$ /mysite/#!/$1/login;
The idea is that a shortcode like
/foo
gets redirected to
/mysite/#!/foo/login
However nginx is redirecting to:
/mysite/%23!/foo/login
How do I prevent the URL encoding from happening in the rewrite?
I can reproduce this issue by using a reverse proxy.
Nginx is actually doing the right thing, as # is a reserved character for URIs and identifies the start of the fragment identifier.
The fragment identifier is for the browser's use only and is not usually received by the server in the requested URL. I am not sure how your Tomcat server is receiving requests containing a naked # in the first place.
I'm using http.FileServer in Go to serve some static file into a directory.
This is how I map it using mux as a router:
r.PathPrefix("/download").Handler(http.StripPrefix("/download", http.FileServer(http.Dir(dirPath)))).Methods("GET")
where dirPath is an absolute path of a directory in my file system.
Now it seems to work fine when asking the directory listing with localhost:8080/download, because it returns a page like this
<pre>
a.xml
b.zip
</pre>
Unfortunately the links are broken because I expect them to be mapped, for example to localhost:8080/download/a.xml , while file server maps them to localhost:8080/a.xml.
How can I make my directory listing keep the /download path prefix in links?
The problem is the pattern you register you handler with: "/download".
There are 2 problems with it:
The generated URLs are wrong because the handler returned by the http.FileServer() function generates relative URLs to files and subfolders; relative to the root folder passed to http.FileServer(), and if your page is available under the path /download, a relative URL like href="a.xml" will be resolved to /a.xml, and not to /download/a.xml.
Even if the URLs would be good, the files would not be served as the requests would not get routed to your handled (to the file server handler). You must add a trailing slash, as "/download" only matches this single path, and not all paths starting with it. Add a trailing slash: "/download/" and it will match the rooted subtree /download/*.
So the solution is:
r.PathPrefix("/download/").Handler(
http.StripPrefix("/download", http.FileServer(http.Dir(dirPath))),
).Methods("GET")
This is documented at http.ServeMux:
Patterns name fixed, rooted paths, like "/favicon.ico", or rooted subtrees, like "/images/" (note the trailing slash).
Note that even though we're now using the "/download/" registered path, users are not required to type the trailing slash in the browser, as leaving that out the server will send a redirect to the path that ends with a trailing slash. This will happen automatically. This is also documented at http.ServeMux:
If a subtree has been registered and a request is received naming the subtree root without its trailing slash, ServeMux redirects that request to the subtree root (adding the trailing slash). This behavior can be overridden with a separate registration for the path without the trailing slash. For example, registering "/images/" causes ServeMux to redirect a request for "/images" to "/images/", unless "/images" has been registered separately.
Read related question: Go web server is automatically redirecting POST requests
Here's a simple file server app using only the standard library:
http.Handle("/dl/",
http.StripPrefix("/dl", http.FileServer(http.Dir("/home/bob/Downloads"))),
)
panic(http.ListenAndServe("localhost:8080", nil))
I've been trying to solve a weird problem for quite some time now. After stepping through lots of angular code, I noticed something weird while logging requests to my server through Charles.
When I post to a url /myurl the request never actually hits my server. Instead, it gets a 301 response and THEN a GET request hite my server.
This is incredibly puzzling. Has anyone else run into this problem? I've uploaded a screenshot of my Charles log incase you are interested.
Just as a reference, this is what my server looks like:
type FormStruct struct {
Test string
}
func PHandler(w http.ResponseWriter, r *http.Request) {
var t FormStruct
req, _ := httputil.DumpRequest(r, true)
log.Println(string(req))
log.Println(r.Method) // GET
log.Println(r.Body)
decoder := json.NewDecoder(r.Body)
err := decoder.Decode(&t)
log.Println("Decoding complete")
if err != nil {
log.Println("Error")
panic(err.Error()+"\n\n")
}
log.Println(t.Test)
w.Write([]byte("Upload complete, no errors"))
}
func main() {
http.HandleFunc("/myurl/", PHandler)
fmt.Println("Go Server listening on port 8001")
http.ListenAndServe(":8001", nil)
}
The explanation is simple: because you used the "/myurl/" path when you registered your PHandler (note the trailing slash /!) but you directed your browser to /myurl (note there is no trailing slash). And by default the http package implementation will perform (send back) a redirect request so if the browser follows it (it will), the new URL will match the registered path.
This is documented at type http.ServeMux:
If a subtree has been registered and a request is received naming the subtree root without its trailing slash, ServeMux redirects that request to the subtree root (adding the trailing slash). This behavior can be overridden with a separate registration for the path without the trailing slash. For example, registering "/images/" causes ServeMux to redirect a request for "/images" to "/images/", unless "/images" has been registered separately.
Should you direct your browser directly to /myurl/, you would not experience a redirect.
Or if you don't need to handle a rooted subtree but only a single path (e.g. /myurl), then register your handler only to this single path:
http.HandleFunc("/myurl", PHandler)
And then of course direct your browser to /myurl, and you will not experience any redirect either.
...Or as the documentation suggests: register both paths to your handler if you really need it:
http.HandleFunc("/myurl", PHandler)
http.HandleFunc("/myurl/", PHandler)
And now no matter which path you call (/myurl or /myurl/), both will result in calling your handler without any redirection taking place.
Notes:
In your situation when a redirect was sent back to the browser, the browser will not repeat the POST request (but rather just a "simple" GET request).
Generally speaking a browser will not send POST data to a redirect URL because the browser is not qualified to decide if you're willing to send the same data to the new URL what you intended to send to the original URL (think about passwords, credit card numbers and other sensitive data). But don't try to circumvent it, simply use registered path of your handler to POST to, or any of the other tips mentioned above.
You can read more on the subject here:
Why doesn't HTTP have POST redirect?