Repository: swaggest/rest
Pull request #26 (2021-02-20 22:54:23)

Allow custom response structures and statuses in security middlewares

github-actions[bot]: ### Unit Test Coverage
total:	(statements)	81.1%
<details><summary>Coverage diff with base branch</summary>

```diff
45c45
< github.com/swaggest/rest/nethttp/handler.go	NewHandler	100.0%
---
> github.com/swaggest/rest/nethttp/handler.go	NewHandler	80.0%
60c60
< github.com/swaggest/rest/nethttp/openapi.go	HTTPBasicSecurityMiddleware	100.0%
---
> github.com/swaggest/rest/nethttp/openapi.go	HTTPBasicSecurityMiddleware	88.9%
62a63
> github.com/swaggest/rest/nethttp/openapi.go	SecurityResponse	100.0%
74c75
< github.com/swaggest/rest/openapi/collector.go	Collect	72.0%
---
> github.com/swaggest/rest/openapi/collector.go	Collect	70.4%
```
</details>
<!-- Sticky Pull Request Commentunit-test -->
codecov[bot]: # [Codecov](https://codecov.io/gh/swaggest/rest/pull/26?src=pr&el=h1) Report
> Merging [#26](https://codecov.io/gh/swaggest/rest/pull/26?src=pr&el=desc) (b343997) into [master](https://codecov.io/gh/swaggest/rest/commit/1beaa1a17c2175024b268901ff2f912240c6cf0f?el=desc) (1beaa1a) will **decrease** coverage by `0.06%`.
> The diff coverage is `69.56%`.

[![Impacted file tree graph](https://codecov.io/gh/swaggest/rest/pull/26/graphs/tree.svg?width=650&height=150&src=pr&token=OpLHxxPdmn)](https://codecov.io/gh/swaggest/rest/pull/26?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master      #26      +/-   ##
==========================================
- Coverage   73.41%   73.34%   -0.07%     
==========================================
  Files          26       26              
  Lines        1482     1497      +15     
==========================================
+ Hits         1088     1098      +10     
- Misses        249      252       +3     
- Partials      145      147       +2     
```

| Flag | Coverage Δ | |
|---|---|---|
| unittests | `73.34% <69.56%> (-0.07%)` | :arrow_down: |

Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags#carryforward-flags-in-the-pull-request-comment) to find out more.

| [Impacted Files](https://codecov.io/gh/swaggest/rest/pull/26?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [nethttp/handler.go](https://codecov.io/gh/swaggest/rest/pull/26/diff?src=pr&el=tree#diff-bmV0aHR0cC9oYW5kbGVyLmdv) | `86.84% <0.00%> (-2.35%)` | :arrow_down: |
| [openapi/collector.go](https://codecov.io/gh/swaggest/rest/pull/26/diff?src=pr&el=tree#diff-b3BlbmFwaS9jb2xsZWN0b3IuZ28=) | `64.62% <33.33%> (-0.62%)` | :arrow_down: |
| [nethttp/openapi.go](https://codecov.io/gh/swaggest/rest/pull/26/diff?src=pr&el=tree#diff-bmV0aHR0cC9vcGVuYXBpLmdv) | `92.95% <93.33%> (-0.38%)` | :arrow_down: |

------

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

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

```
Using vendor
name                                    old time/op    new time/op    delta
pkg:github.com/swaggest/rest/jsonschema goos:linux goarch:amd64
RequestValidator_ValidateRequestData-2    1.71µs ± 2%    1.61µs ± 1%  -6.01%  (p=0.016 n=5+4)
pkg:github.com/swaggest/rest/request goos:linux goarch:amd64
Decoder_Decode-2                           962ns ± 3%     893ns ± 0%  -7.18%  (p=0.016 n=5+4)
DecoderFunc_Decode-2                      2.60µs ± 1%    2.48µs ± 1%  -4.49%  (p=0.008 n=5+5)
Decoder_Decode_json-2                     29.7µs ± 1%    28.2µs ± 1%  -4.74%  (p=0.008 n=5+5)
Decoder_Decode_queryObject-2              6.93µs ± 1%    6.65µs ± 1%  -3.92%  (p=0.008 n=5+5)
Decoder_Decode_jsonParam-2                2.34µs ± 1%    2.29µs ± 2%  -2.09%  (p=0.032 n=5+5)
DecoderFactory_SetDecoderFunc-2           2.17µs ± 1%    2.08µs ± 2%  -4.34%  (p=0.008 n=5+5)
pkg:github.com/swaggest/rest/response/gzip goos:linux goarch:amd64
Middleware-2                              16.7µs ± 1%    16.4µs ± 2%    ~     (p=0.095 n=5+5)
Middleware_control-2                      4.11µs ± 4%    3.84µs ± 1%  -6.46%  (p=0.008 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.50kB ± 0%    2.50kB ± 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.3kB ± 0%    12.3kB ± 0%    ~     (all equal)
Decoder_Decode_queryObject-2              2.00kB ± 0%    2.00kB ± 0%    ~     (all equal)
Decoder_Decode_jsonParam-2                  720B ± 0%      720B ± 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.12kB ± 2%    1.12kB ± 3%    ~     (p=0.365 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                        169 ± 0%       169 ± 0%    ~     (all equal)
Decoder_Decode_queryObject-2                36.0 ± 0%      36.0 ± 0%    ~     (all equal)
Decoder_Decode_jsonParam-2                  12.0 ± 0%      12.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>

### Examples Benchmark Result
<details><summary>Benchmark diff with base branch</summary>

```
Using vendor
name                    old time/op    new time/op    delta
pkg:github.com/swaggest/rest/_examples/advanced goos:linux goarch:amd64
_directGzip-2             26.5µs ± 3%    25.2µs ± 0%  -4.85%  (p=0.008 n=5+5)
_directGzipHead-2         26.5µs ± 2%    25.4µs ± 3%  -4.22%  (p=0.016 n=5+5)
_noDirectGzip-2            148µs ± 2%     146µs ± 1%  -1.69%  (p=0.032 n=5+5)
_directGzip_decode-2       551µs ± 1%     533µs ± 1%  -3.42%  (p=0.008 n=5+5)
_noDirectGzip_decode-2     149µs ± 1%     146µs ± 3%    ~     (p=0.222 n=5+5)
_jsonBody-2               48.1µs ± 2%    45.6µs ± 2%  -5.09%  (p=0.008 n=5+5)
_jsonBodyValidation-2     57.3µs ± 2%    52.9µs ± 1%  -7.64%  (p=0.008 n=5+5)
_outputHeaders-2          25.7µs ± 1%    24.4µs ± 1%  -5.16%  (p=0.008 n=5+5)
_validation-2             51.6µs ± 1%    49.0µs ± 3%  -5.04%  (p=0.008 n=5+5)
pkg:github.com/swaggest/rest/_examples/task-api/internal/infra/nethttp goos:linux goarch:amd64
_notFoundSrv-2            29.6µs ± 1%    28.7µs ± 1%  -3.12%  (p=0.008 n=5+5)
_ok-2                     31.0µs ± 1%    30.1µs ± 2%  -2.77%  (p=0.008 n=5+5)
_invalidBody-2            45.7µs ± 1%    44.8µs ± 3%    ~     (p=0.222 n=5+5)

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

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

name                    old rps        new rps        delta
pkg:github.com/swaggest/rest/_examples/advanced goos:linux goarch:amd64
_directGzip-2              37.8k ± 3%     39.7k ± 0%  +5.06%  (p=0.008 n=5+5)
_directGzipHead-2          37.7k ± 2%     39.4k ± 3%  +4.42%  (p=0.016 n=5+5)
_noDirectGzip-2            6.76k ± 2%     6.87k ± 1%  +1.70%  (p=0.032 n=5+5)
_directGzip_decode-2       1.81k ± 1%     1.88k ± 1%  +3.54%  (p=0.008 n=5+5)
_noDirectGzip_decode-2     6.71k ± 1%     6.87k ± 3%    ~     (p=0.222 n=5+5)
_jsonBody-2                20.8k ± 2%     21.9k ± 2%  +5.34%  (p=0.008 n=5+5)
_jsonBodyValidation-2      17.5k ± 2%     18.9k ± 1%  +8.27%  (p=0.008 n=5+5)
_outputHeaders-2           38.9k ± 1%     41.0k ± 1%  +5.44%  (p=0.008 n=5+5)
_validation-2              19.4k ± 1%     20.4k ± 2%  +5.32%  (p=0.008 n=5+5)
pkg:github.com/swaggest/rest/_examples/task-api/internal/infra/nethttp goos:linux goarch:amd64
_notFoundSrv-2             33.7k ± 1%     34.8k ± 1%  +3.22%  (p=0.008 n=5+5)
_ok-2                      32.3k ± 1%     33.2k ± 2%  +2.85%  (p=0.008 n=5+5)
_invalidBody-2             21.9k ± 1%     22.3k ± 3%    ~     (p=0.222 n=5+5)

name                    old alloc/op   new alloc/op   delta
pkg:github.com/swaggest/rest/_examples/advanced goos:linux goarch:amd64
_directGzip-2             3.86kB ± 0%    3.86kB ± 0%    ~     (p=0.587 n=5+5)
_directGzipHead-2         3.86kB ± 0%    3.86kB ± 0%    ~     (p=1.000 n=5+4)
_noDirectGzip-2           6.35kB ±19%    6.10kB ±21%    ~     (p=0.421 n=5+5)
_directGzip_decode-2       402kB ± 0%     402kB ± 0%    ~     (p=0.794 n=5+5)
_noDirectGzip_decode-2    5.13kB ± 2%    5.23kB ± 5%    ~     (p=0.548 n=5+5)
_jsonBody-2               11.7kB ± 0%    11.7kB ± 0%    ~     (p=0.786 n=5+5)
_jsonBodyValidation-2     18.2kB ± 0%    18.2kB ± 0%    ~     (p=0.198 n=5+5)
_outputHeaders-2          3.64kB ± 0%    3.63kB ± 0%    ~     (p=0.095 n=5+4)
_validation-2             16.2kB ± 0%    16.2kB ± 0%    ~     (p=0.516 n=5+5)
pkg:github.com/swaggest/rest/_examples/task-api/internal/infra/nethttp goos:linux goarch:amd64
_notFoundSrv-2            4.81kB ± 0%    4.81kB ± 0%    ~     (p=0.206 n=4+5)
_ok-2                     4.72kB ± 0%    4.72kB ± 0%    ~     (p=0.400 n=4+4)
_invalidBody-2            8.58kB ± 0%    8.58kB ± 0%    ~     (p=1.000 n=4+4)

name                    old allocs/op  new allocs/op  delta
pkg:github.com/swaggest/rest/_examples/advanced goos:linux goarch:amd64
_directGzip-2               40.0 ± 0%      40.0 ± 0%    ~     (all equal)
_directGzipHead-2           40.0 ± 0%      40.0 ± 0%    ~     (all equal)
_noDirectGzip-2             47.8 ± 4%      47.6 ± 3%    ~     (p=1.000 n=5+5)
_directGzip_decode-2         497 ± 0%       497 ± 0%    ~     (all equal)
_noDirectGzip_decode-2      48.0 ± 0%      48.0 ± 0%    ~     (all equal)
_jsonBody-2                  103 ± 0%       103 ± 0%    ~     (all equal)
_jsonBodyValidation-2        144 ± 0%       144 ± 0%    ~     (all equal)
_outputHeaders-2            35.0 ± 0%      35.0 ± 0%    ~     (all equal)
_validation-2                131 ± 0%       131 ± 0%    ~     (all equal)
pkg:github.com/swaggest/rest/_examples/task-api/internal/infra/nethttp goos:linux goarch:amd64
_notFoundSrv-2              52.0 ± 0%      52.0 ± 0%    ~     (all equal)
_ok-2                       48.0 ± 0%      48.0 ± 0%    ~     (all equal)
_invalidBody-2              98.0 ± 0%      98.0 ± 0%    ~     (all equal)
```
</details>
<!-- Sticky Pull Request Commentbench -->

Patch:
diff --git a/_examples/go.mod b/_examples/go.mod
index a815057..8055034 100644
--- a/_examples/go.mod
+++ b/_examples/go.mod
@@ -5,17 +5,17 @@ go 1.13
 replace github.com/swaggest/rest => ../
 
 require (
-	github.com/bool64/dev v0.1.18
+	github.com/bool64/dev v0.1.19
 	github.com/bool64/httptestbench v0.1.1
 	github.com/go-chi/chi v1.5.2
 	github.com/kelseyhightower/envconfig v1.4.0
 	github.com/stretchr/testify v1.7.0
 	github.com/swaggest/assertjson v1.6.3
-	github.com/swaggest/jsonschema-go v0.3.15
+	github.com/swaggest/jsonschema-go v0.3.17
 	github.com/swaggest/openapi-go v0.2.9
 	github.com/swaggest/rest v0.1.17
 	github.com/swaggest/swgui v1.2.0
-	github.com/swaggest/usecase v0.1.2
+	github.com/swaggest/usecase v0.1.3
 	github.com/valyala/fasthttp v1.17.0
-	golang.org/x/net v0.0.0-20210119194325-5f4716e94777 // indirect
+	golang.org/x/net v0.0.0-20210220033124-5f55cee0dc0d // indirect
 )
diff --git a/_examples/go.sum b/_examples/go.sum
index 596e59f..4981618 100644
--- a/_examples/go.sum
+++ b/_examples/go.sum
@@ -8,8 +8,9 @@ github.com/bool64/dev v0.1.10/go.mod h1:pn52JC52uSgpazChx9CeXyG+S3sW2V36HHoLNBbs
 github.com/bool64/dev v0.1.12/go.mod h1:pn52JC52uSgpazChx9CeXyG+S3sW2V36HHoLNBbscdg=
 github.com/bool64/dev v0.1.15/go.mod h1:cTHiTDNc8EewrQPy3p1obNilpMpdmlUesDkFTF2zRWU=
 github.com/bool64/dev v0.1.17/go.mod h1:cTHiTDNc8EewrQPy3p1obNilpMpdmlUesDkFTF2zRWU=
-github.com/bool64/dev v0.1.18 h1:uMN5MsHrVWmtRoauefI8wD86b8vbXYnrCZlIhFGyuXI=
 github.com/bool64/dev v0.1.18/go.mod h1:cTHiTDNc8EewrQPy3p1obNilpMpdmlUesDkFTF2zRWU=
+github.com/bool64/dev v0.1.19 h1:SLAOGmJIrbroYjkGuV0MBLtww2NddzPDne1C7ATJdJk=
+github.com/bool64/dev v0.1.19/go.mod h1:cTHiTDNc8EewrQPy3p1obNilpMpdmlUesDkFTF2zRWU=
 github.com/bool64/httptestbench v0.1.1 h1:08112fXc5F1XuTWLdC9lPGB4FSWNACoeUWTeoR0a2qA=
 github.com/bool64/httptestbench v0.1.1/go.mod h1:gSmfFacu85Je3u6ZjmLZsFolcv7cwg1fLNUCzq2UUsM=
 github.com/bool64/shared v0.1.0/go.mod h1:khzw051JtnOZH6lX21rQ+sqbeC0FFEcU1wfchRS7EyU=
@@ -126,8 +127,8 @@ github.com/swaggest/jsonschema-go v0.2.1/go.mod h1:QFauBdPTrU1UltwocM5FzOWnVjVVt
 github.com/swaggest/jsonschema-go v0.2.4/go.mod h1:m4VV88Gbi7lCrt9ckJzigK1rMlEeFjdZUkJr1o5MnDE=
 github.com/swaggest/jsonschema-go v0.3.7/go.mod h1:TrWgbug4p2ZgcxnHDz+CvYvEtJ5KckL/XOV4mSR6FGw=
 github.com/swaggest/jsonschema-go v0.3.13/go.mod h1:INO3Dt0DeyNCbQbqMXNpMQ489BcyRtSb/k62OfmHbpY=
-github.com/swaggest/jsonschema-go v0.3.15 h1:6D23R4rhU2fVELiMyXtssVxlS/8eq6krRzemv07VvK8=
-github.com/swaggest/jsonschema-go v0.3.15/go.mod h1:1JyQxHbtZcK3WqGqRybu022vrDdI02j9Xu9sK/Z58Tg=
+github.com/swaggest/jsonschema-go v0.3.17 h1:9BjsqEq3nIipXJbbN8+XNMsAu2lxbUVeYqI+l8DWtAA=
+github.com/swaggest/jsonschema-go v0.3.17/go.mod h1:VrFw5hc9rCdtWJPhmfJi8UuSzpDJY+CAPn8QJ0SlxM0=
 github.com/swaggest/openapi-go v0.1.3/go.mod h1:Zx4ZgJ7XvlFH9wCOHE7u8RAjLfiHAnCHeaD5kUDujVM=
 github.com/swaggest/openapi-go v0.1.13/go.mod h1:fju3Ka5zb8qBKf4789zLNVUqITydWLDjtPloZhhYHL8=
 github.com/swaggest/openapi-go v0.2.9 h1:JJQrDSRtKMycIkLju97yjhJbw72mqhsCDJjqFB8HvSs=
@@ -144,8 +145,8 @@ github.com/swaggest/swgen v0.6.27 h1:bO3CtX4NvuetPuABMChEEp2N/bjsJraegaJpYZ72aoI
 github.com/swaggest/swgen v0.6.27/go.mod h1:tQLsoQVLcvtAMzNbm26JbWXDHZM4BBRuI5nemk3gEDw=
 github.com/swaggest/swgui v1.2.0 h1:zZWybwcfeWUNgOJOa+/FkLtYi7xI7pvGXnJrPcwh8vY=
 github.com/swaggest/swgui v1.2.0/go.mod h1:gn/xO+5S+BDFhtZP05gPiB/P6cXurNPv6kAeDJYgT98=
-github.com/swaggest/usecase v0.1.2 h1:Fi3z0nig4gBcX0n0nnEHD8QzCttdV3lAPmaoX3nQowA=
-github.com/swaggest/usecase v0.1.2/go.mod h1:BKvpHx94bpg+4RCXbrCv0jx8CpJRr5P8dYK0b4UfUZc=
+github.com/swaggest/usecase v0.1.3 h1:rK/t7PR3pt6pDEVQuT9n/aXHbMtKNzulqniQ/GSOiCc=
+github.com/swaggest/usecase v0.1.3/go.mod h1:d94PwelVQeMv4RH0VsRMFqRNrk6ferEqg2AetDcjPWo=
 github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
 github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
 github.com/valyala/fasthttp v1.17.0 h1:P8/koH4aSnJ4xbd0cUUFEGQs3jQqIxoDDyRQrUiAkqg=
@@ -181,8 +182,8 @@ golang.org/x/net v0.0.0-20201016165138-7b1cca2348c0/go.mod h1:sp8m0HH+o8qH0wwXwY
 golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
 golang.org/x/net v0.0.0-20201216054612-986b41b23924/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
 golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
-golang.org/x/net v0.0.0-20210119194325-5f4716e94777 h1:003p0dJM77cxMSyCPFphvZf/Y5/NXf5fzg6ufd1/Oew=
-golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
+golang.org/x/net v0.0.0-20210220033124-5f55cee0dc0d h1:1aflnvSoWWLI2k/dMUAl5lvU1YO4Mb4hz0gh+1rjcxU=
+golang.org/x/net v0.0.0-20210220033124-5f55cee0dc0d/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
diff --git a/go.mod b/go.mod
index aaba50e..67b9fef 100644
--- a/go.mod
+++ b/go.mod
@@ -3,7 +3,7 @@ module github.com/swaggest/rest
 go 1.13
 
 require (
-	github.com/bool64/dev v0.1.18
+	github.com/bool64/dev v0.1.19
 	github.com/bool64/shared v0.1.2
 	github.com/cespare/xxhash/v2 v2.1.1
 	github.com/go-chi/chi v1.5.2
@@ -12,8 +12,8 @@ require (
 	github.com/stretchr/testify v1.7.0
 	github.com/swaggest/assertjson v1.6.3
 	github.com/swaggest/form v1.11.1
-	github.com/swaggest/jsonschema-go v0.3.15
+	github.com/swaggest/jsonschema-go v0.3.17
 	github.com/swaggest/openapi-go v0.2.9
 	github.com/swaggest/refl v0.1.7
-	github.com/swaggest/usecase v0.1.2
+	github.com/swaggest/usecase v0.1.3
 )
diff --git a/go.sum b/go.sum
index 82440c1..c89e109 100644
--- a/go.sum
+++ b/go.sum
@@ -5,8 +5,8 @@ github.com/bool64/dev v0.1.10/go.mod h1:pn52JC52uSgpazChx9CeXyG+S3sW2V36HHoLNBbs
 github.com/bool64/dev v0.1.12/go.mod h1:pn52JC52uSgpazChx9CeXyG+S3sW2V36HHoLNBbscdg=
 github.com/bool64/dev v0.1.15/go.mod h1:cTHiTDNc8EewrQPy3p1obNilpMpdmlUesDkFTF2zRWU=
 github.com/bool64/dev v0.1.17/go.mod h1:cTHiTDNc8EewrQPy3p1obNilpMpdmlUesDkFTF2zRWU=
-github.com/bool64/dev v0.1.18 h1:uMN5MsHrVWmtRoauefI8wD86b8vbXYnrCZlIhFGyuXI=
-github.com/bool64/dev v0.1.18/go.mod h1:cTHiTDNc8EewrQPy3p1obNilpMpdmlUesDkFTF2zRWU=
+github.com/bool64/dev v0.1.19 h1:SLAOGmJIrbroYjkGuV0MBLtww2NddzPDne1C7ATJdJk=
+github.com/bool64/dev v0.1.19/go.mod h1:cTHiTDNc8EewrQPy3p1obNilpMpdmlUesDkFTF2zRWU=
 github.com/bool64/shared v0.1.0/go.mod h1:khzw051JtnOZH6lX21rQ+sqbeC0FFEcU1wfchRS7EyU=
 github.com/bool64/shared v0.1.1/go.mod h1:khzw051JtnOZH6lX21rQ+sqbeC0FFEcU1wfchRS7EyU=
 github.com/bool64/shared v0.1.2 h1:3W41/TM41O4dMZRZbKErzMcSZQuDG4UnkoWZWaQnTTo=
@@ -112,8 +112,8 @@ github.com/swaggest/jsonschema-go v0.2.1/go.mod h1:QFauBdPTrU1UltwocM5FzOWnVjVVt
 github.com/swaggest/jsonschema-go v0.2.4/go.mod h1:m4VV88Gbi7lCrt9ckJzigK1rMlEeFjdZUkJr1o5MnDE=
 github.com/swaggest/jsonschema-go v0.3.7/go.mod h1:TrWgbug4p2ZgcxnHDz+CvYvEtJ5KckL/XOV4mSR6FGw=
 github.com/swaggest/jsonschema-go v0.3.13/go.mod h1:INO3Dt0DeyNCbQbqMXNpMQ489BcyRtSb/k62OfmHbpY=
-github.com/swaggest/jsonschema-go v0.3.15 h1:6D23R4rhU2fVELiMyXtssVxlS/8eq6krRzemv07VvK8=
-github.com/swaggest/jsonschema-go v0.3.15/go.mod h1:1JyQxHbtZcK3WqGqRybu022vrDdI02j9Xu9sK/Z58Tg=
+github.com/swaggest/jsonschema-go v0.3.17 h1:9BjsqEq3nIipXJbbN8+XNMsAu2lxbUVeYqI+l8DWtAA=
+github.com/swaggest/jsonschema-go v0.3.17/go.mod h1:VrFw5hc9rCdtWJPhmfJi8UuSzpDJY+CAPn8QJ0SlxM0=
 github.com/swaggest/openapi-go v0.1.3/go.mod h1:Zx4ZgJ7XvlFH9wCOHE7u8RAjLfiHAnCHeaD5kUDujVM=
 github.com/swaggest/openapi-go v0.1.13/go.mod h1:fju3Ka5zb8qBKf4789zLNVUqITydWLDjtPloZhhYHL8=
 github.com/swaggest/openapi-go v0.2.9 h1:JJQrDSRtKMycIkLju97yjhJbw72mqhsCDJjqFB8HvSs=
@@ -128,8 +128,8 @@ github.com/swaggest/swgen v0.6.20/go.mod h1:ipkZNfwztgRfbOWUllLZawfmxXprT8flqNJO
 github.com/swaggest/swgen v0.6.23/go.mod h1:gj2yCLONy3kosKjwRtQeT5O9qqlhUvXAiDnbVwBUNFM=
 github.com/swaggest/swgen v0.6.27 h1:bO3CtX4NvuetPuABMChEEp2N/bjsJraegaJpYZ72aoI=
 github.com/swaggest/swgen v0.6.27/go.mod h1:tQLsoQVLcvtAMzNbm26JbWXDHZM4BBRuI5nemk3gEDw=
-github.com/swaggest/usecase v0.1.2 h1:Fi3z0nig4gBcX0n0nnEHD8QzCttdV3lAPmaoX3nQowA=
-github.com/swaggest/usecase v0.1.2/go.mod h1:BKvpHx94bpg+4RCXbrCv0jx8CpJRr5P8dYK0b4UfUZc=
+github.com/swaggest/usecase v0.1.3 h1:rK/t7PR3pt6pDEVQuT9n/aXHbMtKNzulqniQ/GSOiCc=
+github.com/swaggest/usecase v0.1.3/go.mod h1:d94PwelVQeMv4RH0VsRMFqRNrk6ferEqg2AetDcjPWo=
 github.com/vearutop/json5 v0.1.2 h1:AD+VTRiN8TZb4/ObzXXyYIY66AVNqdBn0O8ks/WqgH0=
 github.com/vearutop/json5 v0.1.2/go.mod h1:0EGfZGmwGu042tElaCn8+Song+vEMQOOgChj+mzD7OQ=
 github.com/yudai/gojsondiff v1.0.0 h1:27cbfqXLVEJ1o8I6v3y9lg8Ydm53EKqHXAOMxEGlCOA=
diff --git a/nethttp/handler.go b/nethttp/handler.go
index 2f41b76..d4cd8f1 100644
--- a/nethttp/handler.go
+++ b/nethttp/handler.go
@@ -15,6 +15,10 @@ var _ http.Handler = &Handler{}
 
 // NewHandler creates use case http handler.
 func NewHandler(useCase usecase.Interactor, options ...func(h *Handler)) *Handler {
+	if useCase == nil {
+		panic("usecase interactor is nil")
+	}
+
 	h := &Handler{
 		options: options,
 	}
diff --git a/nethttp/openapi.go b/nethttp/openapi.go
index cf0d2b6..8c8af97 100644
--- a/nethttp/openapi.go
+++ b/nethttp/openapi.go
@@ -36,7 +36,11 @@ func OpenAPIMiddleware(s *openapi.Collector) func(http.Handler) http.Handler {
 }
 
 // HTTPBasicSecurityMiddleware creates middleware to expose Basic Security schema.
-func HTTPBasicSecurityMiddleware(c *openapi.Collector, name, description string) func(http.Handler) http.Handler {
+func HTTPBasicSecurityMiddleware(
+	c *openapi.Collector,
+	name, description string,
+	options ...func(*MiddlewareConfig),
+) func(http.Handler) http.Handler {
 	hss := openapi3.HTTPSecurityScheme{}
 
 	hss.WithScheme("basic")
@@ -54,12 +58,20 @@ func HTTPBasicSecurityMiddleware(c *openapi.Collector, name, description string)
 		},
 	)
 
-	return securityMiddleware(c, name)
+	cfg := MiddlewareConfig{}
+
+	for _, o := range options {
+		o(&cfg)
+	}
+
+	return securityMiddleware(c, name, cfg)
 }
 
 // HTTPBearerSecurityMiddleware creates middleware to expose HTTP Bearer security schema.
 func HTTPBearerSecurityMiddleware(
-	c *openapi.Collector, name, description, bearerFormat string,
+	c *openapi.Collector,
+	name, description, bearerFormat string,
+	options ...func(*MiddlewareConfig),
 ) func(http.Handler) http.Handler {
 	hss := openapi3.HTTPSecurityScheme{}
 
@@ -82,7 +94,13 @@ func HTTPBearerSecurityMiddleware(
 		},
 	)
 
-	return securityMiddleware(c, name)
+	cfg := MiddlewareConfig{}
+
+	for _, o := range options {
+		o(&cfg)
+	}
+
+	return securityMiddleware(c, name, cfg)
 }
 
 // AnnotateOpenAPI applies OpenAPI annotation to relevant handlers.
@@ -105,10 +123,35 @@ func AnnotateOpenAPI(
 	}
 }
 
-func securityMiddleware(s *openapi.Collector, name string) func(http.Handler) http.Handler {
+// SecurityResponse is an security middleware option to customize response structure and status.
+func SecurityResponse(structure interface{}, httpStatus int) func(config *MiddlewareConfig) {
+	return func(config *MiddlewareConfig) {
+		config.ResponseStructure = structure
+		config.ResponseStatus = httpStatus
+	}
+}
+
+// MiddlewareConfig defines security middleware options.
+type MiddlewareConfig struct {
+	// ResponseStructure declares structure that is used for unauthorized message, default rest.ErrResponse{}.
+	ResponseStructure interface{}
+
+	// ResponseStatus declares HTTP status code that is used for unauthorized message, default http.StatusUnauthorized.
+	ResponseStatus int
+}
+
+func securityMiddleware(s *openapi.Collector, name string, cfg MiddlewareConfig) func(http.Handler) http.Handler {
 	return AnnotateOpenAPI(s, func(op *openapi3.Operation) error {
 		op.Security = append(op.Security, map[string][]string{name: {}})
 
-		return s.Reflector().SetJSONResponse(op, rest.ErrResponse{}, http.StatusUnauthorized)
+		if cfg.ResponseStatus == 0 {
+			cfg.ResponseStatus = http.StatusUnauthorized
+		}
+
+		if cfg.ResponseStructure == nil {
+			cfg.ResponseStructure = rest.ErrResponse{}
+		}
+
+		return s.Reflector().SetJSONResponse(op, cfg.ResponseStructure, cfg.ResponseStatus)
 	})
 }
diff --git a/openapi/collector.go b/openapi/collector.go
index 360918c..1e3fe52 100644
--- a/openapi/collector.go
+++ b/openapi/collector.go
@@ -51,11 +51,15 @@ func (c *Collector) Collect(
 	u usecase.Interactor,
 	h rest.HandlerTrait,
 	annotations ...func(*openapi3.Operation) error,
-) error {
+) (err error) {
 	c.mu.Lock()
 	defer c.mu.Unlock()
 
-	var err error
+	defer func() {
+		if err != nil {
+			err = fmt.Errorf("failed to reflect API schema for %s %s: %w", method, pattern, err)
+		}
+	}()
 
 	reflector := c.Reflector()
 
@@ -70,12 +74,12 @@ func (c *Collector) Collect(
 
 		err = c.setupInput(&oc, u, h)
 		if err != nil {
-			return err
+			return fmt.Errorf("failed to setup request: %w", err)
 		}
 
 		err = c.setupOutput(&oc, u)
 		if err != nil {
-			return err
+			return fmt.Errorf("failed to setup response: %w", err)
 		}
 
 		err = c.processUseCase(op, u, h)


Test patch:
diff --git a/_examples/advanced/_testdata/openapi.json b/_examples/advanced/_testdata/openapi.json
index 8c95b75..3187650 100644
--- a/_examples/advanced/_testdata/openapi.json
+++ b/_examples/advanced/_testdata/openapi.json
@@ -39,7 +39,7 @@
     },
     "/gzip-pass-through":{
       "get":{
-        "summary":"directGzip","description":"","operationId":"_examples/advanced.directGzip",
+        "summary":"direct Gzip","description":"","operationId":"_examples/advanced.directGzip",
         "parameters":[
           {
             "name":"plainStruct","in":"query","description":"Output plain structure instead of gzip container.",
@@ -58,7 +58,7 @@
         }
       },
       "head":{
-        "summary":"directGzip","description":"","operationId":"_examples/advanced.directGzip",
+        "summary":"direct Gzip","description":"","operationId":"_examples/advanced.directGzip",
         "parameters":[
           {
             "name":"plainStruct","in":"query","description":"Output plain structure instead of gzip container.",
diff --git a/nethttp/openapi_test.go b/nethttp/openapi_test.go
index 3ffbca3..243456a 100644
--- a/nethttp/openapi_test.go
+++ b/nethttp/openapi_test.go
@@ -54,7 +54,10 @@ func TestOpenAPIMiddleware(t *testing.T) {
 	_ = nethttp.WrapHandler(uh,
 		nethttp.OpenAPIMiddleware(&c),
 		nethttp.HTTPBasicSecurityMiddleware(&c, "admin", "Admin Area."),
-		nethttp.HTTPBearerSecurityMiddleware(&c, "api", "API Security.", "JWT"),
+		nethttp.HTTPBearerSecurityMiddleware(&c, "api", "API Security.", "JWT",
+			nethttp.SecurityResponse(new(struct {
+				Error string `json:"error"`
+			}), http.StatusForbidden)),
 		nethttp.HandlerWithRouteMiddleware(http.MethodGet, "/test"),
 	)
 
@@ -77,6 +80,10 @@ func TestOpenAPIMiddleware(t *testing.T) {
 		 "401":{
 		  "description":"Unauthorized",
 		  "content":{"application/json":{"schema":{"$ref":"#/components/schemas/RestErrResponse"}}}
+		 },
+		 "403":{
+		  "description":"Forbidden",
+		  "content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}
 		 }
 		},
 		"security":[{"api":[]},{"admin":[]}]