Repository: swaggest/rest
Pull request #76 (2022-05-08 22:28:22)

Restore original wrapping behavior of Use() as Wrap(), add options to control web.Service creation

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

| Language  | Files |   Lines    |    Code    | Comments  |   Blanks   | Complexity |     Bytes      |
|-----------|-------|------------|------------|-----------|------------|------------|----------------|
| Go        | 98    | 6674 (+48) | 4837 (+28) | 452 (+10) | 1385 (+10) | 775 (+12)  | 161.6K (+1.6K) |
| Go (test) | 43    | 4551 (+46) | 3492 (+24) | 139 (+13) | 920 (+9)   | 119        | 126.7K (+1.6K) |
| Markdown  | 3     | 419 (+7)   | 324 (+6)   | 0         | 95 (+1)    | 0          | 14.7K (+373B)  |
<!-- Sticky Pull Request CommentLOC -->
github-actions[bot]: ### Unit Test Coverage
total:	(statements)	82.9%
<details><summary>Coverage diff with base branch</summary>

```diff
3a4
> github.com/swaggest/rest/chirouter/wrapper.go	Wrap	0.0%
22a24
> github.com/swaggest/rest/chirouter/wrapper.go	prepareHandler	100.0%
115,116c117,118
< github.com/swaggest/rest/request/middleware.go	DecoderMiddleware	100.0%
< github.com/swaggest/rest/request/middleware.go	ValidatorMiddleware	100.0%
---
> github.com/swaggest/rest/request/middleware.go	DecoderMiddleware	90.9%
> github.com/swaggest/rest/request/middleware.go	ValidatorMiddleware	85.7%
```
</details>
<!-- Sticky Pull Request Commentunit-test -->
github-actions[bot]: ### Go API Changes

<pre>
# github.com/swaggest/rest/chirouter
## compatible changes
(*Wrapper).Wrap: added

# github.com/swaggest/rest/web
## incompatible changes
DefaultService: changed from func() *Service to func(...func(s *Service, initialized bool)) *Service
Service: old is comparable, new is not
## compatible changes
(*Wrapper).Wrap: added
Service.PanicRecoveryMiddleware: added

