UNPKG

8.46 kBPlain TextView Raw
1/// <reference path="../error.d.ts" />
2export function get_root_path_prefix() {
3 return path.join(__dirname, '../../../../')
4}
5
6// 先加载框架层面的错误定义,再加载用户层面的错误定义,支持用户定义错误覆盖框架层面的错误多语言消息定义。
7require('../error')
8const path = require('path')
9require(path.join(get_root_path_prefix(), './app/error')) // 初始化应用扩展的错误
10
11import {get_suffix_ts_or_js} from './base'
12import {__framework_session_init__} from './session'
13
14require(path.join(get_root_path_prefix(), './app/global.out') + '.' + get_suffix_ts_or_js()) // 初始化应用一些枚举常量定义等方法
15
16// 初始化IoC容器
17require(path.join(get_root_path_prefix(), './app/plugins/container'))
18
19const fs = require('fs')
20const view = require('nunjucks')
21const _ = require('lodash')
22const typeis = require('type-is')
23const querystring = require('querystring')
24
25function get_app_entries_path_prefix() {
26 return path.join(get_root_path_prefix(), './app/entries')
27}
28
29// https://github.com/eqfox/http-body-parser 参照koa的源码正确解析post请求参数
30// 兼容application/json和application/x-www-form-urlencoded两种请求类型。
31export function parse_post_param(headers, body) {
32 try {
33 if (!body) {
34 return {}
35 }
36 if (typeis({headers}, ['json'])) {
37 return JSON.parse(body.toString(headers['content-encoding'] || 'utf8'))
38 } else if (typeof headers['Content-Type'] == 'string' && headers['Content-Type'].match(/application\/json/)) {
39 // bugfix API网关的字段类型值的赋值差异与type-is库的字段名称不兼容问题(content-type与Content-Type区别)
40 return JSON.parse(body.toString(headers['content-encoding'] || 'utf8'))
41 }
42 else if (typeis({headers}, ['urlencoded'])) {
43 return querystring.parse(body.toString(headers['content-encoding'] || 'utf8'))
44 } else if (typeof headers['Content-Type'] == 'string' && headers['Content-Type'] == 'application/x-www-form-urlencoded') {
45 // bugfix 线上乐高post请求参数解析不正确问题调试做兼容适配处理 TODO 待查原因为啥typeis库失效了
46 // application/x-www-form-urlencoded
47 return querystring.parse(body.toString(headers['content-encoding'] ||
48 headers['Content-Encoding'] || 'utf8'))
49 }
50 else {
51 xthrow(ERR$PARAM, {headers, body})
52 }
53 } catch (err) {
54 xthrow(err, {headers, body})
55 }
56}
57
58// 请求前置过滤处理
59async function filter_request_begin(api_path: string, param: any) {
60 const entry = require(api_path).default
61 xassert(entry)
62 const obj = new entry()
63 if (obj._) {
64 // 强制执行请求参数验证逻辑
65 xassert(_.isFunction(obj._))
66 await obj._(param)
67 }
68 // 对于MOCK还是正式请求路由的调用判断处理
69 let api_callback = obj.$$
70 if ('__mock__' in param) {
71 api_callback = obj.$ ? obj.$ : undefined
72 } else {
73 // 正式请求前置处理:auth鉴权、等等操作统一框架代码实现
74 if (obj.context) {
75 xassert(_.isFunction(obj.context))
76 const context = obj.context()
77 xassert(_.isPlainObject(context.param) && _.isFunction(context.auth))
78 await context.auth()
79 }
80 }
81 return api_callback
82}
83
84// web页面的请求支持get或post,get仅仅用于页面跳转,自动兼容get和post用于表单递交以及web端的接口数据的返回处理(测试场景下支持get线上环境只支持post协议)。
85async function web_request_process(api: string, param: any) {
86 let prefix = get_app_entries_path_prefix()
87 let api_path = `${prefix}/web/${api}`
88 let out: any = undefined
89 let is_html_request: boolean =
90 fs.existsSync(path.resolve(__dirname, `${prefix}/web/${api}.html`))
91
92 try {
93 let api_callback = await filter_request_begin(api_path, param)
94 xassert(_.isFunction(api_callback))
95 out = await api_callback(param) // 回调API对应的业务逻辑实现
96 if (is_html_request) {
97 // 仅在web请求html页面的时候如果检测到302跳转的时候忽略模板渲染直接返回
98 if (global['__redirect_url__'] !== undefined) {
99 return
100 }
101 // 将out转换到对应的html试图绑定之后再输出
102 view.configure(path.resolve(__dirname, `${prefix}/web`), {noCache: true, autoescape: false})
103 out = view.render(`./${api}.html`, out)
104 return out
105 } else {
106 // 对于web页面的关联ajax接口进行规范rest接口数据格式的统一处理
107 return {
108 success: true,
109 content: out,
110 errorLevel: undefined,
111 errorCode: undefined,
112 errorMsg: undefined,
113 }
114 }
115 } catch (err) {
116 // 业务错误报警处理 TODO 需要考虑错误忽略问题
117 await xwarn(err, api, param)
118
119 if (is_html_request) {
120 // 只对业务逻辑错误处理区分框架错误还是业务错误(业务错误error页面显示,框架错误http错误状态码)FIXME 需要改进掉
121 xassert(fs.existsSync(path.resolve(__dirname, `${prefix}/web/error.` + get_suffix_ts_or_js())) &&
122 fs.existsSync(path.resolve(__dirname, `${prefix}/web/error.html`)))
123 let api_callback = require(`${prefix}/web/error.` + get_suffix_ts_or_js()).default
124 xassert(_.isFunction(api_callback))
125 out = await api_callback(err)
126 view.configure(path.resolve(__dirname, `${prefix}/web`), {noCache: true, autoescape: false})
127 out = view.render(`./error.html`, out)
128 return out
129 } else {
130 let data = xerror(err)
131 return {
132 success: false,
133 content: data.param,
134 errorLevel: 'error',
135 errorCode: data.code,
136 errorMsg: data.msg,
137 }
138 }
139 }
140}
141
142// rest api接口自动兼容get或post请求处理协议(测试场景下支持get线上环境只支持post协议)
143async function rest_request_process(api: string, param: any) {
144 try {
145 let prefix = get_app_entries_path_prefix()
146 let api_path = `${prefix}/rest/${api}`
147 let api_callback = await filter_request_begin(api_path, param)
148 xassert(_.isFunction(api_callback))
149 let out = await api_callback(param) // 回调API对应的业务逻辑实现
150 return {
151 success: true,
152 content: out,
153 errorLevel: undefined,
154 errorCode: undefined,
155 errorMsg: undefined,
156 }
157 } catch (err) {
158 await xwarn(err, api, param)
159 let data = xerror(err)
160 return {
161 success: false,
162 content: data.param,
163 errorLevel: 'error',
164 errorCode: data.code,
165 errorMsg: data.msg,
166 }
167 }
168
169}
170
171// 返回格式是HTML还是JSON取决于API路径对应的ts文件是否存在同名的html模板文件,如果存在则返回HTML否则全部返回JSON数据。
172// 约定规范:POST请求都是restful的API接口(根据web和rest的路径进行具体区分),GET请求对应的路径有HTML就是WEB请求否则就是JSON请求。
173// 通过约定简化路由的定义。
174export async function request_process(api: string, param: any) {
175 await __framework_session_init__()
176 // 根据API的前缀命名规范自动识别应用类型是纯rest项目还是纯web项目进行对应的处理逻辑
177 if (/^\/web\//.test(api)) {
178 return await web_request_process(api.replace(/^\/web\//, ''), param)
179 } else if (/^\/rest\//.test(api)) {
180 let out = await rest_request_process(api.replace(/^\/rest\//, ''), param)
181 global['__redirect_url__'] = undefined
182 return out
183 } else {
184 // TODO 正确优化界面提示返回HTTPS状态码错误!!!!过滤无效URL定义。
185 xthrow(ERR$PARAM, [api, param])
186 }
187}
188
189// ajax返回成功结构说明
190// {
191// success: true,
192// content: 返回内容,{}/[]
193// }
194// ajax返回失败结构说明
195// {
196// success: false,
197// errorLevel:['info’, 'warn’, 'error’, 'fault’],
198// errorCode:错误码,
199// errorMsg:错误信息说明
200// }
\No newline at end of file