{"id":48504150,"url":"https://github.com/7kgame/jweb","last_synced_at":"2026-04-07T15:37:49.908Z","repository":{"id":34247111,"uuid":"173756754","full_name":"7kgame/jweb","owner":"7kgame","description":"A typeScript httpServer support annotation","archived":false,"fork":false,"pushed_at":"2022-09-29T20:53:02.000Z","size":459,"stargazers_count":0,"open_issues_count":11,"forks_count":2,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-08-17T10:49:52.554Z","etag":null,"topics":["annotation","httpserver","typescript"],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/7kgame.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-03-04T14:08:48.000Z","updated_at":"2021-03-23T00:00:40.000Z","dependencies_parsed_at":"2022-08-08T00:02:26.445Z","dependency_job_id":null,"html_url":"https://github.com/7kgame/jweb","commit_stats":null,"previous_names":[],"tags_count":12,"template":false,"template_full_name":null,"purl":"pkg:github/7kgame/jweb","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/7kgame%2Fjweb","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/7kgame%2Fjweb/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/7kgame%2Fjweb/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/7kgame%2Fjweb/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/7kgame","download_url":"https://codeload.github.com/7kgame/jweb/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/7kgame%2Fjweb/sbom","scorecard":{"id":5853,"data":{"date":"2025-08-11","repo":{"name":"github.com/7kgame/jweb","commit":"fa7ad4bf2d7110708f336b93f14b8fb009fdcfb8"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":1.7,"checks":[{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Code-Review","score":0,"reason":"Found 0/30 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"SAST","score":0,"reason":"no SAST tool detected","details":["Warn: no pull requests merged into dev branch"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"Vulnerabilities","score":0,"reason":"28 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: GHSA-968p-4wvh-cqc8","Warn: Project is vulnerable to: GHSA-9vrw-m88g-w75q","Warn: Project is vulnerable to: GHSA-gjph-xf5q-6mfq","Warn: Project is vulnerable to: GHSA-23vw-mhv5-grv5","Warn: Project is vulnerable to: GHSA-c429-5p7v-vgjp","Warn: Project is vulnerable to: GHSA-4rgj-8mq3-hggj","Warn: Project is vulnerable to: GHSA-3wqh-h42r-x8fq","Warn: Project is vulnerable to: GHSA-g9cg-h3jm-cwrc","Warn: Project is vulnerable to: GHSA-6chw-6frg-f759","Warn: Project is vulnerable to: GHSA-v88g-cgmw-v5xw","Warn: Project is vulnerable to: GHSA-93q8-gq69-wqmw","Warn: Project is vulnerable to: GHSA-v6h2-p8h4-qcjw","Warn: Project is vulnerable to: GHSA-3xgq-45jj-v275","Warn: Project is vulnerable to: GHSA-gxpj-cx7g-858c","Warn: Project is vulnerable to: GHSA-phwq-j96m-2c2q","Warn: Project is vulnerable to: GHSA-ghr5-ch3p-vcr6","Warn: Project is vulnerable to: GHSA-ww39-953v-wcq6","Warn: Project is vulnerable to: GHSA-43f8-2h32-f4cj","Warn: Project is vulnerable to: GHSA-p6mc-m468-83gw","Warn: Project is vulnerable to: GHSA-29mw-wpgm-hmr9","Warn: Project is vulnerable to: GHSA-35jh-r3h4-6jhm","Warn: Project is vulnerable to: GHSA-f8q6-p94x-37v3","Warn: Project is vulnerable to: GHSA-vh95-rmgr-6w4m","Warn: Project is vulnerable to: GHSA-xvch-5gv4-984h","Warn: Project is vulnerable to: GHSA-hj48-42vr-x3v9","Warn: Project is vulnerable to: GHSA-9wv6-86v2-598j","Warn: Project is vulnerable to: GHSA-c2qf-rxjj-qqgw","Warn: Project is vulnerable to: GHSA-52f5-9888-hmc6"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-14T13:31:41.603Z","repository_id":34247111,"created_at":"2025-08-14T13:31:41.603Z","updated_at":"2025-08-14T13:31:41.603Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31518634,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-07T03:10:19.677Z","status":"ssl_error","status_checked_at":"2026-04-07T03:10:13.982Z","response_time":105,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["annotation","httpserver","typescript"],"created_at":"2026-04-07T15:37:49.106Z","updated_at":"2026-04-07T15:37:49.891Z","avatar_url":"https://github.com/7kgame.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003c!-- vscode-markdown-toc --\u003e\n* 1. [Usage](#Usage)\n* 2. [Javascript和TypeScript基础学习](#JavascriptTypeScript)\n* 3. [1. Controller方法注解](#Controller)\n\t* 3.1. [1.1 注解的调用顺序](#)\n\t* 3.2. [1.2 注解中的错误处理](#-1)\n\t\t* 3.2.1. [如何抛出错误？](#-1)\n\t\t* 3.2.2. [如何处理错误？](#-1)\n\t* 3.3. [1.3 如何编写自定义函数注解？](#-1)\n\t\t* 3.3.1. [注解示例](#-1)\n\t\t* 3.3.2. [示例的依赖说明](#-1)\n\t\t* 3.3.3. [注解的编写步骤](#-1)\n\t* 3.4. [1.4 使用注解实现完整业务功能的示例](#-1)\n* 4. [2. Entity校验注解](#Entity)\n\t* 4.1. [2.1 使用示例](#-1)\n\t* 4.2. [2.2 注解的调用顺序](#-1)\n\t* 4.3. [2.4 jweb内置注解和说明](#jweb)\n\t\t* 4.3.1. [Min](#Min)\n\t\t* 4.3.2. [Max](#Max)\n\t\t* 4.3.3. [Size](#Size)\n\t\t* 4.3.4. [Required](#Required)\n\t\t* 4.3.5. [Type](#Type)\n\t* 4.4. [2.5 编写自己的验证器](#-1)\n\t\t* 4.4.1. [自定义验证器示例](#-1)\n\t\t* 4.4.2. [示例关键代码说明](#-1)\n\t\t* 4.4.3. [自定义验证器必须遵循的规则](#-1)\n\n\u003c!-- vscode-markdown-toc-config\n\tnumbering=true\n\tautoSave=true\n\t/vscode-markdown-toc-config --\u003e\n\u003c!-- /vscode-markdown-toc --\u003e\n# jweb\nA typeScript httpServer support annotation\n\n##  1. \u003ca name='Usage'\u003e\u003c/a\u003eUsage\n\n```\nnpm install\nnpm run build\nnpm run start\n```\n\n##  2. \u003ca name='JavascriptTypeScript'\u003e\u003c/a\u003eJavascript和TypeScript基础学习\nJavaScript基础学习（ES6标准）：http://es6.ruanyifeng.com/#README\n\nTypeScript基础学习：https://www.tslang.cn/docs/home.html\n\nejs模版引擎学习：https://ejs.co  |  https://ejs.bootcss.com/\n\n# JWeb Controller方法注解和Entity中校验注解\n\n\n##  3. \u003ca name='Controller'\u003e\u003c/a\u003e1. Controller方法注解\n\n###  3.1. \u003ca name=''\u003e\u003c/a\u003e1.1 注解的调用顺序\n\n先看一个样例（下面的方法都是某一个Controller类中的方法），process方法会在请求到来时被调用：\n\n```typescript\n  private preAround (ret) {\n    console.log('preAround', ret)\n  }\n\n  private postAround (ret) {\n    console.log('postAround', ret)\n  }  \n\n  private beforeCall (ret) {\n    console.log('beforeCall' , ret)\n    return ret\n  }\n\n  public afterCall (ret) {\n    console.log('afterCall', ret)\n    if (ret.err) {\n      return {\n        status: -1,\n        err: ret.err\n      }\n    } else {\n      return ret.data\n    }\n  }  \n  @Get('/process/{uid0}')\n  @Auth\n  @ResponseBody('json')\n  @Validation(UserEntity)\n  @Transactional\n  public async process (req: Request, res: Response, { uid0 }) {\n    const user: UserEntity = req.entity\n    console.log('inside call', user)\n    let u = await this.userService.hello(user)\n    let data = {\n      a: 1,\n      b: [2, 3, 4],\n      uid: uid0,\n      u: u\n    }\n    return data\n  }\n```\n\n在样例函数中，定义了preAround、postAround、beforeCall和afterCall，使用了5个注解，每个注解都有一个preCall和postCall的属性，这两个属性的值是一个函数，如下：\n\n```typescript\nAuth.preCall = function authPreCall(ret: any, param: string, req: Request, res: Response) {\n  if (param === 'ignore') {\n    return {\n      err: \"ignore\",\n      data: null,\n      from: Auth.name\n    }\n  }\n  return ret\n}\n\nAuth.postCall = function authPostCall(ret: any) {\n  return ret\n}\n```\n\n注解的调用基于上述两处代码，调用的原则是：\n\n\n\n* 首先调用beforeCall\n* 然后按照注解使用从上向下的顺序调用preCall\n* 然后调用Controller类中的preAround\n* 然后调用被注解的方法，在上例中调用process方法\n* 然后调用Controller类中的postAround\n* 最后按照注解使用从下到上的顺序调用postCall\n\n在上面的示例中，调用链是（Get方法只会在初始化的时候调用，用于注册路由，不会参与到此处的调用链中）：\n\nbeforeCall =\u003e Auth.preCall =\u003e ResponseBody.preCall =\u003e ... =\u003e preAround=\u003e process =\u003e postAround =\u003e Transanctionl.postCall =\u003e ... =\u003e Auth.postCall =\u003e afterCall\n\n当然，没有为注解定义postCall或者preCall，那么跳过相关调用。\n\n\n\n###  3.2. \u003ca name='-1'\u003e\u003c/a\u003e1.2 注解中的错误处理\n\n​\t每一个注解的统一返回格式是：\n\n```js\n{\n    err,\n    data\n}\n```\n\n前一个注解调用的返回结果会传递给后一个注解调用，例如：\n\n```js\nAuth.preCall = function authPreCall(ret: any, param: string, req: Request, res: Response) {\n  if (param === 'ignore') {\n    return {\n      err: \"ignore\",\n      data: null\n    }\n  }\n  return ret\n}\n```\n\n上述preCall函数中的参数ret，是上一个调用的返回值，Auth.preCall的返回值也会传给下一个调用。**如果没有return，会使用之前在调用链上传递的ret传给下一个调用**\n\n\n\n####  3.2.1. \u003ca name='-1'\u003e\u003c/a\u003e如何抛出错误？\n\n如果注解调用中发生了错误，有两种错误抛出方式：\n\n* **如果需要继续执行调用链**：在返回值中设置err属性，该属性可以是字符串，也可以是对象，只要不为null，就表明错误发生。这意味着你可以定义自己的错误结构，方便后面的错误捕获和处理。\n* **如果需要中断调用链**：直接在preCall或者postCall中return null，当检测到调用链中某一个调用的返回值是null时，不会继续执行调用链。**注意：这时框架不会响应请求，如果你希望中断调用链，那么你必须使用函数参数中的Request和Response自定义HTTP响应，如设置状态码，设置响应头，设置响应体。例如，在Auth中如果验证失败，你可能会返回401状态码**\n\n如果路由函数中要抛出错误，需要throw BusinessException，BusinessException位于jbean包中。例如：\n\n```typescript\n  @Get('/process/{uid0}')\n  @Auth\n  @ResponseBody('json')\n  @Validation(UserEntity)\n  @Transactional\n  public async process (req: Request, res: Response, { uid0 }) {\n    const user: UserEntity = req.entity\n    let u = await this.userService.hello(user)\n\n    throw new BusinessException('test Exception') // 在路由函数中抛出错误\n    let data = {\n      a: 1,\n      b: [2, 3, 4],\n      uid: uid0,\n      u: u\n    }\n    return data\n  }\n```\n\n\n\n####  3.2.2. \u003ca name='-1'\u003e\u003c/a\u003e如何处理错误？\n\n​\t如果是设置err的错误抛出方式，自定义的注解需要自行定义错误处理方式。通过参数中的上一个函数的返回值中的err属性，可以监听到前面的调用链中是否发生了错误。发生与未发生错误时如何处理完全由注解自行决定。**如果调用链上的错误始终未被处理，则服务器会返回500错误**\n\n关于自定义注解的编写请参阅: [1.3 如何编写自定义注解？](#1.3 如何编写自定义注解？)\n\n**注意：jweb提供的内置注解，比如Validation，如果检测到错误，会直接return ret**\n\n\n\n如果中断调用链，必须自行处理响应。\n\n\n\n###  3.3. \u003ca name='-1'\u003e\u003c/a\u003e1.3 如何编写自定义函数注解？\n\n####  3.3.1. \u003ca name='-1'\u003e\u003c/a\u003e注解示例\n\n先看一个自定义注解的例子：\n\n```typescript\nimport { AnnotationType, annotationHelper, BeanFactory } from 'jbean'\nimport { Request, Response } from 'jweb'\nimport { jsonEncode, xmlEncode } from '../../lib/utils'\n\nexport default function ResponseBody (component?: any, type?: any) {\n  return annotationHelper(arguments, callback)\n}\n\nconst callback = function (annoType: AnnotationType, ctor: object | Function) {\n  if (annoType === AnnotationType.clz) {\n    BeanFactory.addBeanMeta(AnnotationType.clz, ctor, null, ResponseBody, [arguments[2]])\n  } else if (annoType === AnnotationType.method) {\n    BeanFactory.addBeanMeta(AnnotationType.method, ctor, arguments[2], ResponseBody, [arguments[4]])\n  }\n}\n\nResponseBody.preCall = function rbdPreCall(ret: any, type: string, req: Request, res: Response) {\n  switch (type) {\n    case 'json':\n      res.type('application/json')\n      break\n    case 'xml':\n      res.type('application/xml')\n      break\n    default:\n      break\n  }\n  console.log(\"response body precall\", ret)\n  return ret\n}\n\nResponseBody.postCall = function rbdPostCall(ret: any, type: string, req: Request, res: Response) {\n  console.log(\"jsonbody line 31\", ret)\n  switch (type) {\n    case 'json':\n      if (typeof ret === 'object') {\n        ret.data = jsonEncode(ret.data)\n      }\n      break\n    case 'xml':\n      ret.data = xmlEncode(ret.data)\n      break\n    default:\n      break\n  }\n  return ret\n}\n\n```\n\n​\t上述代码定义了一个名为ResponseBody的注解，该注解的主要功能是，根据@ResponseBody('{type}')来决定返回的数据格式类型，type可选值是json或者xml。接下来我们讨论该注解的详细定义过程。\n\n####  3.3.2. \u003ca name='-1'\u003e\u003c/a\u003e示例的依赖说明\n\n* 首先，注解相关的处理都在jbean包中，这里我们从JBean中引入了AnnotationType、annotationHelper、BeanFactory。其中：\n\n  * AnnotationType：用于定义注解的类型，jweb中支持的注解类型有三种，类、方法、域。分别对应AnnotationType.clz，AnnotationType.method，AnnotationType.field\n\n  * annotationHelper：jbean提供的分析注解参数的函数，该函数接收两个参数，args和callback，args是参数；callback是回调函数，在annotationHelper执行完毕后调用。annotationHelper会根据args参数计算出注解类型，以及能够从[typescript注解](\u003chttps://www.tslang.cn/docs/handbook/decorators.html\u003e)获取对应的**构造函数/原型对象、键名、descriptor对象等**，细节请参阅[typescript注解](\u003chttps://www.tslang.cn/docs/handbook/decorators.html\u003e)。拿到这些数据后，annotationHelper会将其作为参数传入给callback。**callback的详细参数列表请参阅[callback参数列表](#callback参数列表)**\n\n    annotationHelper统一处理[TypeScript装饰器工厂](\u003chttps://www.tslang.cn/docs/handbook/decorators.html#decorator-factories\u003e)和装饰器直接使用，比如Auth('ignore')和@Auth分别对应工厂调用和直接调用\n\n  * BeanFactory：管理Bean、BeanMeta等信息的工厂类。**如果我们希望我们定义的注解的preCall和postCall在请求到来时调用，需要将对应的BeanMeta注册到BeanFactory中**\n\n####  3.3.3. \u003ca name='-1'\u003e\u003c/a\u003e注解的编写步骤\n\n##### 1. 导入依赖：\n\n```js\nimport { AnnotationType, annotationHelper, BeanFactory } from 'jbean'\nimport { Request, Response } from 'jweb'\nimport { jsonEncode, xmlEncode } from '../../lib/utils'\n```\n\n注意：jsonEncode，xmlEncode是自行编写格式处理工具，与jweb和jbean无关。\n\n##### 2. 定义注解：\n\n```typescript\nexport default function ResponseBody (component?: any, type?: any) {\n  return annotationHelper(arguments, callback)\n}\n```\n\n​\t其中ResponseBody是我们定义的注解名，当作为工厂调用时，即以@ResponseBody('参数1', '参数2')形式调用时，ResponseBody函数的参数就是工厂调用时传入的参数。否则其参数是typescript装饰器传入的参数。\n\n​\t我们使用时，可直接传arguments，annotationHelper会帮助我们处理所有细节，并将统一的参数形式传到callback中。接下来我们详细解释callback的使用。\n\n##### 3. 定义callback方法：\n\n```typescript\nconst callback = function (annoType: AnnotationType, ctor: object | Function) {\n  if (annoType === AnnotationType.clz) {\n    BeanFactory.addBeanMeta(AnnotationType.clz, ctor, null, ResponseBody, [arguments[2]])\n  } else if (annoType === AnnotationType.method) {\n    BeanFactory.addBeanMeta(AnnotationType.method, ctor, arguments[2], ResponseBody, [arguments[4]])\n  }\n}\n```\n\n###### callback参数列表\n\n```js\nconst callback = function(annoType: AnnotationType, ctor: Function|object, field: string, descriptor: PropertyDescriptor, ...args)\n```\n\n参数说明：\n\n* annoType：注解的类型，取值为AnnotationType.clz，AnnotationType.method，AnnotationType.field，分别代表类，方法，域\n* ctor：构造函数或者原型对象\n* field：如果被注解的是方法或者域，则该值存在且为键名\n* descriptor：如果被注解的是方法，则该值存在且为该方法对应的描述符对象\n* ...args：注解工厂调用时传入的参数，例如@ResponseBody('json')中传入的‘json'\n\n**注意，如果你希望你的注解在被注解的函数调用时调用，请确保将其添加到BeanFactory的BeanMeta中，并定义响应的preCall、postCall函数**。因为callback仅会在初始化时调用。\n\n注册BeanMeta请参考下面的代码：\n\n```typescript\nBeanFactory.addBeanMeta(AnnotationType.method, ctor, arguments[2], ResponseBody, [arguments[4]])\n```\n\n\n\n##### 4. 定义preCall、postCall函数\n\n​\tpreCall可以在路由函数被调用前调用，可以起到拦截作用，如果你的业务中需要拦截器功能的话，定义preCall就可以实现。postCall在路由函数被调用后调用，可以对数据进行加工。preCall、postCall的使用、参数请参考上述示例。\n\n\n\n###  3.4. \u003ca name='-1'\u003e\u003c/a\u003e1.4 使用注解实现完整业务功能的示例\n\n一个完整的Controller示例代码如下：\n\n```typescript\nimport { Autowired } from 'jbean'\nimport { BaseController, Controller, Get, Post, Request, Response, Transactional, Validation } from 'jweb'\nimport UserService from '../lib/account/UserService'\nimport PayService from '../lib/account/PayService'\nimport Auth from '../annos/Auth'\nimport ResponseBody from '../annos/response_body'\nimport UserEntity from '../lib/account/entity/user'\n\n@Controller('/user')\n@Transactional\nexport default class User extends BaseController {\n\n  @Autowired('userService0')\n  private userService: UserService\n\n  @Autowired\n  private payService: PayService\n\n  constructor () {\n    super()\n  }\n\n  private beforeCall (ret) {\n    return ret\n  }\n\n  public afterCall (ret) {\n    console.log('afterCall', ret)\n    if (ret.err) {\n      return {\n        status: -1,\n        err: ret.err\n      }\n    } else {\n      return ret.data\n    }\n  }\n\n  @Get('/process/{uid0}')\n  @Auth\n  @ResponseBody('json')\n  @Validation(UserEntity)\n  @Transactional\n  public async process (req: Request, res: Response, { uid0 }) {\n    const user: UserEntity = req.entity\n    console.log('inside call', user)\n    let u = await this.userService.hello(user)\n    let data = {\n      a: 1,\n      b: [2, 3, 4],\n      uid: uid0,\n      u: u\n    }\n    return data\n  }\n}\n```\n\n这里我们只关注和process方法有关的部分，Get('/process/{uid0}')注册了一个动态路由，其路由参数是uid0。然后会一次依据[1.1 注解的调用顺序](#1.1 注解的调用顺序)调用注解和方法。**注意，注解调用链的参数传递与process方法无关，process方法只关注自身的业务处理，所以这里没有返回{err,data,from}这种结构，也不会被传入ret，process方法直接返回data**\n\n\n\n在这个示例中，我们将错误的统一拦截放到了afterCall中：\n\n```typescript\npublic afterCall (ret) {\n    console.log('afterCall', ret)\n    if (ret.err) {\n      return {\n        status: -1,\n        errmessage: ret.err\n      }\n    } else {\n      return {\n          status: 1\n          data: ret.data\n      }\n    }\n  }\n```\n\n我们获取前面传过来的ret，判断是否发生了错误，如果发生了错误，将业务定义的错误码以及错误信息返回。\n\n如果成功，将成功码和获取到的数据返回。\n\n##  4. \u003ca name='Entity'\u003e\u003c/a\u003e2. Entity校验注解\n\n###  4.1. \u003ca name='-1'\u003e\u003c/a\u003e2.1 使用示例\n\nuser.ts\n\n```typescript\nimport { Entity, Type } from 'jweb'\nimport {Required, Min, Max, Size} from 'jweb'\n\n@Entity\nexport default class User {\n\n  @Type('string')\n  @Required(\"uid是必填的参数\")\n  public uid = undefined\n\n  @Required\n  @Size(20, 30, 'name的长度应该位于20-30之间')\n  public name = undefined\n\n  @Type('number', true)\n  @Required(\"age is required\")\n  @Min(18)\n  @Max(100)\n  public age = undefined\n\n}\n```\n\n代码中定义了一个User类，类上有@Entity注解，@Entity表明这是一个实体类，与某一张数据库表相对应，会自动根据类名生成表名，如上例中，对应user表。默认的表名规则是下划线分割驼峰式命名，例如：PersonLikeBeer会对应表person_like_beer。\n\n可以通过给@Entity传参数可以自定义表名，例如：@Entity('user_log')表名当前的类对应user_log表。\n\n**注意到每一个字段都给了一个默认值undefined，这是因为如果不给默认值，typescript编译后不会生成对应的属性**\n\n可以通过给表中的字段添加验证注解，来进行参数的校验，在上例中：\n\n```typescript\n  @Type('number', true)\n  @Required(\"age is required\")\n  @Min(18)\n  @Max(100)\n  public age = undefined\n```\n\n* @Type('number', true) 表明这个字段必须是数字，true表示运行进行转换以期望得到正确的类型。**注意，一般情况下请将其设置成true，因为获取到的参数默认都是字符串，例如：可能表单提交的是21，但是获取的参数会是'21'，这时候会导致验证不通过。但如果你不想其他类型转为字符串，可以不写或者传false**。\n* @Required(\"age is required\")表明这个字段是必须的，括号里面的是验证不正确时的提示信息，如果不给定，会使用默认的提示信息\n* @Min(18)最小值18\n* @Max(100)最大值100\n\n###  4.2. \u003ca name='-1'\u003e\u003c/a\u003e2.2 注解的调用顺序\n\n注解会按照使用顺序，从上到下调用，如果一个字段有多个验证规则，中间有一个规则不通过时，不会继续验证后面的规则，直接使用该注解的错误信息。\n\n每一个注解验证后会返回一个值，这个值会传给下一个验证注解，最后的验证注解返回的值会赋值给对应的字段。\n\n所有注解的最后一个参数都是验证出错时提供的消息，如果没有提供该参数，将会使用默认值。\n\n###  4.3. \u003ca name='jweb'\u003e\u003c/a\u003e2.4 jweb内置注解和说明\n\n####  4.3.1. \u003ca name='Min'\u003e\u003c/a\u003eMin\n\n使用：@Min(minval:number，mes?: string)\n\n规则：对应字段的值是否比@Min指定的值大\n\n####  4.3.2. \u003ca name='Max'\u003e\u003c/a\u003eMax\n\n使用：@Max(minval:number，mes?: string)\n\n规则：对应字段的值是否比@Max指定的值小\n\n####  4.3.3. \u003ca name='Size'\u003e\u003c/a\u003eSize\n\n使用：@Size(minval:number，maxval?:number, mes?: string)\n\n规则：对应字段的值是否在指定的区间中\n\n####  4.3.4. \u003ca name='Required'\u003e\u003c/a\u003eRequired\n\n使用：@Requred(mes?: string)\n\n规则：必须的字段\n\n####  4.3.5. \u003ca name='Type'\u003e\u003c/a\u003eType\n\n使用：@Type(type?: string)\n\n规则：字段的数据类型，可选值string、number、integer\n\n###  4.4. \u003ca name='-1'\u003e\u003c/a\u003e2.5 编写自己的验证器\n\n####  4.4.1. \u003ca name='-1'\u003e\u003c/a\u003e自定义验证器示例\n\n```typescript\nimport { AnnotationType, annotationHelper, BeanFactory } from 'jbean'\n\nexport default function Max(maxVal: number, mes?: string) {\n  return annotationHelper([maxVal, mes], callback)\n}\nfunction validate(maxVal: number) {\n  return (val):{valid: boolean, val: any} =\u003e {\n    if (val \u003c= maxVal) {\n      return {valid: true, val: val}\n    } else {\n      return { valid: false, val: null}\n    }\n  }\n}\nfunction message(field: string, maxVal:number, mes?: string) {\n  if (mes) {\n    return () =\u003e mes\n  } else {\n    return () =\u003e `the value of ${field} must smaller than ${maxVal}`\n  }\n}\nMax['validate'] = {}\nconst callback = function(annoType: AnnotationType, ctor: Function, field: string, maxVal:number, mes?: string) {\n  // add descriptor info into BeanFactory, using it in Validation\n  Max['validate'][field] = {\n    validate: validate(maxVal),\n    message: message(field, maxVal, mes)\n  }\n  BeanFactory.addBeanMeta(annoType, ctor, field, Max)\n}\n```\n\n####  4.4.2. \u003ca name='-1'\u003e\u003c/a\u003e示例关键代码说明\n\n每一个验证器需要两个方法，一个是validate，用于验证是否满足规则，另一个是message，用于返回验证失败时的消息。**请确保传给Max\\['validate'\\]\\[field\\]的validate和message属性的值均是函数。validate和message返回的函数都会被传入一个参数——被验证的字段的值**\n\n我们将这两个方法与挂在到对应注解方法的validate属性上，在示例中是Max['validate']，然后:\n\n```typescript\nMax['validate'][field] = {\n    validate: validate(maxVal),\n    message: message(field, maxVal, mes)\n  }\n```\n\n使用field是因为不同的域上可能有相同的注解，而验证规则可能是闭包相关的，所以这里为每一个域都绑定validate和message方法。\n\n最后需要把注解注册到BeanFactory中，采用让其validate和message函数在验证时被调用。\n\n####  4.4.3. \u003ca name='-1'\u003e\u003c/a\u003e自定义验证器必须遵循的规则\n\n1. 确保验证器函数有如下属性，且validate和message必须是函数。\n\n   ```typescript\n   Max['validate'][field] = {\n       validate: validate(maxVal),\n       message: message(field, maxVal, mes)\n   }\n   ```\n\n2. 在{ validate: validate(maxVal), message: message(field, maxVal, mes) }中，validate返回值的结构必须是下面的形式：\n\n   ```typescript\n   {\n       valid: boolean, // 表示验证是否成功，如果成功，其值为true，否则为false\n       val: any // 验证之后应该返回的值，这个值会传给下一个验证器\n   }\n   ```\n\n   **val的值会传给下一个验证器，最后一个验证器的val值会赋值给实体，注意验证成功或者失败时返回的val值**\n\n   message返回值必须是一个字符串，用作验证失败时的提示信息。\n\n3. 确保验证器被注册到BeanFactory中，调用如下API注册\n\n   ```typescript\n   BeanFactory.addBeanMeta(annoType, ctor, field, Max)\n   ```","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F7kgame%2Fjweb","html_url":"https://awesome.ecosyste.ms/projects/github.com%2F7kgame%2Fjweb","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F7kgame%2Fjweb/lists"}