Skip to content

Use BindStyledParameterWithLocation for correct path param parsing #777

@acabarbaye

Description

@acabarbaye

Using codegen 1.11.0 for chi-server and this is the output for a path parameter:

	// ------------- Path parameter "jobName" -------------
	var jobNameJobParam

	err = runtime.BindStyledParameter("simple", false, "jobName", chi.URLParam(r, "jobName"), &jobName)
	if err != nil {
		siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "jobName", Err: err})
		return
	}
...

although I would have expected BindStyledParameterWithLocation to be used instead as seen in the corresponding template https://github.com/deepmap/oapi-codegen/blob/d38f1c9e74308ba7950657df952d49f97606355f/pkg/codegen/templates/chi/chi-middleware.tmpl#L33

The parsing is incorrect for parameter using + character for instance

To reproduce:
the following test passes

package api

import (
	"fmt"
	"io"
	"net/http"
	"net/http/httptest"
	"testing"

	"github.com/deepmap/oapi-codegen/pkg/runtime"
	"github.com/go-chi/chi/v5"
	"github.com/stretchr/testify/assert"
)

func sendRequest(t *testing.T, host string, path string) (body string) {
	t.Log("request", path)
	url := fmt.Sprintf("%v%v", host, path)
	r, err := http.NewRequest(http.MethodGet, url, nil)
	if err != nil {
		return
	}
	c := http.DefaultClient
	resp, err := c.Do(r)
	if err != nil {
		return
	}
	defer func() { _ = resp.Body.Close() }()
	b, err := io.ReadAll(resp.Body)
	if err != nil {
		return
	}
	body = string(b)
	return
}

func TestEscapedURLParams(t *testing.T) {

	m := chi.NewRouter()
	m.Get("/api/{identifier}", func(w http.ResponseWriter, r *http.Request) {
		w.WriteHeader(200)
		var identifier string
		_ = runtime.BindStyledParameterWithLocation("simple", false, "identifier", runtime.ParamLocationPath, chi.URLParam(r, "identifier"), &identifier)
		//_ = runtime.BindStyledParameter("simple", false, "identifier", chi.URLParam(r, "identifier"), &identifier)

		_, _ = w.Write([]byte(identifier))
	})

	ts := httptest.NewServer(m)
	defer ts.Close()

	assert.Equal(t, "hello world", sendRequest(t, ts.URL, "/api/hello world"))
	assert.Equal(t, "hello world", sendRequest(t, ts.URL, "/api/hello%20world"))
	assert.Equal(t, "hello world", sendRequest(t, ts.URL, "/api/hello%2520world"))
	assert.Equal(t, "hello/world", sendRequest(t, ts.URL, "/api/hello%2Fworld"))
	assert.Equal(t, "hello+world.hello world", sendRequest(t, ts.URL, "/api/hello+world.hello world"))
}

whereas if swapping runtime.BindStyledParameterWithLocation("simple", false, "identifier", runtime.ParamLocationPath, chi.URLParam(r, "identifier"), &identifier) with runtime.BindStyledParameter("simple", false, "identifier", chi.URLParam(r, "identifier"), &identifier)

We get the following errors

        	Error:      	Not equal: 
        	            	expected: "hello+world.hello world"
        	            	actual  : "hello world.hello world"
        	            	
        	            	Diff:
        	            	--- Expected
        	            	+++ Actual
        	            	@@ -1 +1 @@
        	            	-hello+world.hello world
        	            	+hello world.hello world
        	Test:       	TestEscapedURLParams
--- FAIL: TestEscapedURLParams (0.01s)


Expected :hello+world.hello world
Actual   :hello world.hello world

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions