Endpoints with dynamic paths

Pattern matching for the http package in the Go standard library isn't the most comprehensive and fully featured implementation out there. For example, Ruby on Rails makes it much easier to have dynamic segments inside the path:

"auth/:action/:provider_name"

This then provides a data map (or dictionary) containing the values that the framework automatically extracted from the matched path. So if you visit auth/login/google, then params[:provider_name] would equal google, and params[:action] would equal login.

The most the http package lets us specify by default is a path prefix, which we can do by leaving a trailing slash at the end of the pattern:

"auth/"

We would then have to manually parse the remaining segments to extract the appropriate data. This is acceptable for relatively simple cases, which suits our needs for the time being since we only need to handle a few different paths such as:

  • /auth/login/google
  • /auth/login/facebook
  • /auth/callback/google
  • /auth/callback/facebook

Tip

If you need to handle more advanced routing situations, you might want to consider using dedicated packages such as Goweb, Pat, Routes, or mux. For extremely simple cases such as ours, the built-in capabilities will do.

We are going to create a new handler that powers our login process. In auth.go, add the following loginHandler code:

// loginHandler handles the third-party login process.
// format: /auth/{action}/{provider}
func loginHandler(w http.ResponseWriter, r *http.Request) {
  segs := strings.Split(r.URL.Path, "/")
  action := segs[2]
  provider := segs[3]
  switch action {
  case "login":
    log.Println("TODO handle login for", provider)
  default:
     w.WriteHeader(http.StatusNotFound)
     fmt.Fprintf(w, "Auth action %s not supported", action)
  }
}

In the preceding code, we break the path into segments using strings.Split before pulling out the values for action and provider. If the action value is known, we will run the specific code; otherwise, we will write out an error message and return an http.StatusNotFound status code (which in the language of HTTP status code, is a 404 code).

Note

We will not bullet-proof our code right now but it's worth noticing that if someone hits loginHandler with too few segments, our code will panic because it expects segs[2] and segs[3] to exist.

For extra credit, see whether you can protect against this and return a nice error message instead of a panic if someone hits /auth/nonsense.

Our loginHandler is only a function and not an object that implements the http.Handler interface. This is because, unlike other handlers, we don't need it to store any state. The Go standard library supports this, so we can use the http.HandleFunc function to map it in a way similar to how we used http.Handle earlier. In main.go, update the handlers:

http.Handle("/chat", MustAuth(&templateHandler{filename: "chat.html"}))
http.Handle("/login", &templateHandler{filename: "login.html"})
http.HandleFunc("/auth/", loginHandler)
http.Handle("/room", r)

Rebuild and run the chat application:

go build –o chat
./chat –host=":8080"

Hit the following URLs and notice the output logged in the terminal:

  • http://localhost:8080/auth/login/google outputs TODO handle login for google
  • http://localhost:8080/auth/login/facebook outputs TODO handle login for facebook

We have successfully implemented a dynamic path-matching mechanism that so far just prints out to-do messages; next we need to write code that integrates with the authentication services.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset