diff --git a/resolver.go b/resolver.go index fe91a7e7..d10f551a 100644 --- a/resolver.go +++ b/resolver.go @@ -271,7 +271,14 @@ func setFields(ctx *hcontext, req *http.Request, input reflect.Value, t reflect. if name, ok := f.Tag.Lookup(locationPath); ok { pname = name location = locationPath - if v := chi.URLParam(req, name); v != "" { + v := chi.URLParam(req, name) + if v == "" { + ctx.AddError(&ErrorDetail{ + Message: fmt.Sprintf("%s is required", name), + Location: location + "." + name, + Value: v, + }) + } else { pv = v } } diff --git a/resolver_test.go b/resolver_test.go index ce6088da..95f960a1 100644 --- a/resolver_test.go +++ b/resolver_test.go @@ -360,3 +360,24 @@ func TestRawBody(t *testing.T) { assert.Equal(t, http.StatusUnprocessableEntity, w.Result().StatusCode) } + +type PathParamTestModel struct { + TestParam string `path:"test-id"` +} + +func TestPathParamAlwaysRequired(t *testing.T) { + app := newTestRouter() + + app.Resource("/test/{test-id}/foo").Get("test", "Test", + NewResponse(http.StatusOK, "desc"), + ).Run(func(ctx Context, input PathParamTestModel) { + ctx.WriteHeader(http.StatusOK) + }) + + w := httptest.NewRecorder() + r, _ := http.NewRequest(http.MethodGet, "/test//foo", nil) + app.ServeHTTP(w, r) + + assert.Equal(t, http.StatusBadRequest, w.Result().StatusCode) + assert.Contains(t, w.Body.String(), "test-id is required") +}