Repository: swaggest/rest
Pull request #54 (2021-12-24 18:27:50)

Fix gotip tests and benchmarks

github-actions[bot]: ### Lines Of Code

| Language | Files |  Lines   |  Code   | Comments | Blanks | Complexity |    Bytes     |
|----------|-------|----------|---------|----------|--------|------------|--------------|
| Makefile | 3     | 124 (-1) | 91 (-1) | 10       | 23     | 11 (-1)    | 3.8K (-111B) |
<!-- Sticky Pull Request CommentLOC -->
github-actions[bot]: ### Unit Test Coverage
total:	(statements)	82.4%
<details><summary>Coverage diff with base branch</summary>

```diff

```
</details>
<!-- Sticky Pull Request Commentunit-test -->
github-actions[bot]: ### Go API Changes

<pre>
# summary
Inferred base version: v0.2.17
Suggested version: v0.2.18
</pre>
<!-- Sticky Pull Request Commentgorelease -->
codecov[bot]: # [Codecov](https://codecov.io/gh/swaggest/rest/pull/54?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=swaggest) Report
> Merging [#54](https://codecov.io/gh/swaggest/rest/pull/54?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=swaggest) (ee834e4) into [master](https://codecov.io/gh/swaggest/rest/commit/cdbab60cf7eed11ad5cd33fff88585028b0ca68c?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=swaggest) (cdbab60) will **not change** coverage.
> The diff coverage is `n/a`.

[![Impacted file tree graph](https://codecov.io/gh/swaggest/rest/pull/54/graphs/tree.svg?width=650&height=150&src=pr&token=OpLHxxPdmn&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=swaggest)](https://codecov.io/gh/swaggest/rest/pull/54?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=swaggest)

```diff
@@           Coverage Diff           @@
##           master      #54   +/-   ##
=======================================
  Coverage   74.65%   74.65%           
=======================================
  Files          26       26           
  Lines        1602     1602           
=======================================
  Hits         1196     1196           
  Misses        253      253           
  Partials      153      153           
```

| Flag | Coverage Δ | |
|---|---|---|
| unittests | `74.65% <ø> (ø)` | |

Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=swaggest#carryforward-flags-in-the-pull-request-comment) to find out more.


------

[Continue to review full report at Codecov](https://codecov.io/gh/swaggest/rest/pull/54?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=swaggest).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=swaggest)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/swaggest/rest/pull/54?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=swaggest). Last update [cdbab60...ee834e4](https://codecov.io/gh/swaggest/rest/pull/54?src=pr&el=lastupdated&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=swaggest). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=swaggest).

github-actions[bot]: ### Benchmark Result
<details><summary>Benchmark diff with base branch</summary>

```
name                                    old time/op    new time/op    delta
pkg:github.com/swaggest/rest/jsonschema goos:linux goarch:amd64
RequestValidator_ValidateRequestData-2    1.35µs ± 5%    1.35µs ± 3%    ~     (p=0.421 n=5+5)
pkg:github.com/swaggest/rest/request goos:linux goarch:amd64
Decoder_Decode-2                           690ns ± 0%     700ns ± 5%    ~     (p=0.421 n=5+5)
DecoderFunc_Decode-2                      2.04µs ± 4%    1.98µs ± 1%  -2.66%  (p=0.016 n=5+5)
Decoder_Decode_json-2                     22.3µs ± 1%    22.3µs ± 1%    ~     (p=0.841 n=5+5)
Decoder_Decode_queryObject-2              4.79µs ± 1%    4.77µs ± 0%    ~     (p=0.063 n=5+5)
Decoder_Decode_jsonParam-2                1.79µs ± 0%    1.79µs ± 0%  -0.43%  (p=0.008 n=5+5)
DecoderFactory_SetDecoderFunc-2           1.66µs ± 1%    1.65µs ± 1%    ~     (p=0.056 n=5+5)
pkg:github.com/swaggest/rest/response/gzip goos:linux goarch:amd64
Middleware-2                              11.2µs ± 1%    11.2µs ± 1%    ~     (p=0.690 n=5+5)
Middleware_control-2                      3.25µs ± 3%    3.26µs ± 2%    ~     (p=0.548 n=5+5)

name                                    old alloc/op   new alloc/op   delta
pkg:github.com/swaggest/rest/jsonschema goos:linux goarch:amd64
RequestValidator_ValidateRequestData-2    2.46kB ± 0%    2.46kB ± 0%    ~     (all equal)
pkg:github.com/swaggest/rest/request goos:linux goarch:amd64
Decoder_Decode-2                            440B ± 0%      440B ± 0%    ~     (all equal)
DecoderFunc_Decode-2                      1.51kB ± 0%    1.51kB ± 0%    ~     (all equal)
Decoder_Decode_json-2                     12.2kB ± 0%    12.2kB ± 0%    ~     (all equal)
Decoder_Decode_queryObject-2              2.00kB ± 0%    2.00kB ± 0%    ~     (all equal)
Decoder_Decode_jsonParam-2                  736B ± 0%      736B ± 0%    ~     (all equal)
DecoderFactory_SetDecoderFunc-2           1.02kB ± 0%    1.02kB ± 0%    ~     (all equal)
pkg:github.com/swaggest/rest/response/gzip goos:linux goarch:amd64
Middleware-2                              1.17kB ± 5%    1.16kB ± 2%    ~     (p=0.722 n=5+5)
Middleware_control-2                      11.2kB ± 0%    11.2kB ± 0%    ~     (all equal)

name                                    old allocs/op  new allocs/op  delta
pkg:github.com/swaggest/rest/jsonschema goos:linux goarch:amd64
RequestValidator_ValidateRequestData-2      8.00 ± 0%      8.00 ± 0%    ~     (all equal)
pkg:github.com/swaggest/rest/request goos:linux goarch:amd64
Decoder_Decode-2                            4.00 ± 0%      4.00 ± 0%    ~     (all equal)
DecoderFunc_Decode-2                        12.0 ± 0%      12.0 ± 0%    ~     (all equal)
Decoder_Decode_json-2                        177 ± 0%       177 ± 0%    ~     (all equal)
Decoder_Decode_queryObject-2                36.0 ± 0%      36.0 ± 0%    ~     (all equal)
Decoder_Decode_jsonParam-2                  13.0 ± 0%      13.0 ± 0%    ~     (all equal)
DecoderFactory_SetDecoderFunc-2             16.0 ± 0%      16.0 ± 0%    ~     (all equal)
pkg:github.com/swaggest/rest/response/gzip goos:linux goarch:amd64
Middleware-2                                11.0 ± 0%      11.0 ± 0%    ~     (all equal)
Middleware_control-2                        9.00 ± 0%      9.00 ± 0%    ~     (all equal)
```
</details>

<details><summary>Benchmark result</summary>

```
name                                    time/op
pkg:github.com/swaggest/rest/jsonschema goos:linux goarch:amd64
RequestValidator_ValidateRequestData-2  1.35µs ± 3%
pkg:github.com/swaggest/rest/request goos:linux goarch:amd64
Decoder_Decode-2                         700ns ± 5%
DecoderFunc_Decode-2                    1.98µs ± 1%
Decoder_Decode_json-2                   22.3µs ± 1%
Decoder_Decode_queryObject-2            4.77µs ± 0%
Decoder_Decode_jsonParam-2              1.79µs ± 0%
DecoderFactory_SetDecoderFunc-2         1.65µs ± 1%
pkg:github.com/swaggest/rest/response/gzip goos:linux goarch:amd64
Middleware-2                            11.2µs ± 1%
Middleware_control-2                    3.26µs ± 2%

name                                    alloc/op
pkg:github.com/swaggest/rest/jsonschema goos:linux goarch:amd64
RequestValidator_ValidateRequestData-2  2.46kB ± 0%
pkg:github.com/swaggest/rest/request goos:linux goarch:amd64
Decoder_Decode-2                          440B ± 0%
DecoderFunc_Decode-2                    1.51kB ± 0%
Decoder_Decode_json-2                   12.2kB ± 0%
Decoder_Decode_queryObject-2            2.00kB ± 0%
Decoder_Decode_jsonParam-2                736B ± 0%
DecoderFactory_SetDecoderFunc-2         1.02kB ± 0%
pkg:github.com/swaggest/rest/response/gzip goos:linux goarch:amd64
Middleware-2                            1.16kB ± 2%
Middleware_control-2                    11.2kB ± 0%

name                                    allocs/op
pkg:github.com/swaggest/rest/jsonschema goos:linux goarch:amd64
RequestValidator_ValidateRequestData-2    8.00 ± 0%
pkg:github.com/swaggest/rest/request goos:linux goarch:amd64
Decoder_Decode-2                          4.00 ± 0%
DecoderFunc_Decode-2                      12.0 ± 0%
Decoder_Decode_json-2                      177 ± 0%
Decoder_Decode_queryObject-2              36.0 ± 0%
Decoder_Decode_jsonParam-2                13.0 ± 0%
DecoderFactory_SetDecoderFunc-2           16.0 ± 0%
pkg:github.com/swaggest/rest/response/gzip goos:linux goarch:amd64
Middleware-2                              11.0 ± 0%
Middleware_control-2                      9.00 ± 0%
```
</details>
<!-- Sticky Pull Request Commentbench -->
github-actions[bot]: ### Examples Benchmark Result
<details><summary>Benchmark diff with base branch</summary>

```
name                       time/op
pkg:github.com/swaggest/rest/_examples/advanced goos:linux goarch:amd64
_directGzip-2              24.4µs ± 9%
_directGzipHead-2          23.9µs ± 5%
_noDirectGzip-2             133µs ± 1%
_directGzip_decode-2        431µs ± 1%
_noDirectGzip_decode-2      133µs ± 0%
_jsonBody-2                42.1µs ± 1%
_jsonBodyValidation-2      48.2µs ± 1%
_outputHeaders-2           23.2µs ± 3%
_requestResponseMapping-2  41.1µs ± 1%
_validation-2              44.5µs ± 1%
_noValidation-2            29.5µs ± 3%
pkg:github.com/swaggest/rest/_examples/task-api/internal/infra/nethttp goos:linux goarch:amd64
_notFoundSrv-2             26.6µs ± 1%
_ok-2                      27.5µs ± 1%
_invalidBody-2             39.0µs ± 2%

name                       B:rcvd/op
pkg:github.com/swaggest/rest/_examples/advanced goos:linux goarch:amd64
_directGzip-2                 639 ± 0%
_directGzipHead-2             183 ± 0%
_noDirectGzip-2             1.04k ± 0%
_directGzip_decode-2          639 ± 0%
_noDirectGzip_decode-2      1.04k ± 0%
_jsonBody-2                   208 ± 0%
_jsonBodyValidation-2         194 ± 0%
_outputHeaders-2              155 ± 0%
_requestResponseMapping-2    94.0 ± 0%
_validation-2                 177 ± 0%
_noValidation-2               177 ± 0%
pkg:github.com/swaggest/rest/_examples/task-api/internal/infra/nethttp goos:linux goarch:amd64
_notFoundSrv-2                337 ± 0%
_ok-2                         359 ± 0%
_invalidBody-2                435 ± 0%

name                       B:sent/op
pkg:github.com/swaggest/rest/_examples/advanced goos:linux goarch:amd64
_directGzip-2                 103 ± 0%
_directGzipHead-2             104 ± 0%
_noDirectGzip-2               117 ± 0%
_directGzip_decode-2          116 ± 0%
_noDirectGzip_decode-2        130 ± 0%
_jsonBody-2                   188 ± 0%
_jsonBodyValidation-2         192 ± 0%
_outputHeaders-2             77.0 ± 0%
_requestResponseMapping-2     169 ± 0%
_validation-2                 170 ± 0%
_noValidation-2               173 ± 0%
pkg:github.com/swaggest/rest/_examples/task-api/internal/infra/nethttp goos:linux goarch:amd64
_notFoundSrv-2               74.0 ± 0%
_ok-2                        74.0 ± 0%
_invalidBody-2                137 ± 0%

name                       rps
pkg:github.com/swaggest/rest/_examples/advanced goos:linux goarch:amd64
_directGzip-2               41.1k ± 9%
_directGzipHead-2           41.8k ± 5%
_noDirectGzip-2             7.52k ± 1%
_directGzip_decode-2        2.32k ± 1%
_noDirectGzip_decode-2      7.54k ± 0%
_jsonBody-2                 23.8k ± 1%
_jsonBodyValidation-2       20.8k ± 1%
_outputHeaders-2            43.1k ± 3%
_requestResponseMapping-2   24.3k ± 1%
_validation-2               22.5k ± 1%
_noValidation-2             33.9k ± 3%
pkg:github.com/swaggest/rest/_examples/task-api/internal/infra/nethttp goos:linux goarch:amd64
_notFoundSrv-2              37.6k ± 1%
_ok-2                       36.4k ± 1%
_invalidBody-2              25.7k ± 2%

name                       alloc/op
pkg:github.com/swaggest/rest/_examples/advanced goos:linux goarch:amd64
_directGzip-2              3.91kB ± 0%
_directGzipHead-2          3.90kB ± 0%
_noDirectGzip-2            6.10kB ±16%
_directGzip_decode-2        402kB ± 0%
_noDirectGzip_decode-2     5.33kB ± 3%
_jsonBody-2                13.1kB ± 0%
_jsonBodyValidation-2      18.9kB ± 0%
_outputHeaders-2           3.68kB ± 0%
_requestResponseMapping-2  16.6kB ± 0%
_validation-2              16.5kB ± 0%
_noValidation-2            6.08kB ± 0%
pkg:github.com/swaggest/rest/_examples/task-api/internal/infra/nethttp goos:linux goarch:amd64
_notFoundSrv-2             4.86kB ± 0%
_ok-2                      4.76kB ± 0%
_invalidBody-2             8.65kB ± 0%

name                       allocs/op
pkg:github.com/swaggest/rest/_examples/advanced goos:linux goarch:amd64
_directGzip-2                42.0 ± 0%
_directGzipHead-2            42.0 ± 0%
_noDirectGzip-2              49.4 ± 3%
_directGzip_decode-2          500 ± 0%
_noDirectGzip_decode-2       50.0 ± 0%
_jsonBody-2                   127 ± 0%
_jsonBodyValidation-2         183 ± 0%
_outputHeaders-2             37.0 ± 0%
_requestResponseMapping-2     122 ± 0%
_validation-2                 152 ± 0%
_noValidation-2              72.0 ± 0%
pkg:github.com/swaggest/rest/_examples/task-api/internal/infra/nethttp goos:linux goarch:amd64
_notFoundSrv-2               54.0 ± 0%
_ok-2                        50.0 ± 0%
_invalidBody-2                101 ± 0%
```
</details>
<!-- Sticky Pull Request Commentbench-examples -->

Patch:
diff --git a/.github/workflows/bench-examples.yml b/.github/workflows/bench-examples.yml
index 2adb3ef..9e37f5e 100644
--- a/.github/workflows/bench-examples.yml
+++ b/.github/workflows/bench-examples.yml
@@ -76,7 +76,10 @@ jobs:
         run: |
           export REF_NAME=master
           cd __base
-          BENCH_COUNT=5 make bench-run-examples bench-stat-examples
+          BENCH_COUNT=5 make bench-run-examples
+          ls -lah
+          pwd
+          cat bench-examples-master.txt
           cp bench-examples-master.txt ../bench-examples-master.txt
       - name: Benchmark
         id: bench
@@ -91,7 +94,6 @@ jobs:
         continue-on-error: true
         uses: marocchino/sticky-pull-request-comment@v2
         with:
-          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
           header: bench-examples
           message: |
             ### Examples Benchmark Result
diff --git a/Makefile b/Makefile
index 9021d1d..32096eb 100644
--- a/Makefile
+++ b/Makefile
@@ -42,13 +42,12 @@ REF_NAME ?= $(shell git symbolic-ref HEAD --short | tr / - 2>/dev/null)
 test: test-unit test-examples
 
 test-examples:
-	cd _examples && go test -race ./...
+	cd _examples && $(GO) test -race ./...
 
 ## Run benchmark for app examples, iterations count controlled by BENCH_COUNT, default 5.
 bench-run-examples:
-	@cd _examples && $(GO) test -bench=. -count=$(BENCH_COUNT) -run=^a  ./... >../bench-examples-$(REF_NAME).txt
+	@cd _examples && set -o pipefail && $(GO) test -bench=. -count=$(BENCH_COUNT) -run=^a  ./... | tee ../bench-examples-$(REF_NAME).txt
 
 ## Show result of benchmark for app examples.
-bench-stat-examples:
-	@test -s $(GOPATH)/bin/benchstat || GO111MODULE=off GOFLAGS= GOBIN=$(GOPATH)/bin $(GO) get -u golang.org/x/perf/cmd/benchstat
-	@test -s bench-examples-master.txt && benchstat bench-examples-master.txt bench-examples-$(REF_NAME).txt || benchstat bench-examples-$(REF_NAME).txt
\ No newline at end of file
+bench-stat-examples: bench-stat-cli
+	@test -s bench-examples-master.txt && benchstat bench-examples-master.txt bench-examples-$(REF_NAME).txt || benchstat bench-examples-$(REF_NAME).txt
diff --git a/_examples/.golangci.yml b/_examples/.golangci.yml
new file mode 100644
index 0000000..97710e1
--- /dev/null
+++ b/_examples/.golangci.yml
@@ -0,0 +1,52 @@
+# See https://github.com/golangci/golangci-lint/blob/master/.golangci.example.yml
+run:
+  tests: true
+
+linters-settings:
+  errcheck:
+    check-type-assertions: true
+    check-blank: true
+  gocyclo:
+    min-complexity: 20
+  dupl:
+    threshold: 100
+  misspell:
+    locale: US
+  unused:
+    check-exported: false
+  unparam:
+    check-exported: true
+
+linters:
+  enable-all: true
+  disable:
+    - lll
+    - maligned
+    - gochecknoglobals
+    - gomnd
+    - wrapcheck
+    - paralleltest
+    - forbidigo
+    - exhaustivestruct
+    - interfacer # deprecated
+    - forcetypeassert
+    - scopelint # deprecated
+    - ifshort # too many false positives
+    - golint # deprecated
+    - varnamelen
+    - tagliatelle
+    - errname
+    - ireturn
+
+issues:
+  exclude-use-default: false
+  exclude-rules:
+    - linters:
+        - gomnd
+        - goconst
+        - goerr113
+        - noctx
+        - funlen
+        - dupl
+      path: "_test.go"
+
diff --git a/_examples/Makefile b/_examples/Makefile
index 80ead41..4b37b06 100644
--- a/_examples/Makefile
+++ b/_examples/Makefile
@@ -1,3 +1,5 @@
+#GOLANGCI_LINT_VERSION := "v1.43.0" # Optional configuration to pinpoint golangci-lint version.
+
 # The head of Makefile determines location of dev-go to include standard targets.
 GO ?= go
 export GO111MODULE = on
@@ -21,16 +23,17 @@ ifeq ($(DEVGO_PATH),)
 	DEVGO_PATH := $(shell GO111MODULE=on $(GO) list ${modVendor} -f '{{.Dir}}' -m github.com/bool64/dev)
 	ifeq ($(DEVGO_PATH),)
     	$(info Module github.com/bool64/dev not found, downloading.)
-    	DEVGO_PATH := $(shell export GO111MODULE=on && $(GO) mod tidy && $(GO) list -f '{{.Dir}}' -m github.com/bool64/dev)
+    	DEVGO_PATH := $(shell export GO111MODULE=on && $(GO) get github.com/bool64/dev && $(GO) list -f '{{.Dir}}' -m github.com/bool64/dev)
 	endif
 endif
 
 -include $(DEVGO_PATH)/makefiles/main.mk
--include $(DEVGO_PATH)/makefiles/test-unit.mk
 -include $(DEVGO_PATH)/makefiles/lint.mk
+-include $(DEVGO_PATH)/makefiles/test-unit.mk
+-include $(DEVGO_PATH)/makefiles/bench.mk
+-include $(DEVGO_PATH)/makefiles/reset-ci.mk
+
+# Add your custom targets here.
 
 ## Run tests
 test: test-unit
-
-test-examples:
-	cd _examples && go test -race ./...
diff --git a/_examples/advanced-generic/no_validation.go b/_examples/advanced-generic/no_validation.go
new file mode 100644
index 0000000..483e49d
--- /dev/null
+++ b/_examples/advanced-generic/no_validation.go
@@ -0,0 +1,41 @@
+//go:build go1.18
+// +build go1.18
+
+package main
+
+import (
+	"context"
+
+	"github.com/swaggest/usecase"
+)
+
+func noValidation() usecase.Interactor {
+	type inputPort struct {
+		Header int  `header:"X-Input"`
+		Query  bool `query:"q"`
+		Data   struct {
+			Value string `json:"value"`
+		} `json:"data"`
+	}
+
+	type outputPort struct {
+		Header        int  `header:"X-Output" json:"-"`
+		AnotherHeader bool `header:"X-Query" json:"-"`
+		Data          struct {
+			Value string `json:"value"`
+		} `json:"data"`
+	}
+
+	u := usecase.NewInteractor(func(ctx context.Context, in inputPort, out *outputPort) (err error) {
+		out.Header = in.Header
+		out.AnotherHeader = in.Query
+		out.Data.Value = in.Data.Value
+
+		return nil
+	})
+
+	u.SetTitle("No Validation")
+	u.SetDescription("Input/Output without validation.")
+
+	return u
+}
diff --git a/_examples/advanced-generic/output_headers.go b/_examples/advanced-generic/output_headers.go
index 92280d0..b259cba 100644
--- a/_examples/advanced-generic/output_headers.go
+++ b/_examples/advanced-generic/output_headers.go
@@ -15,7 +15,7 @@ func outputHeaders() usecase.Interactor {
 		InBody string `json:"inBody" deprecated:"true"`
 	}
 
-	u := usecase.NewInteractor(func(ctx context.Context, _ interface{}, out *headerOutput) (err error) {
+	u := usecase.NewInteractor(func(ctx context.Context, _ struct{}, out *headerOutput) (err error) {
 		out.Header = "abc"
 		out.InBody = "def"
 
diff --git a/_examples/advanced-generic/request_response_mapping.go b/_examples/advanced-generic/request_response_mapping.go
index 6330d32..97adee2 100644
--- a/_examples/advanced-generic/request_response_mapping.go
+++ b/_examples/advanced-generic/request_response_mapping.go
@@ -11,13 +11,13 @@ import (
 
 func reqRespMapping() usecase.Interactor {
 	type inputPort struct {
-		Val1 string `description:"Simple scalar value."`
-		Val2 int    `description:"Simple scalar value."`
+		Val1 string `description:"Simple scalar value with sample validation." required:"true" minLength:"3"`
+		Val2 int    `description:"Simple scalar value with sample validation." required:"true" minimum:"3"`
 	}
 
 	type outputPort struct {
-		Val1 string `json:"-"`
-		Val2 int    `json:"-"`
+		Val1 string `json:"-" description:"Simple scalar value with sample validation." required:"true" minLength:"3"`
+		Val2 int    `json:"-" description:"Simple scalar value with sample validation." required:"true" minimum:"3"`
 	}
 
 	u := usecase.NewInteractor(func(ctx context.Context, in inputPort, out *outputPort) (err error) {
diff --git a/_examples/advanced-generic/router.go b/_examples/advanced-generic/router.go
index 0e80041..379e3fe 100644
--- a/_examples/advanced-generic/router.go
+++ b/_examples/advanced-generic/router.go
@@ -96,6 +96,7 @@ func NewRouter() http.Handler {
 	))
 
 	r.Method(http.MethodPost, "/validation", nethttp.NewHandler(validation()))
+	r.Method(http.MethodPost, "/no-validation", nethttp.NewHandler(validation()))
 
 	// Type mapping is necessary to pass interface as structure into documentation.
 	apiSchema.Reflector().AddTypeMapping(new(gzipPassThroughOutput), new(gzipPassThroughStruct))
diff --git a/_examples/advanced/no_validation.go b/_examples/advanced/no_validation.go
new file mode 100644
index 0000000..53e7385
--- /dev/null
+++ b/_examples/advanced/no_validation.go
@@ -0,0 +1,41 @@
+package main
+
+import (
+	"context"
+
+	"github.com/swaggest/usecase"
+)
+
+func noValidation() usecase.Interactor {
+	type inputPort struct {
+		Header int  `header:"X-Input"`
+		Query  bool `query:"q"`
+		Data   struct {
+			Value string `json:"value"`
+		} `json:"data"`
+	}
+
+	type outputPort struct {
+		Header        int  `header:"X-Output" json:"-"`
+		AnotherHeader bool `header:"X-Query" json:"-"`
+		Data          struct {
+			Value string `json:"value"`
+		} `json:"data"`
+	}
+
+	u := usecase.NewIOI(new(inputPort), new(outputPort), func(ctx context.Context, input, output interface{}) (err error) {
+		in := input.(*inputPort)
+		out := output.(*outputPort)
+
+		out.Header = in.Header
+		out.AnotherHeader = in.Query
+		out.Data.Value = in.Data.Value
+
+		return nil
+	})
+
+	u.SetTitle("No Validation")
+	u.SetDescription("Input/Output without validation.")
+
+	return u
+}
diff --git a/_examples/advanced/request_response_mapping.go b/_examples/advanced/request_response_mapping.go
index 2de9075..3b159cf 100644
--- a/_examples/advanced/request_response_mapping.go
+++ b/_examples/advanced/request_response_mapping.go
@@ -8,13 +8,13 @@ import (
 
 func reqRespMapping() usecase.Interactor {
 	type inputPort struct {
-		Val1 string `description:"Simple scalar value."`
-		Val2 int    `description:"Simple scalar value."`
+		Val1 string `description:"Simple scalar value with sample validation." required:"true" minLength:"3"`
+		Val2 int    `description:"Simple scalar value with sample validation." required:"true" minimum:"3"`
 	}
 
 	type outputPort struct {
-		Val1 string `json:"-"`
-		Val2 int    `json:"-"`
+		Val1 string `json:"-" description:"Simple scalar value with sample validation." required:"true" minLength:"3"`
+		Val2 int    `json:"-" description:"Simple scalar value with sample validation." required:"true" minimum:"3"`
 	}
 
 	u := usecase.NewIOI(new(inputPort), new(outputPort), func(ctx context.Context, input, output interface{}) (err error) {
diff --git a/_examples/advanced/router.go b/_examples/advanced/router.go
index 105f87e..e28500c 100644
--- a/_examples/advanced/router.go
+++ b/_examples/advanced/router.go
@@ -88,6 +88,7 @@ func NewRouter() http.Handler {
 	))
 
 	r.Method(http.MethodPost, "/validation", nethttp.NewHandler(validation()))
+	r.Method(http.MethodPost, "/no-validation", nethttp.NewHandler(noValidation()))
 
 	// Type mapping is necessary to pass interface as structure into documentation.
 	apiSchema.Reflector().AddTypeMapping(new(gzipPassThroughOutput), new(gzipPassThroughStruct))
diff --git a/openapi/collector.go b/openapi/collector.go
index 1e3fe52..c2368ac 100644
--- a/openapi/collector.go
+++ b/openapi/collector.go
@@ -10,6 +10,7 @@ import (
 	"strconv"
 	"sync"
 
+	"github.com/swaggest/jsonschema-go"
 	"github.com/swaggest/openapi-go/openapi3"
 	"github.com/swaggest/rest"
 	"github.com/swaggest/usecase"
@@ -293,34 +294,59 @@ func (c *Collector) ProvideRequestJSONSchemas(
 		return err
 	}
 
-	if op.RequestBody != nil && op.RequestBody.RequestBody != nil {
-		for ct, content := range op.RequestBody.RequestBody.Content {
-			var in rest.ParamIn
-
-			switch ct {
-			case "application/json":
-				in = rest.ParamInBody
-			case "application/x-www-form-urlencoded":
-				in = rest.ParamInFormData
-			default:
-				continue
-			}
+	if op.RequestBody == nil || op.RequestBody.RequestBody == nil {
+		return nil
+	}
 
-			schema := content.Schema.ToJSONSchema(c.Reflector().Spec)
-			if schema.IsTrivial(c.Reflector().ResolveJSONSchemaRef) {
-				continue
-			}
+	for ct, content := range op.RequestBody.RequestBody.Content {
+		schema := content.Schema.ToJSONSchema(c.Reflector().Spec)
+		if schema.IsTrivial(c.Reflector().ResolveJSONSchemaRef) {
+			continue
+		}
 
+		if ct == "application/json" {
 			schemaData, err := schema.JSONSchemaBytes()
 			if err != nil {
-				return errors.New("failed to build JSON Schema for request body")
+				return fmt.Errorf("failed to build JSON Schema for request body: %w", err)
 			}
 
-			err = validator.AddSchema(in, "body", schemaData, false)
+			err = validator.AddSchema(rest.ParamInBody, "body", schemaData, false)
 			if err != nil {
 				return fmt.Errorf("failed to add validation schema for request body: %w", err)
 			}
 		}
+
+		if ct == "application/x-www-form-urlencoded" {
+			if err = provideFormDataSchemas(schema, validator); err != nil {
+				return err
+			}
+		}
+	}
+
+	return nil
+}
+
+func provideFormDataSchemas(schema jsonschema.SchemaOrBool, validator rest.JSONSchemaValidator) error {
+	for name, sch := range schema.TypeObject.Properties {
+		sb, err := sch.JSONSchemaBytes()
+		if err != nil {
+			return fmt.Errorf("failed to build JSON Schema for form data parameter %q: %w", name, err)
+		}
+
+		isRequired := false
+
+		for _, req := range schema.TypeObject.Required {
+			if req == name {
+				isRequired = true
+
+				break
+			}
+		}
+
+		err = validator.AddSchema(rest.ParamInFormData, name, sb, isRequired)
+		if err != nil {
+			return fmt.Errorf("failed to add validation schema for request body: %w", err)
+		}
 	}
 
 	return nil
diff --git a/request/middleware.go b/request/middleware.go
index 8e6811a..1df668a 100644
--- a/request/middleware.go
+++ b/request/middleware.go
@@ -71,8 +71,10 @@ func ValidatorMiddleware(factory rest.RequestValidatorFactory) func(http.Handler
 			return handler
 		}
 
-		handlerTrait.RestHandler().ReqValidator = factory.MakeRequestValidator(
-			withRoute.RouteMethod(), useCaseWithInput.InputPort(), nil)
+		rh := handlerTrait.RestHandler()
+
+		rh.ReqValidator = factory.MakeRequestValidator(
+			withRoute.RouteMethod(), useCaseWithInput.InputPort(), rh.ReqMapping)
 
 		return handler
 	}


Test patch:
diff --git a/_examples/advanced-generic/_testdata/openapi.json b/_examples/advanced-generic/_testdata/openapi.json
index 385ede9..c6c4abe 100644
--- a/_examples/advanced-generic/_testdata/openapi.json
+++ b/_examples/advanced-generic/_testdata/openapi.json
@@ -210,6 +210,40 @@
         }
       }
     },
+    "/no-validation":{
+      "post":{
+        "summary":"Validation","description":"Input/Output with validation.",
+        "operationId":"_examples/advanced-generic.validation",
+        "parameters":[
+          {
+            "name":"q","in":"query",
+            "description":"This parameter will bypass explicit validation as it does not have constraints.",
+            "schema":{
+              "type":"boolean",
+              "description":"This parameter will bypass explicit validation as it does not have constraints."
+            }
+          },
+          {
+            "name":"X-Input","in":"header","description":"Request minimum: 10, response maximum: 20.",
+            "schema":{"minimum":10,"type":"integer","description":"Request minimum: 10, response maximum: 20."}
+          }
+        ],
+        "requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdvancedInputPortType2"}}}},
+        "responses":{
+          "200":{
+            "description":"OK",
+            "headers":{
+              "X-Output":{"style":"simple","schema":{"maximum":20,"type":"integer"}},
+              "X-Query":{
+                "style":"simple","description":"This header bypasses validation as it does not have constraints.",
+                "schema":{"type":"boolean","description":"This header bypasses validation as it does not have constraints."}
+              }
+            },
+            "content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdvancedOutputPortType2"}}}
+          }
+        }
+      }
+    },
     "/output-csv-writer":{
       "get":{
         "summary":"Output With Stream Writer","description":"Output with stream writer.",
@@ -291,8 +325,8 @@
         "operationId":"reqRespMapping",
         "parameters":[
           {
-            "name":"X-Header","in":"header","description":"Simple scalar value.",
-            "schema":{"type":"string","description":"Simple scalar value."}
+            "name":"X-Header","in":"header","description":"Simple scalar value with sample validation.","required":true,
+            "schema":{"minLength":3,"type":"string","description":"Simple scalar value with sample validation."}
           }
         ],
         "requestBody":{
@@ -302,8 +336,14 @@
           "204":{
             "description":"No Content",
             "headers":{
-              "X-Value-1":{"style":"simple","schema":{"type":"string"}},
-              "X-Value-2":{"style":"simple","schema":{"type":"integer"}}
+              "X-Value-1":{
+                "style":"simple","description":"Simple scalar value with sample validation.","required":true,
+                "schema":{"minLength":3,"type":"string","description":"Simple scalar value with sample validation."}
+              },
+              "X-Value-2":{
+                "style":"simple","description":"Simple scalar value with sample validation.","required":true,
+                "schema":{"minimum":3,"type":"integer","description":"Simple scalar value with sample validation."}
+              }
             }
           }
         }
@@ -436,7 +476,10 @@
           "name":{"minLength":3,"type":"string"}
         }
       },
-      "FormDataAdvancedInputPort":{"type":"object","properties":{"val2":{"type":"integer","description":"Simple scalar value."}}},
+      "FormDataAdvancedInputPort":{
+        "required":["val2"],"type":"object",
+        "properties":{"val2":{"minimum":3,"type":"integer","description":"Simple scalar value with sample validation."}}
+      },
       "FormDataAdvancedUpload":{
         "type":"object",
         "properties":{
diff --git a/_examples/advanced-generic/output_headers_test.go b/_examples/advanced-generic/output_headers_test.go
index 7483b33..836fd70 100644
--- a/_examples/advanced-generic/output_headers_test.go
+++ b/_examples/advanced-generic/output_headers_test.go
@@ -4,11 +4,15 @@
 package main
 
 import (
+	"io/ioutil"
 	"net/http"
 	"net/http/httptest"
 	"testing"
 
 	"github.com/bool64/httptestbench"
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+	"github.com/swaggest/assertjson"
 	"github.com/valyala/fasthttp"
 )
 
@@ -26,3 +30,22 @@ func Benchmark_outputHeaders(b *testing.B) {
 		return resp.StatusCode() == http.StatusOK
 	})
 }
+
+func Test_outputHeaders(t *testing.T) {
+	r := NewRouter()
+
+	srv := httptest.NewServer(r)
+	defer srv.Close()
+
+	resp, err := http.Get(srv.URL + "/output-headers")
+	require.NoError(t, err)
+
+	assert.Equal(t, resp.StatusCode, http.StatusOK)
+
+	body, err := ioutil.ReadAll(resp.Body)
+	assert.NoError(t, err)
+	assert.NoError(t, resp.Body.Close())
+
+	assert.Equal(t, "abc", resp.Header.Get("X-Header"))
+	assertjson.Equal(t, []byte(`{"inBody":"def"}`), body)
+}
diff --git a/_examples/advanced-generic/request_response_mapping_test.go b/_examples/advanced-generic/request_response_mapping_test.go
new file mode 100644
index 0000000..64fdca2
--- /dev/null
+++ b/_examples/advanced-generic/request_response_mapping_test.go
@@ -0,0 +1,60 @@
+//go:build go1.18
+// +build go1.18
+
+package main
+
+import (
+	"bytes"
+	"io/ioutil"
+	"net/http"
+	"net/http/httptest"
+	"testing"
+
+	"github.com/bool64/httptestbench"
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+	"github.com/valyala/fasthttp"
+)
+
+func Test_requestResponseMapping(t *testing.T) {
+	r := NewRouter()
+
+	srv := httptest.NewServer(r)
+	defer srv.Close()
+
+	req, err := http.NewRequest(http.MethodPost, srv.URL+"/req-resp-mapping",
+		bytes.NewReader([]byte(`val2=3`)))
+	require.NoError(t, err)
+	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
+	req.Header.Set("X-Header", "abc")
+
+	resp, err := http.DefaultTransport.RoundTrip(req)
+	require.NoError(t, err)
+
+	assert.Equal(t, http.StatusNoContent, resp.StatusCode)
+
+	body, err := ioutil.ReadAll(resp.Body)
+	assert.NoError(t, err)
+	assert.NoError(t, resp.Body.Close())
+	assert.Empty(t, body)
+
+	assert.Equal(t, "abc", resp.Header.Get("X-Value-1"))
+	assert.Equal(t, "3", resp.Header.Get("X-Value-2"))
+}
+
+func Benchmark_requestResponseMapping(b *testing.B) {
+	r := NewRouter()
+
+	srv := httptest.NewServer(r)
+	defer srv.Close()
+
+	httptestbench.RoundTrip(b, 50, func(i int, req *fasthttp.Request) {
+		req.Header.SetMethod(http.MethodPost)
+		req.SetRequestURI(srv.URL + "/req-resp-mapping")
+		req.Header.Set("X-Header", "abc")
+		req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
+		req.SetBody([]byte(`val2=3`))
+	}, func(i int, resp *fasthttp.Response) bool {
+		return resp.StatusCode() == http.StatusNoContent
+	})
+}
diff --git a/_examples/advanced-generic/validation_test.go b/_examples/advanced-generic/validation_test.go
index 3ea745b..67d6285 100644
--- a/_examples/advanced-generic/validation_test.go
+++ b/_examples/advanced-generic/validation_test.go
@@ -30,3 +30,20 @@ func Benchmark_validation(b *testing.B) {
 		return resp.StatusCode() == http.StatusOK
 	})
 }
+
+func Benchmark_noValidation(b *testing.B) {
+	r := NewRouter()
+
+	srv := httptest.NewServer(r)
+	defer srv.Close()
+
+	httptestbench.RoundTrip(b, 50, func(i int, req *fasthttp.Request) {
+		req.Header.SetMethod(http.MethodPost)
+		req.SetRequestURI(srv.URL + "/no-validation?q=true")
+		req.Header.Set("X-Input", "12")
+		req.Header.Set("Content-Type", "application/json")
+		req.SetBody([]byte(`{"data":{"value":"abc"}}`))
+	}, func(i int, resp *fasthttp.Response) bool {
+		return resp.StatusCode() == http.StatusOK
+	})
+}
diff --git a/_examples/advanced/_testdata/openapi.json b/_examples/advanced/_testdata/openapi.json
index 5c3a3e3..3dd3d0e 100644
--- a/_examples/advanced/_testdata/openapi.json
+++ b/_examples/advanced/_testdata/openapi.json
@@ -209,6 +209,27 @@
         }
       }
     },
+    "/no-validation":{
+      "post":{
+        "summary":"No Validation","description":"Input/Output without validation.",
+        "operationId":"_examples/advanced.noValidation",
+        "parameters":[
+          {"name":"q","in":"query","schema":{"type":"boolean"}},
+          {"name":"X-Input","in":"header","schema":{"type":"integer"}}
+        ],
+        "requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdvancedInputPortType3"}}}},
+        "responses":{
+          "200":{
+            "description":"OK",
+            "headers":{
+              "X-Output":{"style":"simple","schema":{"type":"integer"}},
+              "X-Query":{"style":"simple","schema":{"type":"boolean"}}
+            },
+            "content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdvancedOutputPortType3"}}}
+          }
+        }
+      }
+    },
     "/output-csv-writer":{
       "get":{
         "summary":"Output With Stream Writer","description":"Output with stream writer.","operationId":"",
@@ -287,8 +308,8 @@
         "operationId":"reqRespMapping",
         "parameters":[
           {
-            "name":"X-Header","in":"header","description":"Simple scalar value.",
-            "schema":{"type":"string","description":"Simple scalar value."}
+            "name":"X-Header","in":"header","description":"Simple scalar value with sample validation.","required":true,
+            "schema":{"minLength":3,"type":"string","description":"Simple scalar value with sample validation."}
           }
         ],
         "requestBody":{
@@ -298,8 +319,14 @@
           "204":{
             "description":"No Content",
             "headers":{
-              "X-Value-1":{"style":"simple","schema":{"type":"string"}},
-              "X-Value-2":{"style":"simple","schema":{"type":"integer"}}
+              "X-Value-1":{
+                "style":"simple","description":"Simple scalar value with sample validation.","required":true,
+                "schema":{"minLength":3,"type":"string","description":"Simple scalar value with sample validation."}
+              },
+              "X-Value-2":{
+                "style":"simple","description":"Simple scalar value with sample validation.","required":true,
+                "schema":{"minimum":3,"type":"integer","description":"Simple scalar value with sample validation."}
+              }
             }
           }
         }
@@ -374,6 +401,7 @@
           }
         }
       },
