├── .gitignore ├── LICENSE ├── README.md ├── RELEASE ├── RunAllTests.sh ├── RunCodeCheck.sh ├── RunGoDoc.sh ├── go.mod ├── go.sum ├── spring-web-const.go ├── spring-web-container.go ├── spring-web-context.go ├── spring-web-filter.go ├── spring-web-mapper.go ├── spring-web-mapper_test.go ├── spring-web-mapping.go ├── spring-web-method.go ├── spring-web-method_test.go ├── spring-web-middleware.go ├── spring-web-redoc.go ├── spring-web-router.go ├── spring-web-rpc.go ├── spring-web-server.go ├── spring-web-swagger.go ├── spring-web-swagger_test.go ├── spring-web-url.go ├── spring-web-url_test.go └── spring-web-validator.go /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, built with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | 14 | .DS_Store 15 | 16 | .idea/ 17 | vendor/ 18 | 19 | covprofile 20 | coverage.html -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # go-spring-web -------------------------------------------------------------------------------- /RELEASE: -------------------------------------------------------------------------------- 1 | Release History: 2 | 3 | v1.0.4 2020-06-23 4 | 5 | Handler 提升为接口,打印更丰富的路由信息;FilterChain 提升为接口,完美适 6 | 配 echo 和 gin 的中间件机制;全面统一 echo、gin 以及 {} 路由风格;全面实 7 | 现 WebServer、WebContainer、Router、Mapper 四个级别的 Filter 机制; 8 | WebContainer 增加 ReadTimeout 和 WriteTimeout 配置;BIND 模式支持 9 | WebContext 参数,支持无参和无返回值,RPC 处理函数可定制;增加 gin、echo 10 | 与 WebContext 之间的互转函数;改造 WebServer 流式接口;优化 Swagger 11 | 使用;减小 WebContainer 方法集;引入参数校验框架;修复 gin 路由的 BUG; 12 | 增加 WebContext.SetRequest 函数,等等。 13 | 14 | v1.0.3 2020-04-14 15 | 16 | 初步实现 Swagger 框架,集成 Swagger-UI 和 ReDoc,增加 Swg Petstore 17 | 官方示例,统一 Spring-Web 的路由规则,和 echo 保持一致,对gin 进行适配, 18 | 支持 {} 路由风格,等等。 19 | 20 | v1.0.2 2020-03-24 21 | 22 | 重新设计 Request 接口,支持方法组合,支持 Container 级别的 Filter 配置, 23 | 适配 echo 和 gin 原生 handler,增加封装 Bind 操作的 Web RPC 适配函数。 24 | 25 | v1.0.0 2020-02-22 26 | 27 | 适配 echo、gin、http 标准框架函数,更新 gin 和 echo 版本,修复少量 BUG。 28 | 29 | v1.0.0-rc 2020-01-04 30 | 31 | 初步搭建 SpringWeb 框架,适配 echo 和 gin 两个主流 http web 框架,适配 32 | LoggerContext 接口,初步实现 Filter 机制,增加 RPC 适配函数,等等。 -------------------------------------------------------------------------------- /RunAllTests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 执行当前目录及子目录下的测试用例 4 | go test -count=1 ./... -------------------------------------------------------------------------------- /RunCodeCheck.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # https://github.com/golangci/golangci-lint 4 | golangci-lint run -------------------------------------------------------------------------------- /RunGoDoc.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | WORKSPACE=$(cd `dirname $0` && pwd -P) 4 | 5 | # NOTE: 只需要修改这里 6 | MODULE_NAME=go-spring-web 7 | 8 | PACKAGE_PATH=github.com/go-spring 9 | export GOPATH=/tmp/godoc-${MODULE_NAME} 10 | MODULE_PATH=${GOPATH}/src/${PACKAGE_PATH} 11 | 12 | rm -rf $MODULE_PATH/$MODULE_NAME &> /dev/null 13 | 14 | mkdir -p $GOPATH/bin 15 | mkdir -p $MODULE_PATH 16 | 17 | ln -sf $WORKSPACE ${MODULE_PATH}/${MODULE_NAME} 18 | 19 | cd $MODULE_PATH/$MODULE_NAME 20 | 21 | python -m webbrowser "http://localhost:6060/pkg/github.com/go-spring/"${MODULE_NAME} 22 | godoc -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/go-spring/go-spring-web 2 | 3 | go 1.12 4 | 5 | require ( 6 | github.com/go-openapi/spec v0.19.7 7 | github.com/go-playground/validator/v10 v10.2.0 8 | github.com/go-spring/go-spring-error v0.0.0-20200808141421-a3d4acfeb21b 9 | github.com/go-spring/go-spring-logger v0.0.0-20200808141042-f30429918b41 10 | github.com/go-spring/go-spring-test v0.0.0-20200808140725-779b0a9e7a93 11 | github.com/go-spring/go-spring-utils v0.0.0-20200808140357-119aa665f28a 12 | github.com/magiconair/properties v1.8.1 13 | github.com/swaggo/http-swagger v0.0.0-20200308142732-58ac5e232fba 14 | ) 15 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc= 2 | github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE= 3 | github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= 4 | github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= 5 | github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= 6 | github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= 7 | github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= 8 | github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= 9 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 10 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 11 | github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= 12 | github.com/gin-contrib/gzip v0.0.1/go.mod h1:fGBJBCdt6qCZuCAOwWuFhBB4OOq9EFqlo5dEaFhhu5w= 13 | github.com/gin-contrib/sse v0.0.0-20170109093832-22d885f9ecc7/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s= 14 | github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s= 15 | github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= 16 | github.com/gin-gonic/gin v1.3.0/go.mod h1:7cKuhb5qV2ggCFctp2fJQ+ErvciLZrIeoOSOm6mUr7Y= 17 | github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM= 18 | github.com/go-chi/chi v4.0.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ= 19 | github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= 20 | github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= 21 | github.com/go-openapi/jsonpointer v0.19.3 h1:gihV7YNZK1iK6Tgwwsxo2rJbD1GTbdm72325Bq8FI3w= 22 | github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= 23 | github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= 24 | github.com/go-openapi/jsonreference v0.19.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= 25 | github.com/go-openapi/jsonreference v0.19.2 h1:o20suLFB4Ri0tuzpWtyHlh7E7HnkqTNLq6aR6WVNS1w= 26 | github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= 27 | github.com/go-openapi/spec v0.19.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= 28 | github.com/go-openapi/spec v0.19.7 h1:0xWSeMd35y5avQAThZR2PkEuqSosoS5t6gDH4L8n11M= 29 | github.com/go-openapi/spec v0.19.7/go.mod h1:Hm2Jr4jv8G1ciIAo+frC/Ft+rR2kQDh8JHKHb3gWUSk= 30 | github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= 31 | github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= 32 | github.com/go-openapi/swag v0.19.5 h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tFY= 33 | github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= 34 | github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= 35 | github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= 36 | github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= 37 | github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no= 38 | github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= 39 | github.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY= 40 | github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= 41 | github.com/go-spring/go-spring-error v0.0.0-20200808141421-a3d4acfeb21b h1:dTTOxfYreLhVwG/ElOqbh9+Ar7bEgX1k7Y2yI0tcEOo= 42 | github.com/go-spring/go-spring-error v0.0.0-20200808141421-a3d4acfeb21b/go.mod h1:oboMN9paPF3H1aBmqE9M9+nIPUwOQpV2pPHyYLxHeXo= 43 | github.com/go-spring/go-spring-logger v0.0.0-20200808141042-f30429918b41 h1:3WcExndhdB23bhVCK93w9HzJjUdMTcNQ154O3YA7KJU= 44 | github.com/go-spring/go-spring-logger v0.0.0-20200808141042-f30429918b41/go.mod h1:KqmG4T8cdcm5QZkzYLqFiyx3eM4nXWNp00MhlIWMX2w= 45 | github.com/go-spring/go-spring-test v0.0.0-20200808140725-779b0a9e7a93 h1:g83LMBIxm9VuRRRa/BsOME6JgwwUZEzbu/480JiMRfU= 46 | github.com/go-spring/go-spring-test v0.0.0-20200808140725-779b0a9e7a93/go.mod h1:l5dMD3AuWs4vvcecUcAB4hba87cr6xeXUe/K1HNp39I= 47 | github.com/go-spring/go-spring-utils v0.0.0-20200808140357-119aa665f28a h1:+vBaoWmFA9tNxLFtaKwbWyDongOUxhNwUExG63wthr8= 48 | github.com/go-spring/go-spring-utils v0.0.0-20200808140357-119aa665f28a/go.mod h1:oPtJeTil+appmHgfKE9n4qQxvbwTwYpClLpkkSRgFQY= 49 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 50 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 51 | github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= 52 | github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= 53 | github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= 54 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 55 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 56 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 57 | github.com/labstack/gommon v0.3.0 h1:JEeO0bvc78PKdyHxloTKiF8BD5iGrH8T6MSeGvSgob0= 58 | github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= 59 | github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= 60 | github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= 61 | github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4= 62 | github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= 63 | github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= 64 | github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= 65 | github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e h1:hB2xlXdHp/pmPZq0y3QnmWAArdw9PqbmotexnWx/FU8= 66 | github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= 67 | github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU= 68 | github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= 69 | github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= 70 | github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= 71 | github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= 72 | github.com/mattn/go-isatty v0.0.9 h1:d5US/mDsogSGW37IV293h//ZFaeajb69h+EHFsv2xGg= 73 | github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= 74 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 75 | github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 76 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 77 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 78 | github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng= 79 | github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= 80 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 81 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 82 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 83 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 84 | github.com/swaggo/files v0.0.0-20190704085106-630677cd5c14 h1:PyYN9JH5jY9j6av01SpfRMb+1DWg/i3MbGOKPxJ2wjM= 85 | github.com/swaggo/files v0.0.0-20190704085106-630677cd5c14/go.mod h1:gxQT6pBGRuIGunNf/+tSOB5OHvguWi8Tbt82WOkf35E= 86 | github.com/swaggo/gin-swagger v1.2.0/go.mod h1:qlH2+W7zXGZkczuL+r2nEBR2JTT+/lX05Nn6vPhc7OI= 87 | github.com/swaggo/http-swagger v0.0.0-20200308142732-58ac5e232fba h1:lUPlXKqgbqT2SVg2Y+eT9mu5wbqMnG+i/+Q9nK7C0Rs= 88 | github.com/swaggo/http-swagger v0.0.0-20200308142732-58ac5e232fba/go.mod h1:O1lAbCgAAX/KZ80LM/OXwtWFI/5TvZlwxSg8Cq08PV0= 89 | github.com/swaggo/swag v1.5.1/go.mod h1:1Bl9F/ZBpVWh22nY0zmYyASPO1lI/zIwRDrpZU+tv8Y= 90 | github.com/swaggo/swag v1.6.3 h1:N+uVPGP4H2hXoss2pt5dctoSUPKKRInr6qcTMOm0usI= 91 | github.com/swaggo/swag v1.6.3/go.mod h1:wcc83tB4Mb2aNiL/HP4MFeQdpHUrca+Rp/DRNgWAUio= 92 | github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= 93 | github.com/ugorji/go v1.1.5-pre/go.mod h1:FwP/aQVg39TXzItUBMwnWp9T9gPQnXw4Poh4/oBQZ/0= 94 | github.com/ugorji/go/codec v0.0.0-20181022190402-e5e69e061d4f/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= 95 | github.com/ugorji/go/codec v1.1.5-pre/go.mod h1:tULtS6Gy1AE1yCENaw4Vb//HLH5njI2tfCQDUqRd8fI= 96 | github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= 97 | github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= 98 | github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= 99 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 100 | golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 101 | golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 102 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 103 | golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 104 | golang.org/x/net v0.0.0-20190611141213-3f473d35a33a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 105 | golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 106 | golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297 h1:k7pJ2yAPLPgbskkFdhRCsA77k2fySZ1zf2zCjvQCiIM= 107 | golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 108 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 109 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 110 | golang.org/x/sys v0.0.0-20181228144115-9a3f9b0469bb/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 111 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 112 | golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 113 | golang.org/x/sys v0.0.0-20190610200419-93c9922d18ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 114 | golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a h1:aYOabOQFp6Vj6W1F80affTUvO9UxmJRx8K0gsfABByQ= 115 | golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 116 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 117 | golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= 118 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 119 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 120 | golang.org/x/tools v0.0.0-20190606050223-4d9ae51c2468/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 121 | golang.org/x/tools v0.0.0-20190611222205-d73e1c7e250b h1:/mJ+GKieZA6hFDQGdWZrjj4AXPl5ylY+5HusG80roy0= 122 | golang.org/x/tools v0.0.0-20190611222205-d73e1c7e250b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 123 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 124 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 125 | gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= 126 | gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y= 127 | gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 128 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 129 | gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= 130 | gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 131 | -------------------------------------------------------------------------------- /spring-web-const.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2019 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package SpringWeb 18 | 19 | const ( 20 | HeaderContentDisposition = "Content-Disposition" 21 | HeaderContentType = "Content-Type" 22 | HeaderXForwardedProto = "X-Forwarded-Proto" 23 | HeaderXForwardedProtocol = "X-Forwarded-Protocol" 24 | HeaderXForwardedSsl = "X-Forwarded-Ssl" 25 | HeaderXUrlScheme = "X-Url-Scheme" 26 | 27 | CharsetUTF8 = "charset=UTF-8" 28 | 29 | MIMEApplicationJSON = "application/json" 30 | MIMEApplicationJSONCharsetUTF8 = MIMEApplicationJSON + "; " + CharsetUTF8 31 | MIMEApplicationJavaScript = "application/javascript" 32 | MIMEApplicationJavaScriptCharsetUTF8 = MIMEApplicationJavaScript + "; " + CharsetUTF8 33 | MIMEApplicationXML = "application/xml" 34 | MIMEApplicationXMLCharsetUTF8 = MIMEApplicationXML + "; " + CharsetUTF8 35 | MIMETextXML = "text/xml" 36 | MIMETextXMLCharsetUTF8 = MIMETextXML + "; " + CharsetUTF8 37 | MIMEApplicationForm = "application/x-www-form-urlencoded" 38 | MIMEApplicationProtobuf = "application/protobuf" 39 | MIMEApplicationMsgpack = "application/msgpack" 40 | MIMETextHTML = "text/html" 41 | MIMETextHTMLCharsetUTF8 = MIMETextHTML + "; " + CharsetUTF8 42 | MIMETextPlain = "text/plain" 43 | MIMETextPlainCharsetUTF8 = MIMETextPlain + "; " + CharsetUTF8 44 | MIMEMultipartForm = "multipart/form-data" 45 | MIMEOctetStream = "application/octet-stream" 46 | MIMEJsonAPI = "application/vnd.api+json" 47 | MIMEJsonStream = "application/x-json-stream" 48 | MIMEImagePng = "image/png" 49 | MIMEImageJpeg = "image/jpeg" 50 | MIMEImageGif = "image/gif" 51 | ) 52 | -------------------------------------------------------------------------------- /spring-web-container.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2019 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package SpringWeb 18 | 19 | import ( 20 | "context" 21 | "fmt" 22 | "net/http" 23 | "time" 24 | 25 | "github.com/go-spring/go-spring-logger" 26 | "github.com/go-spring/go-spring-utils" 27 | "github.com/swaggo/http-swagger" 28 | ) 29 | 30 | // HandlerFunc 标准 Web 处理函数 31 | type HandlerFunc func(WebContext) 32 | 33 | // Handler Web 处理接口 34 | type Handler interface { 35 | // Invoke 响应函数 36 | Invoke(WebContext) 37 | 38 | // FileLine 获取用户函数的文件名、行号以及函数名称 39 | FileLine() (file string, line int, fnName string) 40 | } 41 | 42 | // ContainerConfig Web 容器配置 43 | type ContainerConfig struct { 44 | IP string // 监听 IP 45 | Port int // 监听端口 46 | EnableSSL bool // 使用 SSL 47 | KeyFile string // SSL 证书 48 | CertFile string // SSL 秘钥 49 | 50 | ReadTimeout time.Duration 51 | WriteTimeout time.Duration 52 | } 53 | 54 | // WebContainer Web 容器 55 | type WebContainer interface { 56 | // WebMapping 路由表 57 | WebMapping 58 | 59 | // Config 获取 Web 容器配置 60 | Config() ContainerConfig 61 | 62 | // GetFilters 返回过滤器列表 63 | GetFilters() []Filter 64 | 65 | // ResetFilters 重新设置过滤器列表 66 | ResetFilters(filters []Filter) 67 | 68 | // AddFilter 添加过滤器 69 | AddFilter(filter ...Filter) 70 | 71 | // GetLoggerFilter 获取 Logger Filter 72 | GetLoggerFilter() Filter 73 | 74 | // SetLoggerFilter 设置 Logger Filter 75 | SetLoggerFilter(filter Filter) 76 | 77 | // GetRecoveryFilter 获取 Recovery Filter 78 | GetRecoveryFilter() Filter 79 | 80 | // SetRecoveryFilter 设置 Recovery Filter 81 | SetRecoveryFilter(filter Filter) 82 | 83 | // AddRouter 添加新的路由信息 84 | AddRouter(router *Router) 85 | 86 | // EnableSwagger 是否启用 Swagger 功能 87 | EnableSwagger() bool 88 | 89 | // SetEnableSwagger 设置是否启用 Swagger 功能 90 | SetEnableSwagger(enable bool) 91 | 92 | // Swagger 返回和容器绑定的 Swagger 对象 93 | Swagger() *Swagger 94 | 95 | // Start 启动 Web 容器,非阻塞 96 | Start() 97 | 98 | // Stop 停止 Web 容器,阻塞 99 | Stop(ctx context.Context) 100 | } 101 | 102 | // BaseWebContainer WebContainer 的通用部分 103 | type BaseWebContainer struct { 104 | WebMapping 105 | 106 | config ContainerConfig 107 | 108 | enableSwag bool // 是否启用 Swagger 功能 109 | swagger *Swagger // 和容器绑定的 Swagger 对象 110 | 111 | filters []Filter // 其他过滤器 112 | loggerFilter Filter // 日志过滤器 113 | recoveryFilter Filter // 恢复过滤器 114 | } 115 | 116 | // NewBaseWebContainer BaseWebContainer 的构造函数 117 | func NewBaseWebContainer(config ContainerConfig) *BaseWebContainer { 118 | return &BaseWebContainer{ 119 | WebMapping: NewDefaultWebMapping(), 120 | config: config, 121 | enableSwag: true, 122 | loggerFilter: defaultLoggerFilter, 123 | recoveryFilter: defaultRecoveryFilter, 124 | } 125 | } 126 | 127 | // Address 返回监听地址 128 | func (c *BaseWebContainer) Address() string { 129 | return fmt.Sprintf("%s:%d", c.config.IP, c.config.Port) 130 | } 131 | 132 | // Config 获取 Web 容器配置 133 | func (c *BaseWebContainer) Config() ContainerConfig { 134 | return c.config 135 | } 136 | 137 | // GetFilters 返回过滤器列表 138 | func (c *BaseWebContainer) GetFilters() []Filter { 139 | return c.filters 140 | } 141 | 142 | // ResetFilters 重新设置过滤器列表 143 | func (c *BaseWebContainer) ResetFilters(filters []Filter) { 144 | c.filters = filters 145 | } 146 | 147 | // AddFilter 添加过滤器 148 | func (c *BaseWebContainer) AddFilter(filter ...Filter) { 149 | c.filters = append(c.filters, filter...) 150 | } 151 | 152 | // GetLoggerFilter 获取 Logger Filter 153 | func (c *BaseWebContainer) GetLoggerFilter() Filter { 154 | return c.loggerFilter 155 | } 156 | 157 | // SetLoggerFilter 设置 Logger Filter 158 | func (c *BaseWebContainer) SetLoggerFilter(filter Filter) { 159 | c.loggerFilter = filter 160 | } 161 | 162 | // GetRecoveryFilter 获取 Recovery Filter 163 | func (c *BaseWebContainer) GetRecoveryFilter() Filter { 164 | return c.recoveryFilter 165 | } 166 | 167 | // 设置 Recovery Filter 168 | func (c *BaseWebContainer) SetRecoveryFilter(filter Filter) { 169 | c.recoveryFilter = filter 170 | } 171 | 172 | // AddRouter 添加新的路由信息 173 | func (c *BaseWebContainer) AddRouter(router *Router) { 174 | for _, mapper := range router.mapping.Mappers() { 175 | c.AddMapper(mapper) 176 | } 177 | } 178 | 179 | // EnableSwagger 是否启用 Swagger 功能 180 | func (c *BaseWebContainer) EnableSwagger() bool { 181 | return c.enableSwag 182 | } 183 | 184 | // SetEnableSwagger 设置是否启用 Swagger 功能 185 | func (c *BaseWebContainer) SetEnableSwagger(enable bool) { 186 | c.enableSwag = enable 187 | } 188 | 189 | // Swagger 返回和容器绑定的 Swagger 对象 190 | func (c *BaseWebContainer) Swagger() *Swagger { 191 | if c.swagger == nil { 192 | c.swagger = NewSwagger() 193 | } 194 | return c.swagger 195 | } 196 | 197 | // PreStart 执行 Start 之前的准备工作 198 | func (c *BaseWebContainer) PreStart() { 199 | 200 | if c.enableSwag && c.swagger != nil { 201 | 202 | // 注册 path 的 Operation 203 | for _, mapper := range c.Mappers() { 204 | if op := mapper.swagger; op != nil { 205 | if err := op.parseBind(); err != nil { 206 | panic(err) 207 | } 208 | c.swagger.AddPath(mapper.Path(), mapper.Method(), op) 209 | } 210 | } 211 | 212 | doc := c.swagger.ReadDoc() 213 | hSwagger := httpSwagger.Handler(httpSwagger.URL("/swagger/doc.json")) 214 | 215 | // 注册 swagger-ui 和 doc.json 接口 216 | c.GetMapping("/swagger/*", func(webCtx WebContext) { 217 | if webCtx.PathParam("*") == "doc.json" { 218 | webCtx.Header(HeaderContentType, MIMEApplicationJSONCharsetUTF8) 219 | webCtx.String(http.StatusOK, doc) 220 | } else { 221 | hSwagger(webCtx.ResponseWriter(), webCtx.Request()) 222 | } 223 | }) 224 | 225 | // 注册 redoc 接口 226 | c.GetMapping("/redoc", ReDoc) 227 | } 228 | 229 | } 230 | 231 | // PrintMapper 打印路由注册信息 232 | func (c *BaseWebContainer) PrintMapper(m *Mapper) { 233 | file, line, fnName := m.handler.FileLine() 234 | SpringLogger.Infof("%v :%d %s -> %s:%d %s", GetMethod(m.method), c.config.Port, m.path, file, line, fnName) 235 | } 236 | 237 | /////////////////// Invoke Handler ////////////////////// 238 | 239 | // InvokeHandler 执行 Web 处理函数 240 | func InvokeHandler(ctx WebContext, fn Handler, filters []Filter) { 241 | if len(filters) > 0 { 242 | filters = append(filters, HandlerFilter(fn)) 243 | chain := NewDefaultFilterChain(filters) 244 | chain.Next(ctx) 245 | } else { 246 | fn.Invoke(ctx) 247 | } 248 | } 249 | 250 | /////////////////// Web Handlers ////////////////////// 251 | 252 | // fnHandler 封装 Web 处理函数 253 | type fnHandler HandlerFunc 254 | 255 | func (f fnHandler) Invoke(ctx WebContext) { 256 | f(ctx) 257 | } 258 | 259 | func (f fnHandler) FileLine() (file string, line int, fnName string) { 260 | return SpringUtils.FileLine(f) 261 | } 262 | 263 | // FUNC 标准 Web 处理函数的辅助函数 264 | func FUNC(fn HandlerFunc) Handler { 265 | return fnHandler(fn) 266 | } 267 | 268 | // httpHandler 标准 Http 处理函数 269 | type httpHandler http.HandlerFunc 270 | 271 | func (h httpHandler) Invoke(ctx WebContext) { 272 | h(ctx.ResponseWriter(), ctx.Request()) 273 | } 274 | 275 | func (h httpHandler) FileLine() (file string, line int, fnName string) { 276 | return SpringUtils.FileLine(h) 277 | } 278 | 279 | // HTTP 标准 Http 处理函数的辅助函数 280 | func HTTP(fn http.HandlerFunc) Handler { 281 | return httpHandler(fn) 282 | } 283 | 284 | // WrapF 标准 Http 处理函数的辅助函数,兼容 gin 写法 285 | func WrapF(fn http.HandlerFunc) Handler { 286 | return httpHandler(fn) 287 | } 288 | 289 | // WrapH 标准 Http 处理函数的辅助函数,兼容 gin 写法 290 | func WrapH(h http.Handler) Handler { 291 | return httpHandler(h.ServeHTTP) 292 | } 293 | 294 | /////////////////// Web Filters ////////////////////// 295 | 296 | var defaultRecoveryFilter = &recoveryFilter{} 297 | 298 | // recoveryFilter 恢复过滤器 299 | type recoveryFilter struct{} 300 | 301 | func (f *recoveryFilter) Invoke(ctx WebContext, chain FilterChain) { 302 | 303 | defer func() { 304 | if err := recover(); err != nil { 305 | ctx.LogError("[PANIC RECOVER] ", err) 306 | ctx.Status(http.StatusInternalServerError) 307 | } 308 | }() 309 | 310 | chain.Next(ctx) 311 | } 312 | 313 | var defaultLoggerFilter = &loggerFilter{} 314 | 315 | // loggerFilter 日志过滤器 316 | type loggerFilter struct{} 317 | 318 | func (f *loggerFilter) Invoke(ctx WebContext, chain FilterChain) { 319 | start := time.Now() 320 | chain.Next(ctx) 321 | ctx.LogInfo("cost: ", time.Since(start)) 322 | } 323 | -------------------------------------------------------------------------------- /spring-web-context.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2019 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package SpringWeb 18 | 19 | import ( 20 | "io" 21 | "mime/multipart" 22 | "net/http" 23 | "net/url" 24 | 25 | "github.com/go-spring/go-spring-logger" 26 | ) 27 | 28 | // WebContextKey WebContext 和 NativeContext 相互转换的 Key 29 | const WebContextKey = "@WebCtx" 30 | 31 | // WebContext 上下文接口,设计理念:为社区中优秀的 Web 服务器提供一个抽象层, 32 | // 使得底层可以灵活切换,因此在功能上取这些 Web 服务器功能的交集,同时提供获取 33 | // 底层对象的接口,以便在不能满足用户要求的时候使用底层实现的能力,当然要慎用。 34 | type WebContext interface { 35 | ///////////////////////////////////////// 36 | // 通用能力部分 37 | 38 | // LoggerContext 日志接口上下文 39 | SpringLogger.LoggerContext 40 | 41 | // NativeContext 返回封装的底层上下文对象 42 | NativeContext() interface{} 43 | 44 | // Get retrieves data from the context. 45 | Get(key string) interface{} 46 | 47 | // Set saves data in the context. 48 | Set(key string, val interface{}) 49 | 50 | ///////////////////////////////////////// 51 | // Request Part 52 | 53 | // Request returns `*http.Request`. 54 | Request() *http.Request 55 | 56 | // SetRequest sets `*http.Request`. 57 | SetRequest(r *http.Request) 58 | 59 | // IsTLS returns true if HTTP connection is TLS otherwise false. 60 | IsTLS() bool 61 | 62 | // IsWebSocket returns true if HTTP connection is WebSocket otherwise false. 63 | IsWebSocket() bool 64 | 65 | // Scheme returns the HTTP protocol scheme, `http` or `https`. 66 | Scheme() string 67 | 68 | // ClientIP implements a best effort algorithm to return the real client IP, 69 | // it parses X-Real-IP and X-Forwarded-For in order to work properly with 70 | // reverse-proxies such us: nginx or haproxy. Use X-Forwarded-For before 71 | // X-Real-Ip as nginx uses X-Real-Ip with the proxy's IP. 72 | ClientIP() string 73 | 74 | // Path returns the registered path for the handler. 75 | Path() string 76 | 77 | // Handler returns the matched handler by router. 78 | Handler() Handler 79 | 80 | // ContentType returns the Content-Type header of the request. 81 | ContentType() string 82 | 83 | // GetHeader returns value from request headers. 84 | GetHeader(key string) string 85 | 86 | // GetRawData return stream data. 87 | GetRawData() ([]byte, error) 88 | 89 | // PathParam returns path parameter by name. 90 | PathParam(name string) string 91 | 92 | // PathParamNames returns path parameter names. 93 | PathParamNames() []string 94 | 95 | // PathParamValues returns path parameter values. 96 | PathParamValues() []string 97 | 98 | // QueryParam returns the query param for the provided name. 99 | QueryParam(name string) string 100 | 101 | // QueryParams returns the query parameters as `url.Values`. 102 | QueryParams() url.Values 103 | 104 | // QueryString returns the URL query string. 105 | QueryString() string 106 | 107 | // FormValue returns the form field value for the provided name. 108 | FormValue(name string) string 109 | 110 | // FormParams returns the form parameters as `url.Values`. 111 | FormParams() (url.Values, error) 112 | 113 | // FormFile returns the multipart form file for the provided name. 114 | FormFile(name string) (*multipart.FileHeader, error) 115 | 116 | // SaveUploadedFile uploads the form file to specific dst. 117 | SaveUploadedFile(file *multipart.FileHeader, dst string) error 118 | 119 | // MultipartForm returns the multipart form. 120 | MultipartForm() (*multipart.Form, error) 121 | 122 | // Cookie returns the named cookie provided in the request. 123 | Cookie(name string) (*http.Cookie, error) 124 | 125 | // Cookies returns the HTTP cookies sent with the request. 126 | Cookies() []*http.Cookie 127 | 128 | // Bind binds the request body into provided type `i`. The default binder 129 | // does it based on Content-Type header. 130 | Bind(i interface{}) error 131 | 132 | ///////////////////////////////////////// 133 | // Response Part 134 | 135 | // ResponseWriter returns `http.ResponseWriter`. 136 | ResponseWriter() http.ResponseWriter 137 | 138 | // Status sets the HTTP response code. 139 | Status(code int) 140 | 141 | // Header is a intelligent shortcut for c.Writer.Header().Set(key, value). 142 | // It writes a header in the response. 143 | // If value == "", this method removes the header `c.Writer.Header().Del(key)` 144 | Header(key, value string) 145 | 146 | // SetCookie adds a `Set-Cookie` header in HTTP response. 147 | SetCookie(cookie *http.Cookie) 148 | 149 | // NoContent sends a response with no body and a status code. 150 | NoContent(code int) 151 | 152 | // String writes the given string into the response body. 153 | String(code int, format string, values ...interface{}) 154 | 155 | // HTML sends an HTTP response with status code. 156 | HTML(code int, html string) 157 | 158 | // HTMLBlob sends an HTTP blob response with status code. 159 | HTMLBlob(code int, b []byte) 160 | 161 | // JSON sends a JSON response with status code. 162 | JSON(code int, i interface{}) 163 | 164 | // JSONPretty sends a pretty-print JSON with status code. 165 | JSONPretty(code int, i interface{}, indent string) 166 | 167 | // JSONBlob sends a JSON blob response with status code. 168 | JSONBlob(code int, b []byte) 169 | 170 | // JSONP sends a JSONP response with status code. It uses `callback` 171 | // to construct the JSONP payload. 172 | JSONP(code int, callback string, i interface{}) 173 | 174 | // JSONPBlob sends a JSONP blob response with status code. It uses 175 | // `callback` to construct the JSONP payload. 176 | JSONPBlob(code int, callback string, b []byte) 177 | 178 | // XML sends an XML response with status code. 179 | XML(code int, i interface{}) 180 | 181 | // XMLPretty sends a pretty-print XML with status code. 182 | XMLPretty(code int, i interface{}, indent string) 183 | 184 | // XMLBlob sends an XML blob response with status code. 185 | XMLBlob(code int, b []byte) 186 | 187 | // Blob sends a blob response with status code and content type. 188 | Blob(code int, contentType string, b []byte) 189 | 190 | // Stream sends a streaming response with status code and content type. 191 | Stream(code int, contentType string, r io.Reader) 192 | 193 | // File sends a response with the content of the file. 194 | File(file string) 195 | 196 | // Attachment sends a response as attachment, prompting client to save the 197 | // file. 198 | Attachment(file string, name string) 199 | 200 | // Inline sends a response as inline, opening the file in the browser. 201 | Inline(file string, name string) 202 | 203 | // Redirect redirects the request to a provided URL with status code. 204 | Redirect(code int, url string) 205 | 206 | // SSEvent writes a Server-Sent Event into the body stream. 207 | SSEvent(name string, message interface{}) 208 | } 209 | -------------------------------------------------------------------------------- /spring-web-filter.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2019 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package SpringWeb 18 | 19 | // Filter 过滤器接口 20 | type Filter interface { 21 | // Invoke 通过 chain.Next() 驱动链条向后执行 22 | Invoke(ctx WebContext, chain FilterChain) 23 | } 24 | 25 | // handlerFilter 包装 Web 处理接口的过滤器 26 | type handlerFilter struct { 27 | fn Handler 28 | } 29 | 30 | // HandlerFilter 把 Web 处理接口转换成过滤器 31 | func HandlerFilter(fn Handler) Filter { 32 | return &handlerFilter{fn: fn} 33 | } 34 | 35 | func (h *handlerFilter) Invoke(ctx WebContext, _ FilterChain) { 36 | h.fn.Invoke(ctx) 37 | } 38 | 39 | // FilterChain 过滤器链条接口 40 | type FilterChain interface { 41 | Next(ctx WebContext) 42 | } 43 | 44 | // DefaultFilterChain 默认的过滤器链条 45 | type DefaultFilterChain struct { 46 | filters []Filter // 过滤器列表 47 | next int // 下一个等待执行的过滤器的序号 48 | } 49 | 50 | // NewDefaultFilterChain DefaultFilterChain 的构造函数 51 | func NewDefaultFilterChain(filters []Filter) *DefaultFilterChain { 52 | return &DefaultFilterChain{filters: filters} 53 | } 54 | 55 | func (chain *DefaultFilterChain) Next(ctx WebContext) { 56 | 57 | // 链条执行到此结束 58 | if chain.next >= len(chain.filters) { 59 | return 60 | } 61 | 62 | // 执行下一个过滤器 63 | f := chain.filters[chain.next] 64 | chain.next++ 65 | f.Invoke(ctx, chain) 66 | } 67 | -------------------------------------------------------------------------------- /spring-web-mapper.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2019 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package SpringWeb 18 | 19 | import ( 20 | "fmt" 21 | ) 22 | 23 | // Mapper 路由映射器 24 | type Mapper struct { 25 | method uint32 // 方法 26 | path string // 路径 27 | handler Handler // 处理函数 28 | filters []Filter // 过滤器列表 29 | swagger *Operation 30 | } 31 | 32 | // NewMapper Mapper 的构造函数 33 | func NewMapper(method uint32, path string, fn Handler, filters []Filter) *Mapper { 34 | return &Mapper{ 35 | method: method, 36 | path: path, 37 | handler: fn, 38 | filters: filters, 39 | } 40 | } 41 | 42 | // Key 返回 Mapper 的标识符 43 | func (m *Mapper) Key() string { 44 | return fmt.Sprintf("0x%.4x@%s", m.method, m.path) 45 | } 46 | 47 | // Method 返回 Mapper 的方法 48 | func (m *Mapper) Method() uint32 { 49 | return m.method 50 | } 51 | 52 | // Path 返回 Mapper 的路径 53 | func (m *Mapper) Path() string { 54 | return m.path 55 | } 56 | 57 | // Handler 返回 Mapper 的处理函数 58 | func (m *Mapper) Handler() Handler { 59 | return m.handler 60 | } 61 | 62 | // Filters 返回 Mapper 的过滤器列表 63 | func (m *Mapper) Filters() []Filter { 64 | return m.filters 65 | } 66 | 67 | // Swagger 生成并返回 Operation 对象 68 | func (m *Mapper) Swagger(id string) *Operation { 69 | m.swagger = NewOperation(id) 70 | return m.swagger 71 | } 72 | 73 | // GetSwagger 返回 Swagger 文档 74 | func (m *Mapper) GetSwagger() *Operation { 75 | return m.swagger 76 | } 77 | 78 | // WithSwagger 设置 Swagger 文档 79 | func (m *Mapper) WithSwagger(swagger *Operation) *Mapper { 80 | m.swagger = swagger 81 | return m 82 | } 83 | -------------------------------------------------------------------------------- /spring-web-mapper_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2019 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package SpringWeb_test 18 | 19 | import ( 20 | "fmt" 21 | "testing" 22 | 23 | "github.com/go-spring/go-spring-web" 24 | ) 25 | 26 | func TestMapper_Key(t *testing.T) { 27 | fmt.Println(SpringWeb.NewMapper(SpringWeb.MethodAny, "/", nil, nil).Key()) 28 | fmt.Println(SpringWeb.NewMapper(SpringWeb.MethodGet, "/", nil, nil).Key()) 29 | fmt.Println(SpringWeb.NewMapper(SpringWeb.MethodGetPost, "/", nil, nil).Key()) 30 | } 31 | -------------------------------------------------------------------------------- /spring-web-mapping.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2019 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package SpringWeb 18 | 19 | // UrlRegister 路由注册接口 20 | type UrlRegister interface { 21 | 22 | // Request 注册任意 HTTP 方法处理函数 23 | Request(method uint32, path string, fn Handler, filters ...Filter) *Mapper 24 | 25 | // HandleGet 注册 GET 方法处理函数 26 | HandleGet(path string, fn Handler, filters ...Filter) *Mapper 27 | 28 | // GetMapping 注册 GET 方法处理函数 29 | GetMapping(path string, fn HandlerFunc, filters ...Filter) *Mapper 30 | 31 | // GetBinding 注册 GET 方法处理函数 32 | GetBinding(path string, fn interface{}, filters ...Filter) *Mapper 33 | 34 | // HandlePost 注册 POST 方法处理函数 35 | HandlePost(path string, fn Handler, filters ...Filter) *Mapper 36 | 37 | // PostMapping 注册 POST 方法处理函数 38 | PostMapping(path string, fn HandlerFunc, filters ...Filter) *Mapper 39 | 40 | // PostBinding 注册 POST 方法处理函数 41 | PostBinding(path string, fn interface{}, filters ...Filter) *Mapper 42 | 43 | // HandlePut 注册 PUT 方法处理函数 44 | HandlePut(path string, fn Handler, filters ...Filter) *Mapper 45 | 46 | // PutMapping 注册 PUT 方法处理函数 47 | PutMapping(path string, fn HandlerFunc, filters ...Filter) *Mapper 48 | 49 | // PutBinding 注册 PUT 方法处理函数 50 | PutBinding(path string, fn interface{}, filters ...Filter) *Mapper 51 | 52 | // HandleDelete 注册 DELETE 方法处理函数 53 | HandleDelete(path string, fn Handler, filters ...Filter) *Mapper 54 | 55 | // DeleteMapping 注册 DELETE 方法处理函数 56 | DeleteMapping(path string, fn HandlerFunc, filters ...Filter) *Mapper 57 | 58 | // DeleteBinding 注册 DELETE 方法处理函数 59 | DeleteBinding(path string, fn interface{}, filters ...Filter) *Mapper 60 | } 61 | 62 | // defaultUrlRegister 路由注册接口的默认实现 63 | type defaultUrlRegister struct { 64 | request func(method uint32, path string, fn Handler, filters []Filter) *Mapper 65 | } 66 | 67 | // Request 注册任意 HTTP 方法处理函数 68 | func (r *defaultUrlRegister) Request(method uint32, path string, fn Handler, filters ...Filter) *Mapper { 69 | return r.request(method, path, fn, filters) 70 | } 71 | 72 | // HandleGet 注册 GET 方法处理函数 73 | func (r *defaultUrlRegister) HandleGet(path string, fn Handler, filters ...Filter) *Mapper { 74 | return r.request(MethodGet, path, fn, filters) 75 | } 76 | 77 | // GetMapping 注册 GET 方法处理函数 78 | func (r *defaultUrlRegister) GetMapping(path string, fn HandlerFunc, filters ...Filter) *Mapper { 79 | return r.request(MethodGet, path, FUNC(fn), filters) 80 | } 81 | 82 | // GetBinding 注册 GET 方法处理函数 83 | func (r *defaultUrlRegister) GetBinding(path string, fn interface{}, filters ...Filter) *Mapper { 84 | return r.request(MethodGet, path, BIND(fn), filters) 85 | } 86 | 87 | // HandlePost 注册 POST 方法处理函数 88 | func (r *defaultUrlRegister) HandlePost(path string, fn Handler, filters ...Filter) *Mapper { 89 | return r.request(MethodPost, path, fn, filters) 90 | } 91 | 92 | // PostMapping 注册 POST 方法处理函数 93 | func (r *defaultUrlRegister) PostMapping(path string, fn HandlerFunc, filters ...Filter) *Mapper { 94 | return r.request(MethodPost, path, FUNC(fn), filters) 95 | } 96 | 97 | // PostBinding 注册 POST 方法处理函数 98 | func (r *defaultUrlRegister) PostBinding(path string, fn interface{}, filters ...Filter) *Mapper { 99 | return r.request(MethodPost, path, BIND(fn), filters) 100 | } 101 | 102 | // HandlePut 注册 PUT 方法处理函数 103 | func (r *defaultUrlRegister) HandlePut(path string, fn Handler, filters ...Filter) *Mapper { 104 | return r.request(MethodPut, path, fn, filters) 105 | } 106 | 107 | // PutMapping 注册 PUT 方法处理函数 108 | func (r *defaultUrlRegister) PutMapping(path string, fn HandlerFunc, filters ...Filter) *Mapper { 109 | return r.request(MethodPut, path, FUNC(fn), filters) 110 | } 111 | 112 | // PutBinding 注册 PUT 方法处理函数 113 | func (r *defaultUrlRegister) PutBinding(path string, fn interface{}, filters ...Filter) *Mapper { 114 | return r.request(MethodPut, path, BIND(fn), filters) 115 | } 116 | 117 | // HandleDelete 注册 DELETE 方法处理函数 118 | func (r *defaultUrlRegister) HandleDelete(path string, fn Handler, filters ...Filter) *Mapper { 119 | return r.request(MethodDelete, path, fn, filters) 120 | } 121 | 122 | // DeleteMapping 注册 DELETE 方法处理函数 123 | func (r *defaultUrlRegister) DeleteMapping(path string, fn HandlerFunc, filters ...Filter) *Mapper { 124 | return r.request(MethodDelete, path, FUNC(fn), filters) 125 | } 126 | 127 | // DeleteBinding 注册 DELETE 方法处理函数 128 | func (r *defaultUrlRegister) DeleteBinding(path string, fn interface{}, filters ...Filter) *Mapper { 129 | return r.request(MethodDelete, path, BIND(fn), filters) 130 | } 131 | 132 | // WebMapping 路由表,Spring-Web 使用的路由规则和 echo 完全相同,并对 gin 做了适配。 133 | type WebMapping interface { 134 | UrlRegister 135 | 136 | // Mappers 返回映射器列表 137 | Mappers() map[string]*Mapper 138 | 139 | // AddMapper 添加一个 Mapper 140 | AddMapper(m *Mapper) *Mapper 141 | 142 | // Route 返回和 Mapping 绑定的路由分组 143 | Route(basePath string, filters ...Filter) *Router 144 | } 145 | 146 | // defaultWebMapping 路由表的默认实现 147 | type defaultWebMapping struct { 148 | UrlRegister 149 | 150 | mappers map[string]*Mapper 151 | } 152 | 153 | // NewDefaultWebMapping defaultWebMapping 的构造函数 154 | func NewDefaultWebMapping() *defaultWebMapping { 155 | m := &defaultWebMapping{} 156 | m.mappers = make(map[string]*Mapper) 157 | m.UrlRegister = &defaultUrlRegister{request: m.request} 158 | return m 159 | } 160 | 161 | // Mappers 返回映射器列表 162 | func (w *defaultWebMapping) Mappers() map[string]*Mapper { 163 | return w.mappers 164 | } 165 | 166 | // AddMapper 添加一个 Mapper 167 | func (w *defaultWebMapping) AddMapper(m *Mapper) *Mapper { 168 | w.mappers[m.Key()] = m 169 | return m 170 | } 171 | 172 | // Route 返回和 Mapping 绑定的路由分组 173 | func (w *defaultWebMapping) Route(basePath string, filters ...Filter) *Router { 174 | return routerWithMapping(w, basePath, filters) 175 | } 176 | 177 | func (w *defaultWebMapping) request(method uint32, path string, fn Handler, filters []Filter) *Mapper { 178 | m := NewMapper(method, path, fn, filters) 179 | w.mappers[m.Key()] = m 180 | return m 181 | } 182 | -------------------------------------------------------------------------------- /spring-web-method.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2019 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package SpringWeb 18 | 19 | import ( 20 | "net/http" 21 | ) 22 | 23 | const ( 24 | MethodGet = 0x0001 // "GET" 25 | MethodHead = 0x0002 // "HEAD" 26 | MethodPost = 0x0004 // "POST" 27 | MethodPut = 0x0008 // "PUT" 28 | MethodPatch = 0x0010 // "PATCH" 29 | MethodDelete = 0x0020 // "DELETE" 30 | MethodConnect = 0x0040 // "CONNECT" 31 | MethodOptions = 0x0080 // "OPTIONS" 32 | MethodTrace = 0x0100 // "TRACE" 33 | MethodAny = 0xffff 34 | MethodGetPost = MethodGet | MethodPost 35 | ) 36 | 37 | var methods = map[uint32]string{ 38 | MethodGet: http.MethodGet, 39 | MethodHead: http.MethodHead, 40 | MethodPost: http.MethodPost, 41 | MethodPut: http.MethodPut, 42 | MethodPatch: http.MethodPatch, 43 | MethodDelete: http.MethodDelete, 44 | MethodConnect: http.MethodConnect, 45 | MethodOptions: http.MethodOptions, 46 | MethodTrace: http.MethodTrace, 47 | } 48 | 49 | // GetMethod 返回 method 对应的 HTTP 方法 50 | func GetMethod(method uint32) []string { 51 | var r []string 52 | for k, v := range methods { 53 | if method&k == k { 54 | r = append(r, v) 55 | } 56 | } 57 | return r 58 | } 59 | -------------------------------------------------------------------------------- /spring-web-method_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2019 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package SpringWeb_test 18 | 19 | import ( 20 | "net/http" 21 | "testing" 22 | 23 | "github.com/go-spring/go-spring-web" 24 | ) 25 | 26 | // cacheMethods 27 | var cacheMethods = map[uint32][]string{ 28 | SpringWeb.MethodGet: {http.MethodGet}, 29 | SpringWeb.MethodHead: {http.MethodHead}, 30 | SpringWeb.MethodPost: {http.MethodPost}, 31 | SpringWeb.MethodPut: {http.MethodPut}, 32 | SpringWeb.MethodPatch: {http.MethodPatch}, 33 | SpringWeb.MethodDelete: {http.MethodDelete}, 34 | SpringWeb.MethodConnect: {http.MethodConnect}, 35 | SpringWeb.MethodOptions: {http.MethodOptions}, 36 | SpringWeb.MethodTrace: {http.MethodTrace}, 37 | SpringWeb.MethodGetPost: {http.MethodGet, http.MethodPost}, 38 | SpringWeb.MethodAny: {http.MethodGet, http.MethodHead, http.MethodPost, http.MethodPut, http.MethodPatch, http.MethodDelete, http.MethodConnect, http.MethodOptions, http.MethodTrace}, 39 | } 40 | 41 | func GetMethodViaCache(method uint32) []string { 42 | if r, ok := cacheMethods[method]; ok { 43 | return r 44 | } 45 | return SpringWeb.GetMethod(method) 46 | } 47 | 48 | func BenchmarkGetMethod(b *testing.B) { 49 | // 测试结论:使用缓存不一定能提高效率 50 | 51 | b.Run("1", func(b *testing.B) { 52 | SpringWeb.GetMethod(SpringWeb.MethodGet) 53 | }) 54 | 55 | b.Run("cache-1", func(b *testing.B) { 56 | GetMethodViaCache(SpringWeb.MethodGet) 57 | }) 58 | 59 | b.Run("2", func(b *testing.B) { 60 | SpringWeb.GetMethod(SpringWeb.MethodGet | SpringWeb.MethodHead) 61 | }) 62 | 63 | b.Run("cache-2", func(b *testing.B) { 64 | GetMethodViaCache(SpringWeb.MethodGet | SpringWeb.MethodHead) 65 | }) 66 | 67 | b.Run("3", func(b *testing.B) { 68 | SpringWeb.GetMethod(SpringWeb.MethodGet | SpringWeb.MethodHead | SpringWeb.MethodPost) 69 | }) 70 | 71 | b.Run("cache-3", func(b *testing.B) { 72 | GetMethodViaCache(SpringWeb.MethodGet | SpringWeb.MethodHead | SpringWeb.MethodPost) 73 | }) 74 | 75 | b.Run("4", func(b *testing.B) { 76 | SpringWeb.GetMethod(SpringWeb.MethodGet | SpringWeb.MethodHead | SpringWeb.MethodPost | SpringWeb.MethodPut) 77 | }) 78 | 79 | b.Run("cache-4", func(b *testing.B) { 80 | GetMethodViaCache(SpringWeb.MethodGet | SpringWeb.MethodHead | SpringWeb.MethodPost | SpringWeb.MethodPut) 81 | }) 82 | 83 | b.Run("5", func(b *testing.B) { 84 | SpringWeb.GetMethod(SpringWeb.MethodGet | SpringWeb.MethodHead | SpringWeb.MethodPost | SpringWeb.MethodPut | SpringWeb.MethodPatch) 85 | }) 86 | 87 | b.Run("cache-5", func(b *testing.B) { 88 | GetMethodViaCache(SpringWeb.MethodGet | SpringWeb.MethodHead | SpringWeb.MethodPost | SpringWeb.MethodPut | SpringWeb.MethodPatch) 89 | }) 90 | 91 | b.Run("6", func(b *testing.B) { 92 | SpringWeb.GetMethod(SpringWeb.MethodGet | SpringWeb.MethodHead | SpringWeb.MethodPost | SpringWeb.MethodPut | SpringWeb.MethodPatch | SpringWeb.MethodDelete) 93 | }) 94 | 95 | b.Run("cache-6", func(b *testing.B) { 96 | GetMethodViaCache(SpringWeb.MethodGet | SpringWeb.MethodHead | SpringWeb.MethodPost | SpringWeb.MethodPut | SpringWeb.MethodPatch | SpringWeb.MethodDelete) 97 | }) 98 | 99 | b.Run("7", func(b *testing.B) { 100 | SpringWeb.GetMethod(SpringWeb.MethodGet | SpringWeb.MethodHead | SpringWeb.MethodPost | SpringWeb.MethodPut | SpringWeb.MethodPatch | SpringWeb.MethodDelete | SpringWeb.MethodConnect) 101 | }) 102 | 103 | b.Run("cache-7", func(b *testing.B) { 104 | GetMethodViaCache(SpringWeb.MethodGet | SpringWeb.MethodHead | SpringWeb.MethodPost | SpringWeb.MethodPut | SpringWeb.MethodPatch | SpringWeb.MethodDelete | SpringWeb.MethodConnect) 105 | }) 106 | 107 | b.Run("8", func(b *testing.B) { 108 | SpringWeb.GetMethod(SpringWeb.MethodGet | SpringWeb.MethodHead | SpringWeb.MethodPost | SpringWeb.MethodPut | SpringWeb.MethodPatch | SpringWeb.MethodDelete | SpringWeb.MethodConnect | SpringWeb.MethodOptions) 109 | }) 110 | 111 | b.Run("cache-8", func(b *testing.B) { 112 | GetMethodViaCache(SpringWeb.MethodGet | SpringWeb.MethodHead | SpringWeb.MethodPost | SpringWeb.MethodPut | SpringWeb.MethodPatch | SpringWeb.MethodDelete | SpringWeb.MethodConnect | SpringWeb.MethodOptions) 113 | }) 114 | 115 | b.Run("9", func(b *testing.B) { 116 | SpringWeb.GetMethod(SpringWeb.MethodGet | SpringWeb.MethodHead | SpringWeb.MethodPost | SpringWeb.MethodPut | SpringWeb.MethodPatch | SpringWeb.MethodDelete | SpringWeb.MethodConnect | SpringWeb.MethodOptions | SpringWeb.MethodTrace) 117 | }) 118 | 119 | b.Run("cache-9", func(b *testing.B) { 120 | GetMethodViaCache(SpringWeb.MethodGet | SpringWeb.MethodHead | SpringWeb.MethodPost | SpringWeb.MethodPut | SpringWeb.MethodPatch | SpringWeb.MethodDelete | SpringWeb.MethodConnect | SpringWeb.MethodOptions | SpringWeb.MethodTrace) 121 | }) 122 | } 123 | -------------------------------------------------------------------------------- /spring-web-middleware.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2019 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package SpringWeb 18 | 19 | // logger 20 | // echo: https://github.com/labstack/echo/blob/master/middleware/logger.go 21 | // gin: https://github.com/gin-contrib/logger/blob/master/logger.go 22 | // https://github.com/gin-contrib/zap/blob/master/zap.go 23 | 24 | // recover 25 | // echo: https://github.com/labstack/echo/blob/master/middleware/recover.go 26 | // gin: https://github.com/gin-contrib/rollbar/blob/master/recovey.go 27 | 28 | // method_override 29 | // echo: https://github.com/labstack/echo/blob/master/middleware/method_override.go 30 | // gin: 31 | 32 | // redirect 33 | // echo: https://github.com/labstack/echo/blob/master/middleware/redirect.go 34 | // gin: 35 | 36 | // request_id 37 | // echo: https://github.com/labstack/echo/blob/master/middleware/request_id.go 38 | // gin: https://github.com/gin-contrib/requestid/blob/master/requestid.go 39 | 40 | // rewrite 41 | // echo: https://github.com/labstack/echo/blob/master/middleware/rewrite.go 42 | // gin: 43 | 44 | // basic_auth 45 | // echo: https://github.com/labstack/echo/blob/master/middleware/basic_auth.go 46 | // gin: 47 | 48 | // key_auth 49 | // echo: https://github.com/labstack/echo/blob/master/middleware/key_auth.go 50 | // gin: 51 | 52 | // body_dump 53 | // echo: https://github.com/labstack/echo/blob/master/middleware/body_dump.go 54 | // gin: 55 | 56 | // body_limit (413 - Request Entity Too Large) 57 | // echo: https://github.com/labstack/echo/blob/master/middleware/body_limit.go 58 | // gin: https://github.com/gin-contrib/size/blob/master/size.go 59 | 60 | // compress (gzip) 61 | // echo: https://github.com/labstack/echo/blob/master/middleware/compress.go 62 | // gin: https://github.com/gin-contrib/gzip/blob/master/gzip.go 63 | 64 | // cors (Cross-Origin Resource Sharing) 65 | // echo: https://github.com/labstack/echo/blob/master/middleware/cors.go 66 | // gin: https://github.com/gin-contrib/cors/blob/master/cors.go 67 | 68 | // csrf (Cross-Site Request Forgery) 69 | // echo: https://github.com/labstack/echo/blob/master/middleware/csrf.go 70 | // gin: 71 | 72 | // secure (XSS) 73 | // echo: https://github.com/labstack/echo/blob/master/middleware/secure.go 74 | // gin: 75 | 76 | // jwt (JSON Web Token) 77 | // echo: https://github.com/labstack/echo/blob/master/middleware/jwt.go 78 | // gin: https://github.com/appleboy/gin-jwt/blob/master/auth_jwt.go 79 | 80 | // proxy 81 | // echo: https://github.com/labstack/echo/blob/master/middleware/proxy.go 82 | // gin: 83 | 84 | // slash 85 | // echo: https://github.com/labstack/echo/blob/master/middleware/slash.go 86 | // gin: 87 | 88 | // static 89 | // echo: https://github.com/labstack/echo/blob/master/middleware/static.go 90 | // gin: https://github.com/gin-contrib/static/blob/master/static.go 91 | 92 | // casbin 93 | // echo: https://github.com/labstack/echo-contrib/blob/master/casbin/casbin.go 94 | // gin: https://github.com/gin-contrib/authz/blob/master/authz.go 95 | 96 | // tracing 97 | // echo: https://github.com/labstack/echo-contrib/blob/master/jaegertracing/jaegertracing.go 98 | // gin: https://github.com/gin-contrib/opengintracing/blob/master/tracing.go 99 | 100 | // prometheus 101 | // echo: https://github.com/labstack/echo-contrib/blob/master/prometheus/prometheus.go 102 | // gin: https://github.com/zsais/go-gin-prometheus/blob/master/middleware.go 103 | 104 | // session 105 | // echo: https://github.com/labstack/echo-contrib/blob/master/session/session.go 106 | // gin: https://github.com/gin-contrib/sessions/blob/master/sessions.go 107 | 108 | // pprof 109 | // echo: 110 | // gin: https://github.com/gin-contrib/pprof/blob/master/pprof.go 111 | 112 | // cache 113 | // echo: 114 | // gin: https://github.com/gin-contrib/cache/blob/master/cache.go 115 | 116 | // location 117 | // echo: 118 | // gin: https://github.com/gin-contrib/location/blob/master/location.go 119 | 120 | // httpsign 121 | // echo: 122 | // gin: https://github.com/gin-contrib/httpsign/blob/master/signatureheader.go 123 | // https://github.com/gin-contrib/httpsign/blob/master/authenticator.go 124 | 125 | // limiter 126 | // echo: 127 | // gin: https://github.com/ulule/limiter/blob/master/drivers/middleware/gin/middleware.go 128 | 129 | // swagger 130 | // echo: https://github.com/swaggo/echo-swagger/blob/master/swagger.go 131 | // gin: https://github.com/swaggo/gin-swagger/blob/master/swagger.go 132 | 133 | // oauth2 134 | // echo: 135 | // gin: https://github.com/zalando/gin-oauth2/blob/master/ginoauth2.go 136 | -------------------------------------------------------------------------------- /spring-web-redoc.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2019 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package SpringWeb 18 | 19 | import ( 20 | "html/template" 21 | ) 22 | 23 | // ReDoc redoc 响应函数 24 | func ReDoc(ctx WebContext) { 25 | 26 | index, err := template.New("redoc.html").Parse(redocTempl) 27 | if err != nil { 28 | panic(err) 29 | } 30 | 31 | // 不确定 Execute 是否线程安全,官方文档表示也许是线程安全的,谁知道呢 32 | _ = index.Execute(ctx.ResponseWriter(), map[string]interface{}{ 33 | "URL": "/swagger/doc.json", 34 | }) 35 | } 36 | 37 | const redocTempl = ` 38 | 39 | 40 |
41 |