Go: Get path parameters from http.Request
Asked Answered
S

6

61

I'm developing a REST API with Go, but I don't know how can I do the path mappings and retrieve the path parameters from them.

I want something like this:

func main() {
    http.HandleFunc("/provisions/:id", Provisions) //<-- How can I map "id" parameter in the path?
    http.ListenAndServe(":8080", nil)
}

func Provisions(w http.ResponseWriter, r *http.Request) {
    //I want to retrieve here "id" parameter from request
}

I would like to use just http package instead of web frameworks, if it is possible.

Thanks.

Stalag answered 16/12, 2015 at 14:45 Comment(0)
Z
16

If you are using Go 1.22, you can easily retrieve the path parameters with r.PathValue(). For example:

package main

import (
    "fmt"
    "net/http"
    "net/http/httptest"
)

func main() {
    mux := http.NewServeMux()
    mux.HandleFunc("/provisions/{id}", func(w http.ResponseWriter, r *http.Request) {
        fmt.Println("Provision ID: ", r.PathValue("id"))
    })

    mux.ServeHTTP(httptest.NewRecorder(), httptest.NewRequest("GET", "/provisions/123", nil))
    mux.ServeHTTP(httptest.NewRecorder(), httptest.NewRequest("GET", "/provisions/456", nil))
}

Demo in Go Playground. Source: https://pkg.go.dev/net/http#Request.PathValue

Zilber answered 9/3 at 0:2 Comment(0)
B
79

If you don't want to use any of the multitude of the available routing packages, then you need to parse the path yourself:

Route the /provisions path to your handler

http.HandleFunc("/provisions/", Provisions)

Then split up the path as needed in the handler

id := strings.TrimPrefix(req.URL.Path, "/provisions/")
// or use strings.Split, or use regexp, etc.
Betjeman answered 16/12, 2015 at 14:55 Comment(2)
Make sure to not forget to add a trailing / after /provisionsMair
You can do this more idiomatically using the native http.StripPrefix, which transforms a handler directly. See answer below.Ureide
W
22

You can use golang gorilla/mux package's router to do the path mappings and retrieve the path parameters from them.

import (
    "fmt"
    "github.com/gorilla/mux"
    "net/http"
)

func main() {
    r := mux.NewRouter()
    r.HandleFunc("/provisions/{id}", Provisions)
    http.ListenAndServe(":8080", r)
}

func Provisions(w http.ResponseWriter, r *http.Request) {
    vars := mux.Vars(r)
    id, ok := vars["id"]
    if !ok {
        fmt.Println("id is missing in parameters")
    }
    fmt.Println(`id := `, id)
    //call http://localhost:8080/provisions/someId in your browser
    //Output : id := someId
}
Weinman answered 6/5, 2021 at 5:9 Comment(1)
Go 1.22 has implemented this natively in net/http, negating the need for gorilla/mux for this use case. See my answer below #34315475Autonomic
Z
16

If you are using Go 1.22, you can easily retrieve the path parameters with r.PathValue(). For example:

package main

import (
    "fmt"
    "net/http"
    "net/http/httptest"
)

func main() {
    mux := http.NewServeMux()
    mux.HandleFunc("/provisions/{id}", func(w http.ResponseWriter, r *http.Request) {
        fmt.Println("Provision ID: ", r.PathValue("id"))
    })

    mux.ServeHTTP(httptest.NewRecorder(), httptest.NewRequest("GET", "/provisions/123", nil))
    mux.ServeHTTP(httptest.NewRecorder(), httptest.NewRequest("GET", "/provisions/456", nil))
}

Demo in Go Playground. Source: https://pkg.go.dev/net/http#Request.PathValue

Zilber answered 9/3 at 0:2 Comment(0)
A
6

Go 1.22 has now introduced this functionality natively in net/http

This can now be achieved by calling req.PathValue()

PathValue returns the value for the named path wildcard in the ServeMux pattern that matched the request. It returns the empty string if the request was not matched against a pattern or there is no such wildcard in the pattern.


A basic example on how to use reqPathValue() is:

package main

import (
    "fmt"
    "net/http"
)

func main() {
    mux := http.NewServeMux()

    mux.HandleFunc("/provisions/{id}", func(w http.ResponseWriter, req *http.Request) {
        idString := req.PathValue("id")
        fmt.Printf("ID: %v", idString)
    })

    log.Fatal(http.ListenAndServe(":8080", mux))
}
Autonomic answered 13/3 at 0:12 Comment(0)
C
3

Golang read value from URL query "/template/get/123".

I used standard gin routing and handling request parameters from context parameters.

Use this in registering your endpoint:

func main() {
    r := gin.Default()
    g := r.Group("/api")
    {
        g.GET("/template/get/:Id", templates.TemplateGetIdHandler)
    }
    r.Run()
}

And use this function in handler

func TemplateGetIdHandler(c *gin.Context) {
    req := getParam(c, "Id")
    //your stuff
}

func getParam(c *gin.Context, paramName string) string {
    return c.Params.ByName(paramName)
}

Golang read value from URL query "/template/get?id=123".

Use this in registering your endpoint:

func main() {
    r := gin.Default()
    g := r.Group("/api")
    {
        g.GET("/template/get", templates.TemplateGetIdHandler)
    }
    r.Run()
}

And use this function in handler

type TemplateRequest struct {
    Id string `form:"id"`
}

func TemplateGetIdHandler(c *gin.Context) {
    var request TemplateRequest
    err := c.Bind(&request)
    if err != nil {
        log.Println(err.Error())
        c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
    }
    //your stuff
}
Chaetognath answered 30/7, 2021 at 7:21 Comment(1)
Can you add the imports? For example, gin would be in "github.com/gin-gonic/gin" but templates?Daphnedaphnis
U
3

You can do this with standard library handlers. Note http.StripPrefix accepts an http.Handler and returns a new one:

func main() {
  mux := http.NewServeMux()
  provisionsPath := "/provisions/"
  mux.Handle(
    provisionsPath,
    http.StripPrefix(provisionsPath, http.HandlerFunc(Provisions)),
  )
}

func Provisions(w http.ResponseWriter, r *http.Request) {
  fmt.Println("Provision ID:", r.URL.Path)
}

See working demo on Go playground.

You can also nest this behavior using submuxes; http.ServeMux implements http.Handler, so you can pass one into http.StripPrefix just the same.

Ureide answered 15/5, 2023 at 17:16 Comment(1)
Will still match /provisions/123/fooSanchez

© 2022 - 2024 — McMap. All rights reserved.