+      "AdvancedInputPortType3":{"type":"object","properties":{"data":{"type":"object","properties":{"value":{"type":"string"}}}}},
       "AdvancedInputWithJSONType2":{
         "type":"object",
         "properties":{
@@ -406,6 +434,7 @@
         "required":["data"],"type":"object",
         "properties":{"data":{"type":"object","properties":{"value":{"maxLength":7,"type":"string"}}}}
       },
+      "AdvancedOutputPortType3":{"type":"object","properties":{"data":{"type":"object","properties":{"value":{"type":"string"}}}}},
       "AdvancedOutputQueryObject":{
         "type":"object",
         "properties":{"inQuery":{"type":"object","additionalProperties":{"type":"number"},"nullable":true}}
@@ -432,7 +461,10 @@
           "name":{"minLength":3,"type":"string"}
         }
       },
-      "FormDataAdvancedInputPort":{"type":"object","properties":{"val2":{"type":"integer","description":"Simple scalar value."}}},
+      "FormDataAdvancedInputPort":{
+        "required":["val2"],"type":"object",
+        "properties":{"val2":{"minimum":3,"type":"integer","description":"Simple scalar value with sample validation."}}
+      },
       "FormDataAdvancedUpload":{
         "type":"object",
         "properties":{
diff --git a/_examples/advanced/request_response_mapping_test.go b/_examples/advanced/request_response_mapping_test.go
new file mode 100644
index 0000000..6215d12
--- /dev/null
+++ b/_examples/advanced/request_response_mapping_test.go
@@ -0,0 +1,57 @@
+package main
+
+import (
+	"bytes"
+	"io/ioutil"
+	"net/http"
+	"net/http/httptest"
+	"testing"
+
+	"github.com/bool64/httptestbench"
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+	"github.com/valyala/fasthttp"
+)
+
+func Test_requestResponseMapping(t *testing.T) {
+	r := NewRouter()
+
+	srv := httptest.NewServer(r)
+	defer srv.Close()
+
+	req, err := http.NewRequest(http.MethodPost, srv.URL+"/req-resp-mapping",
+		bytes.NewReader([]byte(`val2=3`)))
+	require.NoError(t, err)
+	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
+	req.Header.Set("X-Header", "abc")
+
+	resp, err := http.DefaultTransport.RoundTrip(req)
+	require.NoError(t, err)
+
+	assert.Equal(t, http.StatusNoContent, resp.StatusCode)
+
+	body, err := ioutil.ReadAll(resp.Body)
+	assert.NoError(t, err)
+	assert.NoError(t, resp.Body.Close())
+	assert.Empty(t, body)
+
+	assert.Equal(t, "abc", resp.Header.Get("X-Value-1"))
+	assert.Equal(t, "3", resp.Header.Get("X-Value-2"))
+}
+
+func Benchmark_requestResponseMapping(b *testing.B) {
+	r := NewRouter()
+
+	srv := httptest.NewServer(r)
+	defer srv.Close()
+
+	httptestbench.RoundTrip(b, 50, func(i int, req *fasthttp.Request) {
+		req.Header.SetMethod(http.MethodPost)
+		req.SetRequestURI(srv.URL + "/req-resp-mapping")
+		req.Header.Set("X-Header", "abc")
+		req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
+		req.SetBody([]byte(`val2=3`))
+	}, func(i int, resp *fasthttp.Response) bool {
+		return resp.StatusCode() == http.StatusNoContent
+	})
+}
diff --git a/_examples/advanced/validation_test.go b/_examples/advanced/validation_test.go
index 088b5db..5a181e3 100644
--- a/_examples/advanced/validation_test.go
+++ b/_examples/advanced/validation_test.go
@@ -27,3 +27,20 @@ func Benchmark_validation(b *testing.B) {
 		return resp.StatusCode() == http.StatusOK
 	})
 }
