├── .gitignore ├── LICENSE ├── README.md ├── imgs └── demo.png ├── package-lock.json ├── package.json ├── src ├── app.ts ├── decorators │ ├── http-methods.ts │ ├── path.ts │ └── validate.ts └── router.ts └── tsconfig.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | 3 | # Typescript v1 declaration files 4 | typings/ 5 | output/ 6 | .DS_Store -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 linkFly 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ts-router-to-controller 2 | 3 | 《[从 JavaScript 到 TypeScript - express 路由进化](http://tasaid.com/Blog/20171011233051.html?sgs=github-demo)》 文章 demo。 4 | 5 | ![](https://github.com/linkFly6/ts-router-to-controller/blob/master/imgs/demo.png) 6 | 7 | 8 | 9 | 执行命令: 10 | 11 | ```bash 12 | npm i 13 | npm run dev 14 | ``` 15 | 16 | - POST 访问 `localhost:8000/user/login` 17 | - GET 访问 `localhost:8000/user/exit` 18 | 19 | 20 | -------------------------------------------------------------------------------- /imgs/demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linkFly6/ts-router-to-controller/2a0e381e606c765b72d65680ef569ac9cc5bbdda/imgs/demo.png -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ts-demo", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@types/express": { 8 | "version": "4.0.37", 9 | "resolved": "http://artifactory.intra.xiaojukeji.com:80/artifactory/api/npm/npm/@types/express/download/@types/express-4.0.37.tgz", 10 | "integrity": "sha1-YlrDdlFpZ24BiXykcBHCY3V4SXE=", 11 | "requires": { 12 | "@types/express-serve-static-core": "*", 13 | "@types/serve-static": "*" 14 | } 15 | }, 16 | "@types/express-serve-static-core": { 17 | "version": "4.0.53", 18 | "resolved": "http://artifactory.intra.xiaojukeji.com:80/artifactory/api/npm/npm/@types/express-serve-static-core/download/@types/express-serve-static-core-4.0.53.tgz", 19 | "integrity": "sha1-FyOjXRRH8sVeE8hyHqs0SOQvTYI=", 20 | "requires": { 21 | "@types/node": "*" 22 | } 23 | }, 24 | "@types/mime": { 25 | "version": "2.0.0", 26 | "resolved": "http://artifactory.intra.xiaojukeji.com:80/artifactory/api/npm/npm/@types/mime/download/@types/mime-2.0.0.tgz", 27 | "integrity": "sha1-WnMG42fFObn2VDSZ3o3VGfrDeos=" 28 | }, 29 | "@types/node": { 30 | "version": "8.0.34", 31 | "resolved": "http://artifactory.intra.xiaojukeji.com:80/artifactory/api/npm/npm/@types/node/download/@types/node-8.0.34.tgz", 32 | "integrity": "sha1-VfgB+i3bKkDdbfwV7P4d3pwSn+k=" 33 | }, 34 | "@types/serve-static": { 35 | "version": "1.7.32", 36 | "resolved": "http://artifactory.intra.xiaojukeji.com:80/artifactory/api/npm/npm/@types/serve-static/download/@types/serve-static-1.7.32.tgz", 37 | "integrity": "sha1-D2cy5NqwgTdx3Y/I/hSUDzRyi0w=", 38 | "requires": { 39 | "@types/express-serve-static-core": "*", 40 | "@types/mime": "*" 41 | } 42 | }, 43 | "accepts": { 44 | "version": "1.3.4", 45 | "resolved": "http://artifactory.intra.xiaojukeji.com:80/artifactory/api/npm/npm/accepts/download/accepts-1.3.4.tgz", 46 | "integrity": "sha1-hiRnWMfdbSGmR0/whKR0DsBesh8=", 47 | "requires": { 48 | "mime-types": "~2.1.16", 49 | "negotiator": "0.6.1" 50 | } 51 | }, 52 | "array-flatten": { 53 | "version": "1.1.1", 54 | "resolved": "http://artifactory.intra.xiaojukeji.com:80/artifactory/api/npm/npm/array-flatten/download/array-flatten-1.1.1.tgz", 55 | "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" 56 | }, 57 | "body-parser": { 58 | "version": "1.18.2", 59 | "resolved": "http://artifactory.intra.xiaojukeji.com:80/artifactory/api/npm/npm/body-parser/download/body-parser-1.18.2.tgz", 60 | "integrity": "sha1-h2eKGdhLR9hZuDGZvVm84iKxBFQ=", 61 | "requires": { 62 | "bytes": "3.0.0", 63 | "content-type": "~1.0.4", 64 | "debug": "2.6.9", 65 | "depd": "~1.1.1", 66 | "http-errors": "~1.6.2", 67 | "iconv-lite": "0.4.19", 68 | "on-finished": "~2.3.0", 69 | "qs": "6.5.1", 70 | "raw-body": "2.3.2", 71 | "type-is": "~1.6.15" 72 | } 73 | }, 74 | "bytes": { 75 | "version": "3.0.0", 76 | "resolved": "http://artifactory.intra.xiaojukeji.com:80/artifactory/api/npm/npm/bytes/download/bytes-3.0.0.tgz", 77 | "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=" 78 | }, 79 | "content-disposition": { 80 | "version": "0.5.2", 81 | "resolved": "http://artifactory.intra.xiaojukeji.com:80/artifactory/api/npm/npm/content-disposition/download/content-disposition-0.5.2.tgz", 82 | "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=" 83 | }, 84 | "content-type": { 85 | "version": "1.0.4", 86 | "resolved": "http://artifactory.intra.xiaojukeji.com:80/artifactory/api/npm/npm/content-type/download/content-type-1.0.4.tgz", 87 | "integrity": "sha1-4TjMdeBAxyexlm/l5fjJruJW/js=" 88 | }, 89 | "cookie": { 90 | "version": "0.3.1", 91 | "resolved": "http://artifactory.intra.xiaojukeji.com:80/artifactory/api/npm/npm/cookie/download/cookie-0.3.1.tgz", 92 | "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" 93 | }, 94 | "cookie-signature": { 95 | "version": "1.0.6", 96 | "resolved": "http://artifactory.intra.xiaojukeji.com:80/artifactory/api/npm/npm/cookie-signature/download/cookie-signature-1.0.6.tgz", 97 | "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" 98 | }, 99 | "debug": { 100 | "version": "2.6.9", 101 | "resolved": "http://artifactory.intra.xiaojukeji.com:80/artifactory/api/npm/npm/debug/download/debug-2.6.9.tgz", 102 | "integrity": "sha1-XRKFFd8TT/Mn6QpMk/Tgd6U2NB8=", 103 | "requires": { 104 | "ms": "2.0.0" 105 | } 106 | }, 107 | "depd": { 108 | "version": "1.1.1", 109 | "resolved": "http://artifactory.intra.xiaojukeji.com:80/artifactory/api/npm/npm/depd/download/depd-1.1.1.tgz", 110 | "integrity": "sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k=" 111 | }, 112 | "destroy": { 113 | "version": "1.0.4", 114 | "resolved": "http://artifactory.intra.xiaojukeji.com:80/artifactory/api/npm/npm/destroy/download/destroy-1.0.4.tgz", 115 | "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" 116 | }, 117 | "ee-first": { 118 | "version": "1.1.1", 119 | "resolved": "http://artifactory.intra.xiaojukeji.com:80/artifactory/api/npm/npm/ee-first/download/ee-first-1.1.1.tgz", 120 | "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" 121 | }, 122 | "encodeurl": { 123 | "version": "1.0.1", 124 | "resolved": "http://artifactory.intra.xiaojukeji.com:80/artifactory/api/npm/npm/encodeurl/download/encodeurl-1.0.1.tgz", 125 | "integrity": "sha1-eePVhlU0aQn+bw9Fpd5oEDspTSA=" 126 | }, 127 | "escape-html": { 128 | "version": "1.0.3", 129 | "resolved": "http://artifactory.intra.xiaojukeji.com:80/artifactory/api/npm/npm/escape-html/download/escape-html-1.0.3.tgz", 130 | "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" 131 | }, 132 | "etag": { 133 | "version": "1.8.1", 134 | "resolved": "http://artifactory.intra.xiaojukeji.com:80/artifactory/api/npm/npm/etag/download/etag-1.8.1.tgz", 135 | "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" 136 | }, 137 | "express": { 138 | "version": "4.16.2", 139 | "resolved": "http://artifactory.intra.xiaojukeji.com:80/artifactory/api/npm/npm/express/download/express-4.16.2.tgz", 140 | "integrity": "sha1-41xt/i1kt9ygpc1PIXgb4ymeB2w=", 141 | "requires": { 142 | "accepts": "~1.3.4", 143 | "array-flatten": "1.1.1", 144 | "body-parser": "1.18.2", 145 | "content-disposition": "0.5.2", 146 | "content-type": "~1.0.4", 147 | "cookie": "0.3.1", 148 | "cookie-signature": "1.0.6", 149 | "debug": "2.6.9", 150 | "depd": "~1.1.1", 151 | "encodeurl": "~1.0.1", 152 | "escape-html": "~1.0.3", 153 | "etag": "~1.8.1", 154 | "finalhandler": "1.1.0", 155 | "fresh": "0.5.2", 156 | "merge-descriptors": "1.0.1", 157 | "methods": "~1.1.2", 158 | "on-finished": "~2.3.0", 159 | "parseurl": "~1.3.2", 160 | "path-to-regexp": "0.1.7", 161 | "proxy-addr": "~2.0.2", 162 | "qs": "6.5.1", 163 | "range-parser": "~1.2.0", 164 | "safe-buffer": "5.1.1", 165 | "send": "0.16.1", 166 | "serve-static": "1.13.1", 167 | "setprototypeof": "1.1.0", 168 | "statuses": "~1.3.1", 169 | "type-is": "~1.6.15", 170 | "utils-merge": "1.0.1", 171 | "vary": "~1.1.2" 172 | } 173 | }, 174 | "finalhandler": { 175 | "version": "1.1.0", 176 | "resolved": "http://artifactory.intra.xiaojukeji.com:80/artifactory/api/npm/npm/finalhandler/download/finalhandler-1.1.0.tgz", 177 | "integrity": "sha1-zgtoVbRYU+eRsvzGgARtiCU91/U=", 178 | "requires": { 179 | "debug": "2.6.9", 180 | "encodeurl": "~1.0.1", 181 | "escape-html": "~1.0.3", 182 | "on-finished": "~2.3.0", 183 | "parseurl": "~1.3.2", 184 | "statuses": "~1.3.1", 185 | "unpipe": "~1.0.0" 186 | } 187 | }, 188 | "forwarded": { 189 | "version": "0.1.2", 190 | "resolved": "http://artifactory.intra.xiaojukeji.com:80/artifactory/api/npm/npm/forwarded/download/forwarded-0.1.2.tgz", 191 | "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" 192 | }, 193 | "fresh": { 194 | "version": "0.5.2", 195 | "resolved": "http://artifactory.intra.xiaojukeji.com:80/artifactory/api/npm/npm/fresh/download/fresh-0.5.2.tgz", 196 | "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" 197 | }, 198 | "http-errors": { 199 | "version": "1.6.2", 200 | "resolved": "http://artifactory.intra.xiaojukeji.com:80/artifactory/api/npm/npm/http-errors/download/http-errors-1.6.2.tgz", 201 | "integrity": "sha1-CgAsyFcHGSp+eUbO7cERVfYOxzY=", 202 | "requires": { 203 | "depd": "1.1.1", 204 | "inherits": "2.0.3", 205 | "setprototypeof": "1.0.3", 206 | "statuses": ">= 1.3.1 < 2" 207 | }, 208 | "dependencies": { 209 | "setprototypeof": { 210 | "version": "1.0.3", 211 | "resolved": "http://artifactory.intra.xiaojukeji.com:80/artifactory/api/npm/npm/setprototypeof/download/setprototypeof-1.0.3.tgz", 212 | "integrity": "sha1-ZlZ+NwQ+608E2RvWWMDL77VbjgQ=" 213 | } 214 | } 215 | }, 216 | "iconv-lite": { 217 | "version": "0.4.19", 218 | "resolved": "http://artifactory.intra.xiaojukeji.com:80/artifactory/api/npm/npm/iconv-lite/download/iconv-lite-0.4.19.tgz", 219 | "integrity": "sha1-90aPYBNfXl2tM5nAqBvpoWA6CCs=" 220 | }, 221 | "inherits": { 222 | "version": "2.0.3", 223 | "resolved": "http://artifactory.intra.xiaojukeji.com:80/artifactory/api/npm/npm/inherits/download/inherits-2.0.3.tgz", 224 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" 225 | }, 226 | "ipaddr.js": { 227 | "version": "1.5.2", 228 | "resolved": "http://artifactory.intra.xiaojukeji.com:80/artifactory/api/npm/npm/ipaddr.js/download/ipaddr.js-1.5.2.tgz", 229 | "integrity": "sha1-1LUFvemUaYfM8PxY2QEP+WB+P6A=" 230 | }, 231 | "media-typer": { 232 | "version": "0.3.0", 233 | "resolved": "http://artifactory.intra.xiaojukeji.com:80/artifactory/api/npm/npm/media-typer/download/media-typer-0.3.0.tgz", 234 | "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" 235 | }, 236 | "merge-descriptors": { 237 | "version": "1.0.1", 238 | "resolved": "http://artifactory.intra.xiaojukeji.com:80/artifactory/api/npm/npm/merge-descriptors/download/merge-descriptors-1.0.1.tgz", 239 | "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" 240 | }, 241 | "methods": { 242 | "version": "1.1.2", 243 | "resolved": "http://artifactory.intra.xiaojukeji.com:80/artifactory/api/npm/npm/methods/download/methods-1.1.2.tgz", 244 | "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" 245 | }, 246 | "mime": { 247 | "version": "1.4.1", 248 | "resolved": "http://artifactory.intra.xiaojukeji.com:80/artifactory/api/npm/npm/mime/download/mime-1.4.1.tgz", 249 | "integrity": "sha1-Eh+evEnjdm8xGnbh+hyAA8SwOqY=" 250 | }, 251 | "mime-db": { 252 | "version": "1.30.0", 253 | "resolved": "http://artifactory.intra.xiaojukeji.com:80/artifactory/api/npm/npm/mime-db/download/mime-db-1.30.0.tgz", 254 | "integrity": "sha1-dMZD2i3Z1qRTmZY0ZbJtXKfXHwE=" 255 | }, 256 | "mime-types": { 257 | "version": "2.1.17", 258 | "resolved": "http://artifactory.intra.xiaojukeji.com:80/artifactory/api/npm/npm/mime-types/download/mime-types-2.1.17.tgz", 259 | "integrity": "sha1-Cdejk/A+mVp5+K+Fe3Cp4KsWVXo=", 260 | "requires": { 261 | "mime-db": "~1.30.0" 262 | } 263 | }, 264 | "ms": { 265 | "version": "2.0.0", 266 | "resolved": "http://artifactory.intra.xiaojukeji.com:80/artifactory/api/npm/npm/ms/download/ms-2.0.0.tgz", 267 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" 268 | }, 269 | "negotiator": { 270 | "version": "0.6.1", 271 | "resolved": "http://artifactory.intra.xiaojukeji.com:80/artifactory/api/npm/npm/negotiator/download/negotiator-0.6.1.tgz", 272 | "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=" 273 | }, 274 | "on-finished": { 275 | "version": "2.3.0", 276 | "resolved": "http://artifactory.intra.xiaojukeji.com:80/artifactory/api/npm/npm/on-finished/download/on-finished-2.3.0.tgz", 277 | "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", 278 | "requires": { 279 | "ee-first": "1.1.1" 280 | } 281 | }, 282 | "parseurl": { 283 | "version": "1.3.2", 284 | "resolved": "http://artifactory.intra.xiaojukeji.com:80/artifactory/api/npm/npm/parseurl/download/parseurl-1.3.2.tgz", 285 | "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=" 286 | }, 287 | "path-to-regexp": { 288 | "version": "0.1.7", 289 | "resolved": "http://artifactory.intra.xiaojukeji.com:80/artifactory/api/npm/npm/path-to-regexp/download/path-to-regexp-0.1.7.tgz", 290 | "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" 291 | }, 292 | "proxy-addr": { 293 | "version": "2.0.2", 294 | "resolved": "http://artifactory.intra.xiaojukeji.com:80/artifactory/api/npm/npm/proxy-addr/download/proxy-addr-2.0.2.tgz", 295 | "integrity": "sha1-ZXFQT0e7mI7IGAJT+F3X4UlSvew=", 296 | "requires": { 297 | "forwarded": "~0.1.2", 298 | "ipaddr.js": "1.5.2" 299 | } 300 | }, 301 | "qs": { 302 | "version": "6.5.1", 303 | "resolved": "http://artifactory.intra.xiaojukeji.com:80/artifactory/api/npm/npm/qs/download/qs-6.5.1.tgz", 304 | "integrity": "sha1-NJzfbu+J7EXBLX1es/wMhwNDptg=" 305 | }, 306 | "range-parser": { 307 | "version": "1.2.0", 308 | "resolved": "http://artifactory.intra.xiaojukeji.com:80/artifactory/api/npm/npm/range-parser/download/range-parser-1.2.0.tgz", 309 | "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=" 310 | }, 311 | "raw-body": { 312 | "version": "2.3.2", 313 | "resolved": "http://artifactory.intra.xiaojukeji.com:80/artifactory/api/npm/npm/raw-body/download/raw-body-2.3.2.tgz", 314 | "integrity": "sha1-vNYMd9Prk83gBQKVw/N5OJvIj4k=", 315 | "requires": { 316 | "bytes": "3.0.0", 317 | "http-errors": "1.6.2", 318 | "iconv-lite": "0.4.19", 319 | "unpipe": "1.0.0" 320 | } 321 | }, 322 | "reflect-metadata": { 323 | "version": "0.1.10", 324 | "resolved": "http://artifactory.intra.xiaojukeji.com:80/artifactory/api/npm/npm/reflect-metadata/download/reflect-metadata-0.1.10.tgz", 325 | "integrity": "sha1-tPg3BEFqytiZiMmxVjXUfgO5NEo=" 326 | }, 327 | "safe-buffer": { 328 | "version": "5.1.1", 329 | "resolved": "http://artifactory.intra.xiaojukeji.com:80/artifactory/api/npm/npm/safe-buffer/download/safe-buffer-5.1.1.tgz", 330 | "integrity": "sha1-iTMSr2myEj3vcfV4iQAWce6yyFM=" 331 | }, 332 | "send": { 333 | "version": "0.16.1", 334 | "resolved": "http://artifactory.intra.xiaojukeji.com:80/artifactory/api/npm/npm/send/download/send-0.16.1.tgz", 335 | "integrity": "sha1-pw4coh0TgsEdDZ9iMd6ygQgNerM=", 336 | "requires": { 337 | "debug": "2.6.9", 338 | "depd": "~1.1.1", 339 | "destroy": "~1.0.4", 340 | "encodeurl": "~1.0.1", 341 | "escape-html": "~1.0.3", 342 | "etag": "~1.8.1", 343 | "fresh": "0.5.2", 344 | "http-errors": "~1.6.2", 345 | "mime": "1.4.1", 346 | "ms": "2.0.0", 347 | "on-finished": "~2.3.0", 348 | "range-parser": "~1.2.0", 349 | "statuses": "~1.3.1" 350 | } 351 | }, 352 | "serve-static": { 353 | "version": "1.13.1", 354 | "resolved": "http://artifactory.intra.xiaojukeji.com:80/artifactory/api/npm/npm/serve-static/download/serve-static-1.13.1.tgz", 355 | "integrity": "sha1-TFfVNASnYdjy58HooYpH2/J4pxk=", 356 | "requires": { 357 | "encodeurl": "~1.0.1", 358 | "escape-html": "~1.0.3", 359 | "parseurl": "~1.3.2", 360 | "send": "0.16.1" 361 | } 362 | }, 363 | "setprototypeof": { 364 | "version": "1.1.0", 365 | "resolved": "http://artifactory.intra.xiaojukeji.com:80/artifactory/api/npm/npm/setprototypeof/download/setprototypeof-1.1.0.tgz", 366 | "integrity": "sha1-0L2FU2iHtv58DYGMuWLZ2RxU5lY=" 367 | }, 368 | "statuses": { 369 | "version": "1.3.1", 370 | "resolved": "http://artifactory.intra.xiaojukeji.com:80/artifactory/api/npm/npm/statuses/download/statuses-1.3.1.tgz", 371 | "integrity": "sha1-+vUbnrdKrvOzrPStX2Gr8ky3uT4=" 372 | }, 373 | "type-is": { 374 | "version": "1.6.15", 375 | "resolved": "http://artifactory.intra.xiaojukeji.com:80/artifactory/api/npm/npm/type-is/download/type-is-1.6.15.tgz", 376 | "integrity": "sha1-yrEPtJCeRByChC6v4a1kbIGARBA=", 377 | "requires": { 378 | "media-typer": "0.3.0", 379 | "mime-types": "~2.1.15" 380 | } 381 | }, 382 | "typescript": { 383 | "version": "2.5.3", 384 | "resolved": "http://artifactory.intra.xiaojukeji.com:80/artifactory/api/npm/npm/typescript/download/typescript-2.5.3.tgz", 385 | "integrity": "sha1-3z3Nw48764ANS8MiZGsEo/bKfw0=" 386 | }, 387 | "unpipe": { 388 | "version": "1.0.0", 389 | "resolved": "http://artifactory.intra.xiaojukeji.com:80/artifactory/api/npm/npm/unpipe/download/unpipe-1.0.0.tgz", 390 | "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" 391 | }, 392 | "utils-merge": { 393 | "version": "1.0.1", 394 | "resolved": "http://artifactory.intra.xiaojukeji.com:80/artifactory/api/npm/npm/utils-merge/download/utils-merge-1.0.1.tgz", 395 | "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" 396 | }, 397 | "vary": { 398 | "version": "1.1.2", 399 | "resolved": "http://artifactory.intra.xiaojukeji.com:80/artifactory/api/npm/npm/vary/download/vary-1.1.2.tgz", 400 | "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" 401 | } 402 | } 403 | } 404 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ts-demo", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "output/app.js", 6 | "scripts": { 7 | "dev": "tsc && node ./output/app.js" 8 | }, 9 | "author": "linkFly", 10 | "license": "MIT", 11 | "devDependencies": {}, 12 | "dependencies": { 13 | "typescript": "^2.5.3", 14 | "@types/express": "^4.0.37", 15 | "express": "^4.16.2", 16 | "reflect-metadata": "^0.1.10" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/app.ts: -------------------------------------------------------------------------------- 1 | import 'reflect-metadata' 2 | import router from './router' 3 | import * as express from 'express' 4 | 5 | const app = express() 6 | 7 | router(app) 8 | 9 | app.listen(8000, () => { 10 | console.log('Example app listening at http://localhost:8000') 11 | }) -------------------------------------------------------------------------------- /src/decorators/http-methods.ts: -------------------------------------------------------------------------------- 1 | export const symbolHttpMethodsKey = Symbol("router:httpMethod") 2 | 3 | export const httpGet = function (target: any, propertyKey: string) { 4 | // 挂载到调用装饰器的方法上 5 | Reflect.defineMetadata(symbolHttpMethodsKey, 'get', target, propertyKey) 6 | } 7 | 8 | export const httpPost = function (target: any, propertyKey: string) { 9 | Reflect.defineMetadata(symbolHttpMethodsKey, 'post', target, propertyKey) 10 | } -------------------------------------------------------------------------------- /src/decorators/path.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response } from 'express' 2 | 3 | export const symbolPathKey = Symbol.for('router:path') 4 | 5 | export let path = (path: string): Function => { 6 | return function (target: any, propertyKey: string, descriptor: TypedPropertyDescriptor) { 7 | 8 | Reflect.defineMetadata(symbolPathKey, path, target, propertyKey) 9 | if (!descriptor.value) return 10 | // 覆盖掉原来的 router method,在外层做封装 11 | let oldMethod = descriptor.value 12 | descriptor.value = function (req: Request, res: Response) { 13 | const params = Object.assign({}, req.body, req.query) 14 | let methodResult = oldMethod.call(this, params) 15 | // 输出返回结果 16 | res.send(methodResult) 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /src/decorators/validate.ts: -------------------------------------------------------------------------------- 1 | // 定义一个私有 key 2 | const requiredMetadataKey = Symbol.for('router:required') 3 | 4 | // 定义参数装饰器,大概思路就是把要校验的参数索引保存到成员中 5 | export const required = function (target, propertyKey: string, parameterIndex: number) { 6 | // 属性附加 7 | const rules = Reflect.getMetadata(requiredMetadataKey, target, propertyKey) || [] 8 | rules.push(parameterIndex) 9 | Reflect.defineMetadata(requiredMetadataKey, rules, target, propertyKey) 10 | } 11 | 12 | // 定义一个方法装饰器,从成员中获取要校验的参数进行校验 13 | export const validateEmptyStr = function (target, propertyKey: string, descriptor: PropertyDescriptor) { 14 | // 保存原来的方法 15 | let method = descriptor.value 16 | // 重写原来的方法 17 | descriptor.value = function () { 18 | let args = arguments 19 | // 看看成员里面有没有存的私有的对象 20 | const rules = Reflect.getMetadata(requiredMetadataKey, target, propertyKey) as Array 21 | if (rules && rules.length) { 22 | // 检查私有对象的 key 23 | rules.forEach(parameterIndex => { 24 | // 对应索引的参数进行校验 25 | if (!args[parameterIndex]) throw Error(`arguments${parameterIndex} is invalid`) 26 | }) 27 | } 28 | return method.apply(this, arguments) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/router.ts: -------------------------------------------------------------------------------- 1 | import { Router } from 'express' 2 | 3 | import { symbolHttpMethodsKey, httpGet, httpPost } from './decorators/http-methods' 4 | import { path, symbolPathKey } from './decorators/path' 5 | import { required, validateEmptyStr } from './decorators/validate' 6 | 7 | class User { 8 | @httpPost 9 | @path('/user/login') 10 | login() { 11 | return 'user login' 12 | } 13 | 14 | @httpGet 15 | @path('/user/exit') 16 | exit() { 17 | return 'user logout' 18 | } 19 | 20 | /** 21 | * 属性装饰器演示 22 | * @param v1 23 | */ 24 | @httpGet 25 | @path('/validate') 26 | @validateEmptyStr 27 | valid(@required v1: string) { 28 | console.log(v1) 29 | return v1 30 | } 31 | } 32 | 33 | export default (app: Router) => { 34 | let user = new User() 35 | for (let methodName in user) { 36 | let method = user[methodName] 37 | if (typeof method !== 'function') break 38 | // 反射得到挂载的数据 39 | let httpMethod = Reflect.getMetadata(symbolHttpMethodsKey, user, methodName) 40 | let path = Reflect.getMetadata(symbolPathKey, user, methodName) 41 | 42 | // app.get('/', () => any) 43 | app[httpMethod](path, method) 44 | } 45 | } -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "commonjs", 5 | "moduleResolution": "node", 6 | "lib": [ 7 | "es2015", 8 | "es2015.promise", 9 | "es2017.object" 10 | ], 11 | "outDir": "./output", 12 | "sourceMap": true, 13 | "strict": true, 14 | "isolatedModules": false, 15 | "experimentalDecorators": true, 16 | // 加上源数据 17 | "emitDecoratorMetadata": true, 18 | "noImplicitAny": false, 19 | "noImplicitReturns": true, 20 | "removeComments": true, 21 | "suppressImplicitAnyIndexErrors": true, 22 | "allowSyntheticDefaultImports": true, 23 | "allowUnreachableCode": true, 24 | "allowUnusedLabels": true, 25 | "allowJs": true, 26 | "listFiles": true, 27 | "baseUrl": "." 28 | }, 29 | "include": [ 30 | "**/*.ts" 31 | ], 32 | "exclude": [ 33 | "node_modules", 34 | "**/types/*" 35 | ], 36 | "compileOnSave": false 37 | } --------------------------------------------------------------------------------