# summary
Inferred base version: v0.2.27
Suggested version: v0.3.0
</pre>
<!-- Sticky Pull Request Commentgorelease -->
codecov[bot]: # [Codecov](https://codecov.io/gh/swaggest/rest/pull/76?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=swaggest) Report
> Merging [#76](https://codecov.io/gh/swaggest/rest/pull/76?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=swaggest) (6dff536) into [master](https://codecov.io/gh/swaggest/rest/commit/35eaff5d128728871fbcd806106e1fbf892ec2cc?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=swaggest) (35eaff5) will **decrease** coverage by `0.81%`.
> The diff coverage is `94.73%`.

```diff
@@            Coverage Diff             @@
##           master      #76      +/-   ##
==========================================
- Coverage   76.98%   76.16%   -0.82%     
==========================================
  Files          27       27              
  Lines        1373     1347      -26     
==========================================
- Hits         1057     1026      -31     
- Misses        204      207       +3     
- Partials      112      114       +2     
```

| Flag | Coverage Δ | |
|---|---|---|
| unittests | `76.16% <94.73%> (-0.82%)` | :arrow_down: |

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.

| [Impacted Files](https://codecov.io/gh/swaggest/rest/pull/76?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=swaggest) | Coverage Δ | |
|---|---|---|
| [chirouter/wrapper.go](https://codecov.io/gh/swaggest/rest/pull/76/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=swaggest#diff-Y2hpcm91dGVyL3dyYXBwZXIuZ28=) | `96.42% <85.71%> (-3.58%)` | :arrow_down: |
| [web/service.go](https://codecov.io/gh/swaggest/rest/pull/76/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=swaggest#diff-d2ViL3NlcnZpY2UuZ28=) | `100.00% <100.00%> (ø)` | |
| [request/middleware.go](https://codecov.io/gh/swaggest/rest/pull/76/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=swaggest#diff-cmVxdWVzdC9taWRkbGV3YXJlLmdv) | `87.17% <0.00%> (-10.26%)` | :arrow_down: |

:mega: Codecov can now indicate which changes are the most critical in Pull Requests. [Learn more](https://about.codecov.io/product/feature/runtime-insights/?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.52µs ± 1%    1.45µs ± 1%  -4.97%  (p=0.016 n=4+5)
pkg:github.com/swaggest/rest/request goos:linux goarch:amd64
Decoder_Decode-2                           805ns ± 1%     779ns ± 2%  -3.19%  (p=0.008 n=5+5)
DecoderFunc_Decode-2                      2.34µs ± 1%    2.26µs ± 2%  -3.35%  (p=0.008 n=5+5)
Decoder_Decode_json-2                     27.1µs ± 2%    25.3µs ± 3%  -6.78%  (p=0.008 n=5+5)
Decoder_Decode_queryObject-2              5.43µs ± 2%    5.20µs ± 2%  -4.26%  (p=0.008 n=5+5)
Decoder_Decode_jsonParam-2                2.07µs ± 1%    2.00µs ± 3%  -3.31%  (p=0.016 n=5+5)
DecoderFactory_SetDecoderFunc-2           1.90µs ± 2%    1.86µs ± 2%  -2.10%  (p=0.032 n=5+5)
pkg:github.com/swaggest/rest/response/gzip goos:linux goarch:amd64
Middleware-2                              13.6µs ± 2%    13.4µs ± 3%    ~     (p=0.421 n=5+5)
Middleware_control-2                      3.87µs ± 2%    3.68µs ± 2%  -5.07%  (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.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.15kB ± 4%    1.18kB ± 1%    ~     (p=0.222 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.45µs ± 1%
pkg:github.com/swaggest/rest/request goos:linux goarch:amd64
Decoder_Decode-2                         779ns ± 2%
DecoderFunc_Decode-2                    2.26µs ± 2%
Decoder_Decode_json-2                   25.3µs ± 3%
Decoder_Decode_queryObject-2            5.20µs ± 2%
Decoder_Decode_jsonParam-2              2.00µs ± 3%
DecoderFactory_SetDecoderFunc-2         1.86µs ± 2%
pkg:github.com/swaggest/rest/response/gzip goos:linux goarch:amd64
Middleware-2                            13.4µs ± 3%
Middleware_control-2                    3.68µ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.18kB ± 1%
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                       old time/op    new time/op    delta
pkg:github.com/swaggest/rest/_examples/advanced goos:linux goarch:amd64
_directGzip-2                23.1µs ± 1%    23.4µs ± 2%  +1.45%  (p=0.032 n=5+5)
_directGzipHead-2            23.5µs ± 4%    23.3µs ± 1%    ~     (p=0.730 n=5+4)
_noDirectGzip-2               127µs ± 1%     126µs ± 1%    ~     (p=0.310 n=5+5)
_directGzip_decode-2          431µs ± 1%     433µs ± 1%    ~     (p=0.690 n=5+5)
_noDirectGzip_decode-2        126µs ± 1%     127µs ± 1%    ~     (p=0.095 n=5+5)
_jsonBody-2                  42.1µs ± 1%    42.3µs ± 1%    ~     (p=0.310 n=5+5)
_jsonBodyValidation-2        48.7µs ± 2%    48.9µs ± 1%    ~     (p=0.222 n=5+5)
_outputHeaders-2             22.6µs ± 1%    22.7µs ± 0%    ~     (p=0.802 n=5+5)
_requestResponseMapping-2    43.8µs ± 2%    42.4µs ± 2%  -3.27%  (p=0.032 n=5+5)
_validation-2                45.3µs ± 2%    45.6µs ± 2%    ~     (p=0.421 n=5+5)
_noValidation-2              32.9µs ± 1%    33.4µs ± 2%  +1.61%  (p=0.008 n=5+5)
pkg:github.com/swaggest/rest/_examples/advanced-generic goos:linux goarch:amd64
_directGzip-2                23.0µs ± 1%    23.6µs ± 1%  +2.56%  (p=0.016 n=4+5)
_directGzipHead-2            23.4µs ± 2%    23.6µs ± 1%    ~     (p=0.548 n=5+5)
_noDirectGzip-2               126µs ± 1%     127µs ± 1%    ~     (p=0.421 n=5+5)
_directGzip_decode-2          430µs ± 0%     430µs ± 1%    ~     (p=0.841 n=5+5)
_noDirectGzip_decode-2        126µs ± 1%     127µs ± 1%    ~     (p=0.056 n=5+5)
_jsonBody-2                  42.5µs ± 1%    42.5µs ± 1%    ~     (p=0.841 n=5+5)
_jsonBodyValidation-2        49.3µs ± 1%    48.6µs ± 1%  -1.41%  (p=0.016 n=5+5)
_outputHeaders-2             22.5µs ± 1%    22.8µs ± 1%    ~     (p=0.222 n=5+5)
_requestResponseMapping-2    42.4µs ± 3%    42.2µs ± 2%    ~     (p=1.000 n=5+5)
_validation-2                45.3µs ± 2%    45.7µs ± 3%    ~     (p=0.548 n=5+5)
_noValidation-2              33.0µs ± 2%    33.5µs ± 2%    ~     (p=0.222 n=5+5)
pkg:github.com/swaggest/rest/_examples/task-api/internal/infra/nethttp goos:linux goarch:amd64
_notFoundSrv-2               27.3µs ±10%    26.5µs ± 1%    ~     (p=0.222 n=5+5)
_ok-2                        27.0µs ± 1%    26.9µs ± 1%    ~     (p=0.690 n=5+5)
_invalidBody-2               37.2µs ± 1%    37.5µs ± 1%    ~     (p=0.056 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)
_requestResponseMapping-2      94.0 ± 0%      94.0 ± 0%    ~     (all equal)
_validation-2                   177 ± 0%       177 ± 0%    ~     (all equal)
_noValidation-2                 177 ± 0%       177 ± 0%    ~     (all equal)
pkg:github.com/swaggest/rest/_examples/advanced-generic 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)
_requestResponseMapping-2      94.0 ± 0%      94.0 ± 0%    ~     (all equal)
_validation-2                   177 ± 0%       177 ± 0%    ~     (all equal)
_noValidation-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)
_requestResponseMapping-2       169 ± 0%       169 ± 0%    ~     (all equal)
_validation-2                   170 ± 0%       170 ± 0%    ~     (all equal)
_noValidation-2                 173 ± 0%       173 ± 0%    ~     (all equal)
pkg:github.com/swaggest/rest/_examples/advanced-generic 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)
_requestResponseMapping-2       169 ± 0%       169 ± 0%    ~     (all equal)
_validation-2                   170 ± 0%       170 ± 0%    ~     (all equal)
_noValidation-2                 173 ± 0%       173 ± 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                 43.3k ± 1%     42.7k ± 2%  -1.42%  (p=0.032 n=5+5)
_directGzipHead-2             42.5k ± 4%     42.9k ± 1%    ~     (p=0.730 n=5+4)
_noDirectGzip-2               7.86k ± 1%     7.91k ± 1%    ~     (p=0.310 n=5+5)
_directGzip_decode-2          2.32k ± 1%     2.31k ± 1%    ~     (p=0.460 n=5+5)
_noDirectGzip_decode-2        7.94k ± 1%     7.90k ± 1%    ~     (p=0.095 n=5+5)
_jsonBody-2                   23.8k ± 1%     23.6k ± 1%    ~     (p=0.310 n=5+5)
_jsonBodyValidation-2         20.5k ± 2%     20.4k ± 1%    ~     (p=0.222 n=5+5)
_outputHeaders-2              44.2k ± 1%     44.1k ± 0%    ~     (p=0.841 n=5+5)
_requestResponseMapping-2     22.8k ± 2%     23.6k ± 2%  +3.37%  (p=0.032 n=5+5)
_validation-2                 22.1k ± 2%     21.9k ± 2%    ~     (p=0.421 n=5+5)
_noValidation-2               30.4k ± 1%     29.9k ± 2%  -1.57%  (p=0.008 n=5+5)
pkg:github.com/swaggest/rest/_examples/advanced-generic goos:linux goarch:amd64
_directGzip-2                 43.5k ± 1%     42.4k ± 1%  -2.49%  (p=0.016 n=4+5)
_directGzipHead-2             42.7k ± 2%     42.4k ± 1%    ~     (p=0.548 n=5+5)
_noDirectGzip-2               7.91k ± 1%     7.89k ± 1%    ~     (p=0.421 n=5+5)
_directGzip_decode-2          2.32k ± 0%     2.33k ± 1%    ~     (p=0.857 n=5+5)
_noDirectGzip_decode-2        7.92k ± 1%     7.86k ± 1%    ~     (p=0.063 n=5+5)
_jsonBody-2                   23.5k ± 1%     23.5k ± 1%    ~     (p=0.841 n=5+5)
_jsonBodyValidation-2         20.3k ± 1%     20.6k ± 1%  +1.42%  (p=0.016 n=5+5)
_outputHeaders-2              44.4k ± 1%     44.0k ± 1%    ~     (p=0.222 n=5+5)
_requestResponseMapping-2     23.6k ± 3%     23.7k ± 2%    ~     (p=1.000 n=5+5)
_validation-2                 22.1k ± 2%     21.9k ± 3%    ~     (p=0.548 n=5+5)
_noValidation-2               30.3k ± 2%     29.9k ± 2%    ~     (p=0.222 n=5+5)
pkg:github.com/swaggest/rest/_examples/task-api/internal/infra/nethttp goos:linux goarch:amd64
_notFoundSrv-2                36.8k ± 9%     37.8k ± 1%    ~     (p=0.222 n=5+5)
_ok-2                         37.1k ± 1%     37.1k ± 1%    ~     (p=0.690 n=5+5)
_invalidBody-2                26.9k ± 1%     26.6k ± 1%    ~     (p=0.056 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.92kB ± 0%    3.92kB ± 0%    ~     (p=0.532 n=5+5)
_directGzipHead-2            3.92kB ± 0%    3.92kB ± 0%    ~     (p=0.238 n=4+5)
_noDirectGzip-2              6.46kB ±18%    6.19kB ±24%    ~     (p=0.690 n=5+5)
_directGzip_decode-2          403kB ± 0%     402kB ± 0%    ~     (p=0.841 n=5+5)
_noDirectGzip_decode-2       5.69kB ± 8%    5.69kB ± 5%    ~     (p=1.000 n=5+5)
_jsonBody-2                  13.2kB ± 0%    13.2kB ± 0%  +0.03%  (p=0.008 n=5+5)
_jsonBodyValidation-2        19.0kB ± 0%    19.0kB ± 0%    ~     (p=0.786 n=5+5)
_outputHeaders-2             3.71kB ± 0%    3.71kB ± 0%    ~     (p=0.079 n=4+5)
_requestResponseMapping-2    16.7kB ± 0%    16.7kB ± 0%    ~     (p=0.825 n=5+5)
_validation-2                16.6kB ± 0%    16.6kB ± 0%    ~     (p=0.397 n=5+5)
_noValidation-2              7.88kB ± 0%    7.88kB ± 0%    ~     (p=0.690 n=5+5)
pkg:github.com/swaggest/rest/_examples/advanced-generic goos:linux goarch:amd64
_directGzip-2                3.94kB ± 0%    3.94kB ± 0%    ~     (p=0.619 n=5+5)
_directGzipHead-2            3.94kB ± 0%    3.94kB ± 0%    ~     (p=0.841 n=5+5)
_noDirectGzip-2              6.26kB ±14%    6.42kB ±22%    ~     (p=0.841 n=5+5)
_directGzip_decode-2          403kB ± 0%     402kB ± 0%    ~     (p=0.421 n=5+5)
_noDirectGzip_decode-2       5.53kB ± 9%    5.61kB ± 6%    ~     (p=1.000 n=5+5)
_jsonBody-2                  13.3kB ± 0%    13.3kB ± 0%    ~     (p=0.524 n=5+5)
_jsonBodyValidation-2        19.1kB ± 0%    19.1kB ± 0%    ~     (p=0.198 n=5+5)
_outputHeaders-2             3.71kB ± 0%    3.71kB ± 0%    ~     (p=0.095 n=5+4)
_requestResponseMapping-2    16.7kB ± 0%    16.7kB ± 0%  -0.03%  (p=0.016 n=4+5)
_validation-2                16.6kB ± 0%    16.6kB ± 0%    ~     (p=0.063 n=5+5)
_noValidation-2              7.91kB ± 0%    7.91kB ± 0%    ~     (p=0.524 n=5+5)
pkg:github.com/swaggest/rest/_examples/task-api/internal/infra/nethttp goos:linux goarch:amd64
_notFoundSrv-2               4.88kB ± 0%    4.88kB ± 0%    ~     (p=1.000 n=5+5)
_ok-2                        4.78kB ± 0%    4.78kB ± 0%    ~     (p=0.486 n=4+4)
_invalidBody-2               8.67kB ± 0%    8.67kB ± 0%    ~     (p=0.629 n=4+4)

name                       old allocs/op  new allocs/op  delta
pkg:github.com/swaggest/rest/_examples/advanced goos:linux goarch:amd64
_directGzip-2                  42.0 ± 0%      42.0 ± 0%    ~     (all equal)
_directGzipHead-2              42.0 ± 0%      42.0 ± 0%    ~     (all equal)
_noDirectGzip-2                49.4 ± 3%      49.4 ± 3%    ~     (p=1.000 n=5+5)
_directGzip_decode-2            501 ± 0%       500 ± 0%    ~     (p=0.333 n=5+4)
_noDirectGzip_decode-2         50.0 ± 0%      50.0 ± 0%    ~     (all equal)
_jsonBody-2                     130 ± 0%       130 ± 0%    ~     (all equal)
_jsonBodyValidation-2           186 ± 0%       186 ± 0%    ~     (all equal)
_outputHeaders-2               37.0 ± 0%      37.0 ± 0%    ~     (all equal)
_requestResponseMapping-2       126 ± 0%       126 ± 0%    ~     (all equal)
_validation-2                   155 ± 0%       155 ± 0%    ~     (all equal)
_noValidation-2                92.0 ± 0%      92.0 ± 0%    ~     (all equal)
pkg:github.com/swaggest/rest/_examples/advanced-generic goos:linux goarch:amd64
_directGzip-2                  43.0 ± 0%      43.0 ± 0%    ~     (all equal)
_directGzipHead-2              43.0 ± 0%      43.0 ± 0%    ~     (all equal)
_noDirectGzip-2                50.4 ± 3%      50.4 ± 3%    ~     (p=1.000 n=5+5)
_directGzip_decode-2            502 ± 0%       501 ± 0%    ~     (p=0.484 n=5+5)
_noDirectGzip_decode-2         51.0 ± 0%      51.0 ± 0%    ~     (all equal)
_jsonBody-2                     131 ± 0%       131 ± 0%    ~     (all equal)
_jsonBodyValidation-2           187 ± 0%       187 ± 0%    ~     (all equal)
_outputHeaders-2               37.0 ± 0%      37.0 ± 0%    ~     (all equal)
_requestResponseMapping-2       127 ± 0%       127 ± 0%    ~     (all equal)
_validation-2                   156 ± 0%       156 ± 0%    ~     (all equal)
_noValidation-2                93.0 ± 0%      93.0 ± 0%    ~     (all equal)
pkg:github.com/swaggest/rest/_examples/task-api/internal/infra/nethttp goos:linux goarch:amd64
_notFoundSrv-2                 54.0 ± 0%      54.0 ± 0%    ~     (all equal)
_ok-2                          50.0 ± 0%      50.0 ± 0%    ~     (all equal)
_invalidBody-2                  100 ± 0%       100 ± 0%    ~     (all equal)
```
</details>
<!-- Sticky Pull Request Commentbench-examples -->

Patch:
diff --git a/README.md b/README.md
index d4a6f54..c8a6f12 100644
--- a/README.md
+++ b/README.md
@@ -314,7 +314,7 @@ import (
 
 	"github.com/swaggest/rest/response/gzip"
 	"github.com/swaggest/rest/web"
-	swgui "github.com/swaggest/swgui/v4"
+	swgui "github.com/swaggest/swgui/v4emb"
 	"github.com/swaggest/usecase"
 	"github.com/swaggest/usecase/status"
 )
@@ -328,7 +328,7 @@ func main() {
 	s.OpenAPI.Info.Version = "v1.2.3"
 
 	// Setup middlewares.
-	s.Use(
+	s.Wrap(
 		gzip.Middleware, // Response compression with support for direct gzip pass through.
 	)
 
@@ -336,6 +336,13 @@ func main() {
 	type helloInput struct {
 		Locale string `query:"locale" default:"en-US" pattern:"^[a-z]{2}-[A-Z]{2}$" enum:"ru-RU,en-US"`
 		Name   string `path:"name" minLength:"3"` // Field tags define parameter location and JSON schema constraints.
+
+		// Field tags of unnamed fields are applied to parent schema.
+		// they are optional and can be used to disallow unknown parameters.
+		// For non-body params, name tag must be provided explicitly.
+		// E.g. here no unknown `query` and `cookie` parameters allowed,
+		// unknown `header` params are ok.
+		_ struct{} `query:"_" cookie:"_" additionalProperties:"false"`
 	}
 
 	// Declare output port type.
diff --git a/_examples/advanced-generic/router.go b/_examples/advanced-generic/router.go
index f6e5650..8b26385 100644
--- a/_examples/advanced-generic/router.go
+++ b/_examples/advanced-generic/router.go
@@ -54,7 +54,7 @@ func NewRouter() http.Handler {
 
 	s.OpenAPICollector.CombineErrors = "anyOf"
 
-	s.Use(
+	s.Wrap(
 		// Response validator setup.
 		//
 		// It might be a good idea to disable this middleware in production to save performance,
diff --git a/_examples/advanced/router.go b/_examples/advanced/router.go
index 901432d..3622d98 100644
--- a/_examples/advanced/router.go
+++ b/_examples/advanced/router.go
@@ -48,7 +48,7 @@ func NewRouter() http.Handler {
 
 	s.OpenAPICollector.CombineErrors = "anyOf"
 
-	s.Use(
+	s.Wrap(
 		// Response validator setup.
 		//
 		// It might be a good idea to disable this middleware in production to save performance,
diff --git a/_examples/basic/main.go b/_examples/basic/main.go
index 240fa7f..95683ba 100644
--- a/_examples/basic/main.go
+++ b/_examples/basic/main.go
@@ -24,7 +24,7 @@ func main() {
 	s.OpenAPI.Info.Version = "v1.2.3"
 
 	// Setup middlewares.
-	s.Use(
+	s.Wrap(
 		gzip.Middleware, // Response compression with support for direct gzip pass through.
 	)
 
diff --git a/_examples/generic/main.go b/_examples/generic/main.go
index f767589..e7c0b56 100644
--- a/_examples/generic/main.go
+++ b/_examples/generic/main.go
@@ -27,7 +27,7 @@ func main() {
 	s.OpenAPI.Info.Version = "v1.2.3"
 
 	// Setup middlewares.
-	s.Use(
+	s.Wrap(
 		gzip.Middleware, // Response compression with support for direct gzip pass through.
 	)
 
diff --git a/_examples/go.mod b/_examples/go.mod
index 262175c..88d6174 100644
--- a/_examples/go.mod
+++ b/_examples/go.mod
@@ -13,7 +13,7 @@ require (
 	github.com/kelseyhightower/envconfig v1.4.0
 	github.com/stretchr/testify v1.7.1
 	github.com/swaggest/assertjson v1.6.8
-	github.com/swaggest/jsonschema-go v0.3.34
+	github.com/swaggest/jsonschema-go v0.3.35
 	github.com/swaggest/openapi-go v0.2.18
 	github.com/swaggest/swgui v1.4.5
 	github.com/swaggest/usecase v1.1.2
diff --git a/_examples/go.sum b/_examples/go.sum
index d27f95b..1be76fa 100644
--- a/_examples/go.sum
+++ b/_examples/go.sum
@@ -81,8 +81,8 @@ github.com/swaggest/assertjson v1.6.8 h1:1O/9UI5M+2OJI7BeEWKGj0wTvpRXZt5FkOJ4nRk
 github.com/swaggest/assertjson v1.6.8/go.mod h1:Euf0upn9Vlaf1/llYHTs+Kx5K3vVbpMbsZhth7zlN7M=
 github.com/swaggest/form/v5 v5.0.1 h1:YQH0REX7iMKhtoVPWXREZgbt50VYXNCKK61psnD8Fgo=
 github.com/swaggest/form/v5 v5.0.1/go.mod h1:vdnaSTze7cxVKhWiCabrfm1YeLwWLpb9P941Gxv4FnA=
-github.com/swaggest/jsonschema-go v0.3.34 h1:f/ErwRNR+Qx/0QTSSIVqAtS9gnHhzBl7IIHO+XKG1GA=
-github.com/swaggest/jsonschema-go v0.3.34/go.mod h1:JAF1nm+uIaMOXktuQepmkiRcgQ5yJk4Ccwx9HVt2cXw=
+github.com/swaggest/jsonschema-go v0.3.35 h1:LW5DC0WgR5YdQXyTRc5e8gLdKT0wkACg4aVJyaseU+4=
+github.com/swaggest/jsonschema-go v0.3.35/go.mod h1:JAF1nm+uIaMOXktuQepmkiRcgQ5yJk4Ccwx9HVt2cXw=
 github.com/swaggest/openapi-go v0.2.18 h1:9dTzNe91MoepI5PtHNUgby3R0ZNgTQte+pDh3X5XcDw=
 github.com/swaggest/openapi-go v0.2.18/go.mod h1:xUrd0cNiIfhkSIFwxmMmiw3FLegY/MQzSek3Byp4YjU=
 github.com/swaggest/refl v1.0.2 h1:VmP8smuDS1EzUPn31++TzMi13CAaVJdlWpIxzj0up88=
diff --git a/_examples/task-api/internal/infra/nethttp/router.go b/_examples/task-api/internal/infra/nethttp/router.go
index 0dfd437..8994e7d 100644
--- a/_examples/task-api/internal/infra/nethttp/router.go
+++ b/_examples/task-api/internal/infra/nethttp/router.go
@@ -29,7 +29,7 @@ func NewRouter(locator *service.Locator) http.Handler {
 
 	r := chirouter.NewWrapper(chi.NewRouter())
 
-	r.Use(
+	r.Wrap(
 		middleware.Recoverer, // Panic recovery.
 		nethttp.UseCaseMiddlewares(log.UseCaseMiddleware()), // Sample use case middleware.
 		nethttp.OpenAPIMiddleware(apiSchema),                // Documentation collector.
@@ -41,7 +41,7 @@ func NewRouter(locator *service.Locator) http.Handler {
 	adminAuth := middleware.BasicAuth("Admin Access", map[string]string{"admin": "admin"})
 	userAuth := middleware.BasicAuth("User Access", map[string]string{"user": "user"})
 
-	r.Use(
+	r.Wrap(
 		middleware.NoCache,
 		middleware.Timeout(time.Second),
 	)
diff --git a/chirouter/wrapper.go b/chirouter/wrapper.go
index 66194de..b101192 100644
--- a/chirouter/wrapper.go
+++ b/chirouter/wrapper.go
@@ -24,6 +24,7 @@ type Wrapper struct {
 	basePattern string
 
 	middlewares []func(http.Handler) http.Handler
+	wraps       []func(http.Handler) http.Handler
 }
 
 var _ chi.Router = &Wrapper{}
@@ -34,9 +35,20 @@ func (r *Wrapper) copy(router chi.Router, pattern string) *Wrapper {
 		name:        r.name,
 		basePattern: r.basePattern + pattern,
 		middlewares: r.middlewares,
+		wraps:       r.wraps,
 	}
 }
 
+// Wrap appends one or more wrappers that will be applied to handler before adding to Router.
+// It is different from middleware in the sense that it is handler-centric, rather than request-centric.
+// Wraps are invoked once for each added handler, they are not invoked for http requests.
+// Wraps can leverage nethttp.HandlerAs to inspect and access deeper layers.
+// For most cases Wrap can be safely used instead of Use, Use is mandatory for middlewares
+// that affect routing (such as middleware.StripSlashes for example).
+func (r *Wrapper) Wrap(wraps ...func(handler http.Handler) http.Handler) {
+	r.wraps = append(r.wraps, wraps...)
+}
+
 // Use appends one of more middlewares onto the Router stack.
 func (r *Wrapper) Use(middlewares ...func(http.Handler) http.Handler) {
 	r.Router.Use(middlewares...)
@@ -77,19 +89,22 @@ func (r *Wrapper) Route(pattern string, fn func(r chi.Router)) chi.Router {
 
 // Mount attaches another http.Handler along "./basePattern/*".
 func (r *Wrapper) Mount(pattern string, h http.Handler) {
-	r.captureHandler("", pattern, h)
+	h = r.prepareHandler("", pattern, h)
+	r.captureHandler(h)
 	r.Router.Mount(pattern, h)
 }
 
 // Handle adds routes for `basePattern` that matches all HTTP methods.
 func (r *Wrapper) Handle(pattern string, h http.Handler) {
-	r.captureHandler("", pattern, h)
+	h = r.prepareHandler("", pattern, h)
+	r.captureHandler(h)
 	r.Router.Handle(pattern, h)
 }
 
 // Method adds routes for `basePattern` that matches the `method` HTTP method.
 func (r *Wrapper) Method(method, pattern string, h http.Handler) {
-	r.captureHandler(method, pattern, h)
+	h = r.prepareHandler(method, pattern, h)
+	r.captureHandler(h)
 	r.Router.Method(method, pattern, h)
 }
 
@@ -147,8 +162,14 @@ func (r *Wrapper) resolvePattern(pattern string) string {
 	return r.basePattern + strings.ReplaceAll(pattern, "/*/", "/")
 }
 
-func (r *Wrapper) captureHandler(method, pattern string, h http.Handler) {
-	mw := r.middlewares
+func (r *Wrapper) captureHandler(h http.Handler) {
+	nethttp.WrapHandler(h, r.middlewares...)
+}
+
+func (r *Wrapper) prepareHandler(method, pattern string, h http.Handler) http.Handler {
+	mw := r.wraps
 	mw = append(mw, nethttp.HandlerWithRouteMiddleware(method, r.resolvePattern(pattern)))
-	nethttp.WrapHandler(h, mw...)
+	h = nethttp.WrapHandler(h, mw...)
+
+	return h
 }
diff --git a/go.mod b/go.mod
index 55aa880..3b07640 100644
--- a/go.mod
+++ b/go.mod
@@ -12,7 +12,7 @@ require (
 	github.com/stretchr/testify v1.7.1
 	github.com/swaggest/assertjson v1.6.8
 	github.com/swaggest/form/v5 v5.0.1
-	github.com/swaggest/jsonschema-go v0.3.34
+	github.com/swaggest/jsonschema-go v0.3.35
 	github.com/swaggest/openapi-go v0.2.18
 	github.com/swaggest/refl v1.0.2
 	github.com/swaggest/usecase v1.1.2
diff --git a/go.sum b/go.sum
index af41678..185931f 100644
--- a/go.sum
+++ b/go.sum
@@ -70,8 +70,9 @@ github.com/swaggest/assertjson v1.6.8 h1:1O/9UI5M+2OJI7BeEWKGj0wTvpRXZt5FkOJ4nRk
 github.com/swaggest/assertjson v1.6.8/go.mod h1:Euf0upn9Vlaf1/llYHTs+Kx5K3vVbpMbsZhth7zlN7M=
 github.com/swaggest/form/v5 v5.0.1 h1:YQH0REX7iMKhtoVPWXREZgbt50VYXNCKK61psnD8Fgo=
 github.com/swaggest/form/v5 v5.0.1/go.mod h1:vdnaSTze7cxVKhWiCabrfm1YeLwWLpb9P941Gxv4FnA=
-github.com/swaggest/jsonschema-go v0.3.34 h1:f/ErwRNR+Qx/0QTSSIVqAtS9gnHhzBl7IIHO+XKG1GA=
 github.com/swaggest/jsonschema-go v0.3.34/go.mod h1:JAF1nm+uIaMOXktuQepmkiRcgQ5yJk4Ccwx9HVt2cXw=
+github.com/swaggest/jsonschema-go v0.3.35 h1:LW5DC0WgR5YdQXyTRc5e8gLdKT0wkACg4aVJyaseU+4=
+github.com/swaggest/jsonschema-go v0.3.35/go.mod h1:JAF1nm+uIaMOXktuQepmkiRcgQ5yJk4Ccwx9HVt2cXw=
 github.com/swaggest/openapi-go v0.2.18 h1:9dTzNe91MoepI5PtHNUgby3R0ZNgTQte+pDh3X5XcDw=
 github.com/swaggest/openapi-go v0.2.18/go.mod h1:xUrd0cNiIfhkSIFwxmMmiw3FLegY/MQzSek3Byp4YjU=
 github.com/swaggest/refl v1.0.2 h1:VmP8smuDS1EzUPn31++TzMi13CAaVJdlWpIxzj0up88=
diff --git a/web/service.go b/web/service.go
index 36316d2..45c0406 100644
--- a/web/service.go
+++ b/web/service.go
@@ -19,33 +19,55 @@ import (
 )
 
 // DefaultService initializes router and other basic components of web service.
-func DefaultService() *Service {
+//
+// Provided functional options are invoked twice, before and after initialization.
+func DefaultService(options ...func(s *Service, initialized bool)) *Service {
+	s := Service{}
+
+	for _, option := range options {
+		option(&s, false)
+	}
+
+	if s.OpenAPI == nil {
+		s.OpenAPI = &openapi3.Spec{Openapi: "3.0.3"}
+	}
+
 	// Init API documentation schema.
-	apiSchema := &openapi.Collector{}
+	if s.OpenAPICollector == nil {
+		s.OpenAPICollector = &openapi.Collector{}
+		s.OpenAPICollector.Reflector().Spec = s.OpenAPI
+	}
 
-	// Setup request decoder and validator.
-	validatorFactory := jsonschema.NewFactory(apiSchema, apiSchema)
-	decoderFactory := request.NewDecoderFactory()
-	decoderFactory.ApplyDefaults = true
-	decoderFactory.SetDecoderFunc(rest.ParamInPath, chirouter.PathToURLValues)
+	if s.Wrapper == nil {
+		s.Wrapper = chirouter.NewWrapper(chi.NewRouter())
+	}
+
+	if s.DecoderFactory == nil {
+		decoderFactory := request.NewDecoderFactory()
+		decoderFactory.ApplyDefaults = true
+		decoderFactory.SetDecoderFunc(rest.ParamInPath, chirouter.PathToURLValues)
+
+		s.DecoderFactory = decoderFactory
+	}
 
-	router := chirouter.NewWrapper(chi.NewRouter())
+	validatorFactory := jsonschema.NewFactory(s.OpenAPICollector, s.OpenAPICollector)
+	s.ResponseValidatorFactory = validatorFactory
+
+	if s.PanicRecoveryMiddleware == nil {
+		s.PanicRecoveryMiddleware = middleware.Recoverer
+	}
 
 	// Setup middlewares.
-	router.Use(
-		middleware.Recoverer,                          // Panic recovery.
-		nethttp.OpenAPIMiddleware(apiSchema),          // Documentation collector.
-		request.DecoderMiddleware(decoderFactory),     // Request decoder setup.
+	s.Wrapper.Wrap(
+		s.PanicRecoveryMiddleware,                     // Panic recovery.
+		nethttp.OpenAPIMiddleware(s.OpenAPICollector), // Documentation collector.
+		request.DecoderMiddleware(s.DecoderFactory),   // Request decoder setup.
 		request.ValidatorMiddleware(validatorFactory), // Request validator setup.
 		response.EncoderMiddleware,                    // Response encoder setup.
 	)
 
-	s := Service{
-		OpenAPI:                  apiSchema.Reflector().SpecEns(),
-		OpenAPICollector:         apiSchema,
-		Wrapper:                  router,
-		DecoderFactory:           decoderFactory,
-		ResponseValidatorFactory: validatorFactory,
+	for _, option := range options {
+		option(&s, true)
 	}
 
 	return &s
@@ -55,9 +77,14 @@ func DefaultService() *Service {
 type Service struct {
 	*chirouter.Wrapper
 
-	OpenAPI                  *openapi3.Spec
-	OpenAPICollector         *openapi.Collector
-	DecoderFactory           *request.DecoderFactory
+	PanicRecoveryMiddleware func(handler http.Handler) http.Handler // Default is middleware.Recoverer.
+	OpenAPI                 *openapi3.Spec
+	OpenAPICollector        *openapi.Collector
+	DecoderFactory          *request.DecoderFactory
+
+	// Response validation is not enabled by default for its less justifiable performance impact.
+	// This field is populated so that response.ValidatorMiddleware(s.ResponseValidatorFactory) can be
+	// added to service via Wrap.
 	ResponseValidatorFactory rest.ResponseValidatorFactory
 }
 


Test patch:
diff --git a/chirouter/wrapper_test.go b/chirouter/wrapper_test.go
index e4f02a7..01bf740 100644
--- a/chirouter/wrapper_test.go
+++ b/chirouter/wrapper_test.go
@@ -117,7 +117,7 @@ func TestNewWrapper(t *testing.T) {
 		assert.Equal(t, "foobar", rw.Body.String(), u)
 	}
 
-	assert.Equal(t, 13, handlersCnt)
+	assert.Equal(t, 14, handlersCnt)
 	assert.Equal(t, 20, totalCnt)
 }
 
@@ -183,9 +183,34 @@ func TestWrapper_Use_precedence(t *testing.T) {
 	}, log)
 }
 
+// This test covers original behavior discrepancy between wrapper and router
+// in how middlewares are applied.
+// Router runs middlewares for every request that comes in before route matching,
+// and so middlewares like StripSlashes can affect route matching in a useful way.
+// Wrapper in contrast was creating a new handler by running middlewares during handler
+// registration, and then adding prepared handler to router.
+// In this case middlewares were "baked-in" the handler and so, were running only
+// after route match.
+// For the use case of StripSlashes that would result in not found, because middleware was
+// invoked AFTER route matching, not BEFORE.
+
+// Solution to this problem was passing middlewares to Router as is, the problem however is
+// that Router does not allow unwrapping handlers (that is the purpose of Wrapper) to introspect
+// or augment handlers.
 func TestWrapper_Use_StripSlashes(t *testing.T) {
 	var log []string
 
+	r := chi.NewRouter()
+	r.Use(
+		middleware.StripSlashes,
+
+		func(handler http.Handler) http.Handler {
+			return http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
+				handler.ServeHTTP(writer, request)
+			})
+		},
+	)
+
 	// Wrapped chi router.
 	wr := chirouter.NewWrapper(chi.NewRouter())
 	wr.Use(
@@ -211,6 +236,14 @@ func TestWrapper_Use_StripSlashes(t *testing.T) {
 		log = append(log, "h")
 	})
 
+	r.Method(http.MethodGet, "/foo", h)
+	r.ServeHTTP(rw, req)
+
+	assert.Equal(t, http.StatusOK, rw.Code)
+	assert.Equal(t, "OK", rw.Body.String())
+
+	rw = httptest.NewRecorder()
+
 	wr.Method(http.MethodGet, "/foo", h)
 	wr.ServeHTTP(rw, req)
 
@@ -218,6 +251,6 @@ func TestWrapper_Use_StripSlashes(t *testing.T) {
 	assert.Equal(t, "OK", rw.Body.String())
 
 	assert.Equal(t, []string{
-		"h",
+		"h", "h",
 	}, log)
 }
diff --git a/nethttp/example_test.go b/nethttp/example_test.go
index 18e0328..531eb0f 100644
--- a/nethttp/example_test.go
+++ b/nethttp/example_test.go
@@ -20,7 +20,7 @@ func ExampleSecurityMiddleware() {
 	apiSchema := &openapi.Collector{}
 
 	// Setup middlewares (non-documentary middlewares omitted for brevity).
-	r.Use(
+	r.Wrap(
 		nethttp.OpenAPIMiddleware(apiSchema), // Documentation collector.
 	)
 
diff --git a/request/file_test.go b/request/file_test.go
index 68263ff..ec7417f 100644
--- a/request/file_test.go
+++ b/request/file_test.go
@@ -40,7 +40,7 @@ func TestMapper_Decode_fileUploadTag(t *testing.T) {
 
 	decoderFactory.SetDecoderFunc(rest.ParamInPath, chirouter.PathToURLValues)
 
-	r.Use(
+	r.Wrap(
 		nethttp.OpenAPIMiddleware(&apiSchema),
 		request.DecoderMiddleware(decoderFactory),
 		request.ValidatorMiddleware(validatorFactory),
diff --git a/web/service_test.go b/web/service_test.go
index 3342ca4..d8ad725 100644
--- a/web/service_test.go
+++ b/web/service_test.go
@@ -2,6 +2,7 @@ package web_test
 
 import (
 	"context"
+	"fmt"
 	"io/ioutil"
 	"net/http"
 	"net/http/httptest"
@@ -30,7 +31,16 @@ func albumByID() usecase.Interactor {
 }
 
 func TestDefaultService(t *testing.T) {
-	service := web.DefaultService()
+	var l []string
+
+	service := web.DefaultService(
+		func(s *web.Service, initialized bool) {
+			l = append(l, fmt.Sprintf("one:%v", initialized))
+		},
+		func(s *web.Service, initialized bool) {
+			l = append(l, fmt.Sprintf("two:%v", initialized))
+		},
+	)
 
 	service.OpenAPI.Info.Title = "Albums API"
 	service.OpenAPI.Info.WithDescription("This service provides API to manage albums.")
@@ -60,4 +70,6 @@ func TestDefaultService(t *testing.T) {
 	expected, err := ioutil.ReadFile("_testdata/openapi.json")
 	require.NoError(t, err)
 	assertjson.EqualMarshal(t, expected, service.OpenAPI)
+
+	assert.Equal(t, []string{"one:false", "two:false", "one:true", "two:true"}, l)
 }