+
+func Benchmark_noValidation(b *testing.B) {
+	r := NewRouter()
+
+	srv := httptest.NewServer(r)
+	defer srv.Close()
+
+	httptestbench.RoundTrip(b, 50, func(i int, req *fasthttp.Request) {
+		req.Header.SetMethod(http.MethodPost)
+		req.SetRequestURI(srv.URL + "/no-validation?q=true")
+		req.Header.Set("X-Input", "12")
+		req.Header.Set("Content-Type", "application/json")
+		req.SetBody([]byte(`{"data":{"value":"abc"}}`))
+	}, func(i int, resp *fasthttp.Response) bool {
+		return resp.StatusCode() == http.StatusOK
+	})
+}
diff --git a/jsonschema/validator_test.go b/jsonschema/validator_test.go
index b0b365c..0e69ee7 100644
--- a/jsonschema/validator_test.go
+++ b/jsonschema/validator_test.go
@@ -36,10 +36,12 @@ func BenchmarkRequestValidator_ValidateRequestData(b *testing.B) {
 func TestRequestValidator_ValidateData(t *testing.T) {
 	validator := jsonschema.NewFactory(&openapi.Collector{}, &openapi.Collector{}).
 		MakeRequestValidator(http.MethodPost, new(struct {
-			Cookie string `cookie:"in_cookie" minimum:"100" required:"true"`
-			Query  string `query:"in_query_ignored" minLength:"3"`
+			Cookie   string `cookie:"in_cookie" minimum:"100" required:"true"`
+			Query    string `query:"in_query_ignored" minLength:"3"`
+			FormData string `formData:"inFormDataIgnored" minLength:"3"`
 		}), rest.RequestMapping{
-			rest.ParamInQuery: map[string]string{"Query": "in_query"},
+			rest.ParamInQuery:    map[string]string{"Query": "in_query"},
+			rest.ParamInFormData: map[string]string{"FormData": "inFormData"},
 		})
 
 	err := validator.ValidateData(rest.ParamInCookie, map[string]interface{}{"in_cookie": 123})
@@ -59,6 +61,11 @@ func TestRequestValidator_ValidateData(t *testing.T) {
 	assert.NoError(t, validator.ValidateData(rest.ParamInQuery, map[string]interface{}{"in_query_ignored": 123}))
 	assert.NoError(t, validator.ValidateData("unknown", map[string]interface{}{}))
 	assert.NoError(t, validator.ValidateData(rest.ParamInCookie, map[string]interface{}{"in_cookie": "abc"}))
+
+	assert.NoError(t, validator.ValidateData(rest.ParamInFormData, map[string]interface{}{"inFormData": "abc"}))
+
+	err = validator.ValidateData(rest.ParamInFormData, map[string]interface{}{"inFormData": "ab"})
+	assert.Equal(t, err, rest.ValidationErrors{"formData:inFormData": []string{"#: length must be >= 3, but got 2"}})
 }
 
 func TestFactory_MakeResponseValidator(t *testing.T) {