940 lines
23 KiB
Markdown
940 lines
23 KiB
Markdown
# MESCommonBase 响应输出与 SqlWebCall 默认分支详细文档
|
||
|
||
文档范围:流程图中下半部分的两块内容。
|
||
|
||
- `响应输出`
|
||
- `DataLink.SqlWebCall 默认分支`
|
||
|
||
关联入口:`MES_Manage/submit/MESCommonBase.ashx`
|
||
|
||
生成时间:2026-07-02
|
||
|
||
## 1. 相关程序清单
|
||
|
||
| 程序文件 | 关键位置 | 作用 |
|
||
| --- | --- | --- |
|
||
| `MES_Manage/submit/MESCommonBase.ashx` | `ProcessRequest` 的 `switch(type)` 和外层 `catch` | 负责把不同 Type 的结果写入 HTTP 响应 |
|
||
| `02DataLinkMesWork/DataLinkMesWork.SqlWebCall.cs` | `DataLink.SqlWebCall(int type, JsonData jsonData, jsonobj dataobj)` | 默认分支二次分发入口 |
|
||
| `02DataLinkMesWork/DataLinkMesWork.SqlWebCall.cs` | `InitSystemReg(...)` | 初始化数据库连接字符串 |
|
||
| `02DataLinkMesWork/DataLinkMesWork.SqlWebCall.cs` | `GetString_JsonData(...)` | 从新协议 JSON 读取 `Type/Name/Param/token` 等字段 |
|
||
| `02DataLinkMesWork/DataLinkMesWork.cs` | `ExePROCEDURE_Type*`、`ExecuteInsertMesWork`、`ExecuteSelectMesWork` | 具体 SQL、存储过程、登录、分页、返回格式处理 |
|
||
| `02DataLinkMesWork/P0.MES.Common/bizDataAccess/SQLCommon.cs` | `ExecuteStoredProcedure`、`ExecuteDataTable`、`ExecuteDataset`、`ExecuteInsertMesWork`、`ExecuteSelectMesWork` | 底层 ADO.NET 数据库执行封装 |
|
||
| `01BasicData/BasicData/BasicData.cs` | `jsonobj` | 旧协议请求对象 |
|
||
| `MES_Manage/Web.config` | `appSettings["ConnectionString"]` | 默认数据库连接配置来源 |
|
||
|
||
## 2. 这部分在总流程中的位置
|
||
|
||
`MESCommonBase.ashx` 先解析请求体或 `param` 参数,拿到 `type` 后进入 `switch(type)`。
|
||
|
||
流程图下半部分对应的是:
|
||
|
||
```text
|
||
switch(type)
|
||
|
|
||
|-- 文件下载分支 2001/2002/2003/2004/16 -> 响应输出:二进制下载
|
||
|-- 文件上传分支 15 -> 响应输出:JSON 文本
|
||
|-- IP 查询分支 4000 -> 响应输出:文本 IP
|
||
|-- default -> DataLink.SqlWebCall(...) -> 响应输出:JSON 文本
|
||
```
|
||
|
||
也就是说,`响应输出` 是所有分支最终面向 HTTP 客户端的出口;`DataLink.SqlWebCall 默认分支` 是普通业务请求的二次路由器。
|
||
|
||
## 3. 响应输出:程序流程
|
||
|
||
### 3.1 默认响应头
|
||
|
||
`ProcessRequest` 进入后先执行:
|
||
|
||
```csharp
|
||
context.Response.ContentType = "application/json";
|
||
```
|
||
|
||
因此除文件下载分支外,默认都会以 `application/json` 作为响应类型。需要注意:`Type=4000` 实际写出的是纯文本 IP,但 Content-Type 没有改成 `text/plain`。
|
||
|
||
### 3.2 二进制下载响应
|
||
|
||
适用 Type:
|
||
|
||
- `2001`
|
||
- `2002`
|
||
- `2003`
|
||
- `2004`
|
||
- `16`
|
||
|
||
典型代码模式:
|
||
|
||
```csharp
|
||
HttpContext.Current.Response.ContentType = "application/octet-stream";
|
||
HttpContext.Current.Response.AddHeader(
|
||
"Content-Disposition",
|
||
"attachment; filename=" + HttpUtility.UrlEncode(fileName, Encoding.UTF8)
|
||
);
|
||
HttpContext.Current.Response.AddHeader(
|
||
"Access-Control-Expose-Headers",
|
||
"Content-Disposition"
|
||
);
|
||
HttpContext.Current.Response.BinaryWrite(bytes);
|
||
HttpContext.Current.Response.Flush();
|
||
HttpContext.Current.Response.End();
|
||
```
|
||
|
||
字段含义:
|
||
|
||
| 字段 | 含义 |
|
||
| --- | --- |
|
||
| `ContentType = application/octet-stream` | 告诉浏览器按二进制文件处理 |
|
||
| `Content-Disposition = attachment` | 触发浏览器下载,而不是直接打开 |
|
||
| `filename=...` | 下载文件名,使用 UTF-8 URL 编码处理中文文件名 |
|
||
| `Access-Control-Expose-Headers` | 允许跨域前端读取 `Content-Disposition` 响应头 |
|
||
| `BinaryWrite(bytes)` | 写出文件二进制 |
|
||
| `Flush()` | 刷新响应缓冲区 |
|
||
| `End()` / `Close()` | 结束响应 |
|
||
|
||
不同文件分支的差异:
|
||
|
||
| Type | 生成文件的程序 | 文件名来源 | 结束方式 |
|
||
| --- | --- | --- | --- |
|
||
| `2001` | `ExcelWebCall.ExcelFile(...)` | `fileName + "." + fileExtension` | `Response.End()` |
|
||
| `2002` | `ExcelWebCall.ExcelFilePdf(...)` | `fileName + "." + fileExtension`,通常为 PDF | `Response.End()` |
|
||
| `2003` | `ExcelWebCall.ExcelFile(...)` | `fileName + "." + dataimg[0]` | `Response.Close()` |
|
||
| `2004` | `DataLink.ExePROCEDURE_Type2004(...)` | 数据库返回文件名和扩展名 | `Response.End()` |
|
||
| `16` | `DataLink.ExePROCEDURE_Type16(...)` | 数据库返回文件名和后缀 | `Response.End()` |
|
||
|
||
### 3.3 Type=16 的空文件处理
|
||
|
||
`Type=16` 下载前有一层空判断:
|
||
|
||
```csharp
|
||
DataLink.ExePROCEDURE_Type16(jsonData, out bytes, out fileName, out suffix);
|
||
if (bytes == null) return;
|
||
```
|
||
|
||
如果下游没有返回文件二进制,则直接 `return`,不会写 JSON 错误,也不会写文件响应。调用方看到的可能是空响应。
|
||
|
||
### 3.4 Type=15 上传文件后的 JSON 响应
|
||
|
||
`Type=15` 是上传文件分支,不返回下载流。
|
||
|
||
流程:
|
||
|
||
```text
|
||
读取 context.Request.Files
|
||
|
|
||
|-- 有文件:
|
||
| 读取第一个文件 InputStream 为 byte[]
|
||
| 从 FileName 拆 name/suffix
|
||
| 调 DataLink.ExePROCEDURE_Type15(jsonData, name, suffix, bytes)
|
||
| Response.Write(responseText)
|
||
|
|
||
|-- 无文件:
|
||
name=""
|
||
suffix=""
|
||
bytes=new byte[1]
|
||
调同一个 ExePROCEDURE_Type15
|
||
Response.Write(responseText)
|
||
```
|
||
|
||
`responseText` 来自下游存储过程调用,常见返回:
|
||
|
||
```json
|
||
[{"result":"1"}]
|
||
```
|
||
|
||
或:
|
||
|
||
```json
|
||
[{"result":"0"}]
|
||
```
|
||
|
||
### 3.5 Type=4000 的文本响应
|
||
|
||
`Type=4000` 返回请求 IP:
|
||
|
||
```csharp
|
||
context.Response.Write(userIP);
|
||
```
|
||
|
||
当前 IP 判断逻辑:
|
||
|
||
```csharp
|
||
if (context.Request.ServerVariables["HTTP_X_FORWARDED_FOR"] != "")
|
||
userIP = context.Request.ServerVariables["REMOTE_ADDR"];
|
||
else
|
||
userIP = context.Request.ServerVariables["HTTP_X_FORWARDED_FOR"];
|
||
if (userIP == null || userIP == "")
|
||
userIP = context.Request.UserHostAddress;
|
||
```
|
||
|
||
注意:这里逻辑疑似写反。通常 `HTTP_X_FORWARDED_FOR` 有值时才优先取它;当前代码在有转发头时反而取 `REMOTE_ADDR`。
|
||
|
||
### 3.6 default 分支的 JSON 响应
|
||
|
||
普通业务请求进入 default:
|
||
|
||
```csharp
|
||
responseText = DataLinkMesWork.DataLink.SqlWebCall(type, jsonData, dataobj);
|
||
context.Response.Headers.Remove("Server");
|
||
context.Response.Write(responseText);
|
||
```
|
||
|
||
特点:
|
||
|
||
- 响应体完全由 `SqlWebCall` 返回值决定。
|
||
- 尝试移除 `Server` 响应头。
|
||
- 仍使用入口处默认的 `application/json`。
|
||
- `SqlWebCall` 对未支持 Type 返回空字符串。
|
||
|
||
### 3.7 外层异常响应
|
||
|
||
`ProcessRequest` 外层包了一个总 `try/catch`:
|
||
|
||
```csharp
|
||
string responseText = "NULL";
|
||
|
||
try
|
||
{
|
||
...
|
||
}
|
||
catch(Exception err)
|
||
{
|
||
context.Response.Write(responseText);
|
||
}
|
||
```
|
||
|
||
异常后的响应行为:
|
||
|
||
- 不记录异常。
|
||
- 不设置 HTTP 状态码。
|
||
- 写出当前 `responseText`。
|
||
- 如果异常发生在下游调用前,返回 `"NULL"`。
|
||
- 如果异常发生在 `responseText` 已赋值之后,可能返回旧结果。
|
||
|
||
## 4. DataLink.SqlWebCall 默认分支:入口流程
|
||
|
||
入口签名:
|
||
|
||
```csharp
|
||
public static string SqlWebCall(int type, JsonData jsonData, jsonobj dataobj)
|
||
```
|
||
|
||
调用来源:
|
||
|
||
```csharp
|
||
DataLinkMesWork.DataLink.SqlWebCall(type, jsonData, dataobj)
|
||
```
|
||
|
||
调用时机:`MESCommonBase.ashx` 的 `switch(type)` 没有匹配到 `2001/2002/2003/2004/15/16/4000` 时进入。
|
||
|
||
### 4.1 初始化数据库连接
|
||
|
||
`SqlWebCall` 开始时检查静态字段 `initSystemIsOk`:
|
||
|
||
```csharp
|
||
if (!initSystemIsOk)
|
||
{
|
||
if (!InitSystemReg(out resultReg))
|
||
{
|
||
return resultReg;
|
||
}
|
||
}
|
||
```
|
||
|
||
`InitSystemReg` 当前实际逻辑:
|
||
|
||
```csharp
|
||
connectionString = ConfigurationManager.AppSettings["ConnectionString"];
|
||
initSystemIsOk = true;
|
||
return true;
|
||
```
|
||
|
||
说明:
|
||
|
||
- 第一次进入 `SqlWebCall` 时,从 `Web.config` 读取 `ConnectionString`。
|
||
- 成功后设置静态标记,后续调用复用。
|
||
- 原先注册号、序列号、加密连接串等校验逻辑已经被注释。
|
||
|
||
### 4.2 二次分发
|
||
|
||
初始化后进入 `switch(type)`:
|
||
|
||
```text
|
||
SqlWebCall(type,jsonData,dataobj)
|
||
|
|
||
|-- 8888 / 5001 / 5002:加密、注册/改密、登录
|
||
|-- 1 / 2 / 5:旧协议存储过程
|
||
|-- 11 / 111 / 12 / 13 / 21:新协议存储过程
|
||
|-- 1001 / 1002 / 3 / 4 / 7 / 22 / 3001:SQL、建表导入、SQL 命令
|
||
```
|
||
|
||
`SqlWebCall` 自身不直接访问数据库;它只选择具体方法。真正数据库执行在:
|
||
|
||
- `DataLinkMesWork.cs`
|
||
- `SQLCommon.cs`
|
||
|
||
## 5. SqlWebCall 支持的 Type 明细
|
||
|
||
### 5.1 登录、注册、加密类
|
||
|
||
| Type | 方法 | 输入对象 | 主要流程 | 返回 |
|
||
| --- | --- | --- | --- | --- |
|
||
| `8888` | `GetEncryptStr(jsonData)` | 新协议 `JsonData` | 读取 `Name/name`,用 `AesKeyIvGenerator.Encrypt(name1, SN, SN)` 加密 | 加密字符串,失败默认 `[]` |
|
||
| `5001` | `ExePROCEDURE_Type11_AddUser(jsonData)` | 新协议 `JsonData` | 读取 `Param` 数组,找到 `Password` 后 MD5,再执行 `Name` 指定存储过程 | 存储过程首表 JSON 或 `[]` |
|
||
| `5002` | `ExePROCEDURE_Type11_Login(jsonData)` | 新协议 `JsonData` | 解密前端密码,执行 `Name` 指定存储过程查账号,再比对数据库密码,成功后写入 token | 成功返回用户表 JSON 并追加 `token`,失败返回 `result=0,msg=...` |
|
||
|
||
登录成功后会调用固定存储过程:
|
||
|
||
```text
|
||
权限管理_Token_增加数据
|
||
```
|
||
|
||
校验 token 时调用:
|
||
|
||
```text
|
||
权限管理_Token_查询数据
|
||
```
|
||
|
||
### 5.2 旧协议存储过程类
|
||
|
||
旧协议使用 `BasicData.jsonobj dataobj`,字段为:
|
||
|
||
```csharp
|
||
public string Type;
|
||
public string ModularID;
|
||
public string Name;
|
||
public string Param;
|
||
public string UserID;
|
||
public string Pagination;
|
||
public string token;
|
||
public bool HasReturn;
|
||
```
|
||
|
||
| Type | 方法 | 执行对象 | 参数格式 | 返回 |
|
||
| --- | --- | --- | --- | --- |
|
||
| `1` | `ExePROCEDURE_Type1(dataobj)` | `Name` 的第一个 `&` 前作为存储过程名 | `@p=value=type&@p2=value=type` | 首个 DataTable JSON;分页时返回带总数结构;失败 `result=0` |
|
||
| `2` | `ExePROCEDURE_Type2(dataobj)` | `Name` 指定存储过程 | 同 Type 1 | 成功 `result=1`,失败 `result=0` |
|
||
| `5` | `ExePROCEDURE_Type5(dataobj)` | `Name` 指定存储过程 | `@p&value&type|@p2&value&type` | 首个 DataTable JSON;参数错误返回中文错误字符串 |
|
||
|
||
旧协议参数解析在 `SQLCommon.GetCmdParam(...)` 中完成:
|
||
|
||
```text
|
||
多个参数:用 & 分隔
|
||
单个参数:参数名=值=类型
|
||
输出参数:参数名=值=类型=output
|
||
```
|
||
|
||
支持类型:
|
||
|
||
- `int`
|
||
- `string`
|
||
- `boolean`
|
||
- `datetime`
|
||
- 默认按字符串处理
|
||
|
||
token 行为:
|
||
|
||
- `Type=1/2/3/4/5` 都是“如果 token 非空则校验”。
|
||
- 不传 token 时通常不会拒绝执行。
|
||
|
||
### 5.3 新协议存储过程类
|
||
|
||
新协议使用 `JsonData jsonData`,字段从请求 JSON 中读取:
|
||
|
||
```json
|
||
{
|
||
"type": "11",
|
||
"name": "存储过程名",
|
||
"param": "[{\"name\":\"@p\",\"value\":\"v\",\"type\":\"string\"}]",
|
||
"token": "..."
|
||
}
|
||
```
|
||
|
||
通用字段读取由 `GetString_JsonData(...)` 完成,兼容大小写:
|
||
|
||
| 大写字段 | 小写字段 | 含义 |
|
||
| --- | --- | --- |
|
||
| `Type` | `type` | Type |
|
||
| `Name` | `name` | 存储过程名或 SQL 文本 |
|
||
| `Param` | `param` | 参数数组字符串 |
|
||
| `UserID` | `userID` | 用户 |
|
||
| `Pagination` | `pagination` | 旧分页参数 |
|
||
| `HasReturn` | `hasReturn` | 是否返回 |
|
||
| `ModularID` | `modularID` | 模块 |
|
||
| `token` | `token` | token |
|
||
|
||
#### Type=11:查询型存储过程
|
||
|
||
方法:
|
||
|
||
```csharp
|
||
ExePROCEDURE_Type11(jsonData)
|
||
```
|
||
|
||
流程:
|
||
|
||
```text
|
||
读取 Name/Param/token
|
||
|
|
||
|-- token 非空:CheckToken(token),失败返回 [{"result":"3"}]
|
||
|
|
||
|-- Param 为空:无参数执行存储过程
|
||
|-- Param 非空:解析 JSON 数组为 SqlParameter[]
|
||
|
|
||
|-- SQLCommon.ExecuteStoredProcedure(... out DataSet ...)
|
||
|
|
||
|-- 有结果表:
|
||
|-- 有 pageSize/pageList:内存分页,返回分页 JSON
|
||
|-- 无分页字段:返回首个 DataTable JSON
|
||
|
|
||
|-- 有 output 参数:包装为 {"result":..., "output":...}
|
||
```
|
||
|
||
#### Type=111:服务端分页存储过程
|
||
|
||
方法:
|
||
|
||
```csharp
|
||
ExePROCEDURE_Type111(jsonData)
|
||
```
|
||
|
||
它解决“通讯服务器分页”的问题。
|
||
|
||
如果请求中包含:
|
||
|
||
```json
|
||
{
|
||
"pageSize": 20,
|
||
"pageList": 1
|
||
}
|
||
```
|
||
|
||
程序会自动向参数数组追加 4 个参数:
|
||
|
||
| 参数名 | 值 | 说明 |
|
||
| --- | --- | --- |
|
||
| `PageCurrent` | `pageList` | 当前页 |
|
||
| `PageSize` | `pageSize` | 每页条数 |
|
||
| `PageCount` | `1111`,`type=int`,`output=1` | 输出参数 |
|
||
| `ItemCount` | `1111`,`type=int`,`output=1` | 输出参数,总记录数 |
|
||
|
||
执行后返回:
|
||
|
||
```json
|
||
{
|
||
"rows": [],
|
||
"total": "总记录数"
|
||
}
|
||
```
|
||
|
||
其中 `total` 来自输出参数 `ItemCount`。
|
||
|
||
#### Type=12:执行型存储过程
|
||
|
||
方法:
|
||
|
||
```csharp
|
||
ExePROCEDURE_Type12(jsonData)
|
||
```
|
||
|
||
特点:
|
||
|
||
- 执行 `Name/name` 指定存储过程。
|
||
- 不关注 DataSet。
|
||
- 成功返回:
|
||
|
||
```json
|
||
[{"result":"1"}]
|
||
```
|
||
|
||
- 失败返回:
|
||
|
||
```json
|
||
[{"result":"0"}]
|
||
```
|
||
|
||
- 如果有输出参数,返回:
|
||
|
||
```json
|
||
{
|
||
"result": [{"result":"1"}],
|
||
"output": [{"参数名":"参数值"}]
|
||
}
|
||
```
|
||
|
||
#### Type=13:返回 DataSet JSON
|
||
|
||
方法:
|
||
|
||
```csharp
|
||
ExePROCEDURE_Type13(jsonData)
|
||
```
|
||
|
||
特点:
|
||
|
||
- 执行 `Name/name` 指定存储过程。
|
||
- 返回整个 `DataSet` 的 JSON,而不是只返回第一张表。
|
||
- 有输出参数时包装为:
|
||
|
||
```json
|
||
{
|
||
"result": { "DataSet序列化结果": "..." },
|
||
"output": [{"参数名":"参数值"}]
|
||
}
|
||
```
|
||
|
||
#### Type=21:新响应结构的存储过程
|
||
|
||
方法:
|
||
|
||
```csharp
|
||
ExePROCEDURE_Type21(jsonData)
|
||
```
|
||
|
||
特点:
|
||
|
||
- 仍然执行 `Name/name` 指定存储过程。
|
||
- 返回结构改为:
|
||
|
||
```json
|
||
{
|
||
"code": "200",
|
||
"message": "",
|
||
"data": {}
|
||
}
|
||
```
|
||
|
||
或:
|
||
|
||
```json
|
||
{
|
||
"code": "500",
|
||
"message": "错误信息",
|
||
"data": {}
|
||
}
|
||
```
|
||
|
||
注意:这里是否成功主要看底层返回的 `result` 字符串是否为空,不完全等同于数据库是否返回数据。
|
||
|
||
### 5.4 SQL 文本和建表导入类
|
||
|
||
| Type | 方法 | 执行对象 | 返回 |
|
||
| --- | --- | --- | --- |
|
||
| `1001` | `ExecuteInsertMesWork(jsonData)` | `Name/name` 作为 SQL 文本,参数来自 `Param` JSON 数组 | 成功 `result=1`;有输出时返回 `output` |
|
||
| `1002` | `ExecuteSelectMesWork(jsonData)` | `Name/name` 作为 SQL 查询文本,参数来自 `Param` JSON 数组 | DataTable JSON;支持 `pageSize/pageList` 内存分页 |
|
||
| `3` | `ExePROCEDURE_Type3(dataobj)` | `dataobj.Name` 作为 SQL 查询文本 | DataTable JSON |
|
||
| `4` | `ExePROCEDURE_Type4(dataobj)` | `dataobj.Name` 作为非查询 SQL 文本 | 成功 `result=1` |
|
||
| `7` | `ExePROCEDURE_Type7(jsonData)` | `Name/name` 作为表名,`Param` 作为行数据 | 删除并重建表,批量插入,成功 `result=1` |
|
||
| `22` | `ExePROCEDURE_Type22(dataobj)` | `dataobj.Name` 作为 SQL 文本 | `{ code, message, data }` |
|
||
| `3001` | `DbCallType1003_SqlCmd.SqlExec(jsonData)` | SQL 命令执行入口 | 由该方法决定 |
|
||
|
||
这类 Type 的共性:
|
||
|
||
- 执行对象来自客户端 `Name/name`。
|
||
- 参数虽然使用 `SqlParameter` 绑定,但 SQL 文本或表名本身不是固定白名单。
|
||
- 需要调用方和网络边界可信,否则风险很高。
|
||
|
||
## 6. SqlWebCall 参数处理流程
|
||
|
||
### 6.1 新协议 Param 数组字符串
|
||
|
||
常见格式:
|
||
|
||
```json
|
||
{
|
||
"type": "11",
|
||
"name": "存储过程名",
|
||
"param": "[{\"name\":\"@工位号\",\"value\":\"OP10\",\"type\":\"string\"},{\"name\":\"@数量\",\"value\":\"10\",\"type\":\"int\"}]"
|
||
}
|
||
```
|
||
|
||
处理逻辑:
|
||
|
||
```text
|
||
param 字符串
|
||
|
|
||
|-- JsonMapper.ToObject(param)
|
||
|
|
||
|-- 遍历数组
|
||
|-- name:SqlParameter 名
|
||
|-- value:参数值;如果是数组,转为逗号字符串并做 Unicode 解码
|
||
|-- type:存在时做类型转换
|
||
|-- output == "1":改为输出参数
|
||
|
|
||
|-- SQLCommon.ExecuteStoredProcedure / ExecuteSelectMesWork / ExecuteInsertMesWork
|
||
```
|
||
|
||
支持类型主要为:
|
||
|
||
- `int`
|
||
- `string`
|
||
- `boolean`
|
||
- `bool`
|
||
- `datetime`
|
||
- 默认字符串
|
||
|
||
### 6.2 旧协议 Param 拼接字符串
|
||
|
||
Type 1/2 的旧协议格式:
|
||
|
||
```text
|
||
@p1=value1=string&@p2=10=int&@out=0=int=output
|
||
```
|
||
|
||
`SQLCommon.GetCmdParam(...)` 处理逻辑:
|
||
|
||
```text
|
||
Param
|
||
|
|
||
|-- 按 & 分割参数
|
||
|-- 过滤包含 == / null / undefined 的项
|
||
|-- 每项按 = 分割
|
||
|-- 长度 2:直接字符串参数
|
||
|-- 长度 3:按类型转换
|
||
|-- 长度 4 且第 4 项为 output:设为输出参数
|
||
```
|
||
|
||
Type 5 的旧协议格式不同:
|
||
|
||
```text
|
||
@p1&value1&String|@p2&10&Int
|
||
```
|
||
|
||
## 7. 底层数据库执行链路
|
||
|
||
### 7.1 存储过程查询
|
||
|
||
典型调用链:
|
||
|
||
```text
|
||
MESCommonBase.ashx default
|
||
-> DataLink.SqlWebCall(...)
|
||
-> DataLink.ExePROCEDURE_Type11/13/21(...)
|
||
-> SQLCommon.ExecuteStoredProcedure(...)
|
||
-> SqlConnection / SqlDataAdapter
|
||
-> DataSet / DataTable
|
||
-> JsonHelper / JsonConvert 序列化
|
||
-> Response.Write(responseText)
|
||
```
|
||
|
||
`SQLCommon.ExecuteStoredProcedure(... out DataSet ...)` 的关键行为:
|
||
|
||
- 创建 `SqlConnection`。
|
||
- 创建 `SqlCommand(procedureName, conn)`。
|
||
- 设置 `CommandType = StoredProcedure`。
|
||
- 设置 `CommandTimeout = 0`,即不限制命令超时。
|
||
- 添加 `SqlParameter[]`。
|
||
- 使用 `SqlDataAdapter.Fill(ds)` 填充结果集。
|
||
- 异常时写 `ApplicationLog`,并通过 `errorMessage` 返回异常字符串。
|
||
|
||
### 7.2 SQL 查询
|
||
|
||
典型调用链:
|
||
|
||
```text
|
||
Type 1002 / 3 / 22
|
||
-> SQLCommon.ExecuteSelectMesWork 或 ExecuteDataTable / ExecuteDataset
|
||
-> SqlDataAdapter(sql, conn)
|
||
-> Fill(DataTable/DataSet)
|
||
-> JSON
|
||
```
|
||
|
||
### 7.3 SQL 增删改
|
||
|
||
典型调用链:
|
||
|
||
```text
|
||
Type 1001 / 4
|
||
-> SQLCommon.ExecuteInsertMesWork 或 ExecuteNonQuery
|
||
-> SqlCommand.CommandText = SQL 文本
|
||
-> ExecuteNonQuery / ExecuteScalar
|
||
-> result=1 或 result=0
|
||
```
|
||
|
||
## 8. 返回格式汇总
|
||
|
||
### 8.1 成功返回首表 JSON
|
||
|
||
常见于:
|
||
|
||
- `Type=1`
|
||
- `Type=3`
|
||
- `Type=5`
|
||
- `Type=11`
|
||
- `Type=1002`
|
||
|
||
示例:
|
||
|
||
```json
|
||
[
|
||
{
|
||
"字段1": "值1",
|
||
"字段2": "值2"
|
||
}
|
||
]
|
||
```
|
||
|
||
### 8.2 成功/失败标志
|
||
|
||
常见于:
|
||
|
||
- `Type=2`
|
||
- `Type=4`
|
||
- `Type=12`
|
||
- `Type=1001`
|
||
|
||
成功:
|
||
|
||
```json
|
||
[{"result":"1"}]
|
||
```
|
||
|
||
失败:
|
||
|
||
```json
|
||
[{"result":"0"}]
|
||
```
|
||
|
||
### 8.3 token 失败
|
||
|
||
部分方法在 token 非空且校验失败时返回:
|
||
|
||
```json
|
||
[{"result":"3"}]
|
||
```
|
||
|
||
### 8.4 输出参数包装
|
||
|
||
普通输出参数:
|
||
|
||
```json
|
||
{
|
||
"result": [{"result":"1"}],
|
||
"output": [{"@out":"123"}]
|
||
}
|
||
```
|
||
|
||
分页输出参数:
|
||
|
||
```json
|
||
{
|
||
"rows": [],
|
||
"total": "123"
|
||
}
|
||
```
|
||
|
||
### 8.5 新结构响应
|
||
|
||
常见于:
|
||
|
||
- `Type=21`
|
||
- `Type=22`
|
||
|
||
```json
|
||
{
|
||
"code": "200",
|
||
"message": "",
|
||
"data": {}
|
||
}
|
||
```
|
||
|
||
失败:
|
||
|
||
```json
|
||
{
|
||
"code": "500",
|
||
"message": "错误信息",
|
||
"data": {}
|
||
}
|
||
```
|
||
|
||
### 8.6 空或未支持 Type
|
||
|
||
`SqlWebCall` 对未匹配 Type 没有 default 处理,`result` 初始为空字符串。
|
||
|
||
因此 `MESCommonBase.ashx` default 分支可能返回空响应体:
|
||
|
||
```text
|
||
""
|
||
```
|
||
|
||
如果进入外层异常,则可能返回:
|
||
|
||
```text
|
||
NULL
|
||
```
|
||
|
||
## 9. 鉴权与 token 逻辑
|
||
|
||
`CheckToken(token)` 逻辑:
|
||
|
||
```text
|
||
token == "" -> true
|
||
token 非空:
|
||
调 权限管理_Token_查询数据
|
||
|
|
||
|-- 存储过程报错 -> false
|
||
|-- 返回空表 -> false
|
||
|-- 返回有数据 -> true
|
||
```
|
||
|
||
关键影响:
|
||
|
||
- 多数业务方法只有在 `token` 非空时才调用 `CheckToken`。
|
||
- 如果请求不传 token,常见路径不会拒绝。
|
||
- `SqlWebCall` 入口层没有统一鉴权。
|
||
- 因此权限控制依赖调用方是否传 token,以及下游存储过程是否自行校验。
|
||
|
||
## 10. 风险点与维护注意事项
|
||
|
||
### 10.1 响应层风险
|
||
|
||
1. `Response.End()` 位于大 try 内,可能触发 `ThreadAbortException`,随后外层 catch 写出 `responseText`。
|
||
2. 下载失败时没有统一 JSON 错误,`Type=16` 的 `bytes == null` 会直接空返回。
|
||
3. `Type=4000` 返回纯文本,但 Content-Type 仍可能是 `application/json`。
|
||
4. 外层异常不记录日志,定位问题困难。
|
||
5. 多种响应格式混用,前端必须按 Type 分别处理。
|
||
|
||
### 10.2 SqlWebCall 风险
|
||
|
||
1. 客户端可通过 `Name/name` 指定存储过程名、SQL 文本或表名。
|
||
2. `Type=3/4/22/1001/1002/3001` 存在直接 SQL 执行能力。
|
||
3. `Type=7` 可根据请求表名执行 `DROP TABLE` 和 `CREATE TABLE`。
|
||
4. token 校验不是统一强制的。
|
||
5. `CommandTimeout = 0` 可能导致长时间阻塞。
|
||
6. 失败返回经常只有 `result=0`,缺少错误原因。
|
||
|
||
### 10.3 参数层风险
|
||
|
||
1. 新协议 `Param` 是 JSON 字符串,不是 JSON 对象;调用方需要二次转义。
|
||
2. 输出参数类型转换依赖 `type` 和 `output` 字段,格式错误会进入 catch 并返回 `result=0`。
|
||
3. 数组参数被拼成逗号字符串,数组元素里如果本身包含逗号,语义会丢失。
|
||
4. 旧协议分隔符为 `&`、`=`、`|`,参数值包含这些字符时容易解析错误。
|
||
|
||
## 11. 建议改进方案
|
||
|
||
### 11.1 低成本修复
|
||
|
||
- 在 `MESCommonBase.ashx` default 分支前统一校验 token。
|
||
- 未支持 Type 返回明确错误:
|
||
|
||
```json
|
||
{"code":"400","message":"Unsupported Type","data":null}
|
||
```
|
||
|
||
- 下载分支改为 `HttpContext.Current.ApplicationInstance.CompleteRequest()`,避免 `Response.End()` 的线程中止异常。
|
||
- `Type=4000` 设置 `ContentType = "text/plain"`,并修正代理 IP 判断逻辑。
|
||
- 外层 catch 记录异常:Type、Name、IP、异常堆栈。
|
||
|
||
### 11.2 中期改造
|
||
|
||
- 建立 `Type + Name` 白名单,禁止任意存储过程名和 SQL 文本。
|
||
- 将 `Param` 从“JSON 字符串”改为真正的 JSON 数组字段。
|
||
- 统一 JSON 返回结构:
|
||
|
||
```json
|
||
{
|
||
"code": "200",
|
||
"message": "",
|
||
"data": {}
|
||
}
|
||
```
|
||
|
||
- 将 SQL 执行类 Type 与普通业务接口分离。
|
||
|
||
### 11.3 长期改造
|
||
|
||
- 按业务模块拆分接口,减少万能网关。
|
||
- 数据库账号按能力拆分权限。
|
||
- 移除明文连接字符串,使用部署环境密钥。
|
||
- 对文件下载、上传、SQL 执行加入审计日志和限流。
|
||
|
||
## 12. 典型流程示例
|
||
|
||
### 12.1 Type=11 查询型存储过程
|
||
|
||
请求:
|
||
|
||
```json
|
||
{
|
||
"type": "11",
|
||
"name": "MES_工位_查询",
|
||
"param": "[{\"name\":\"@工位号\",\"value\":\"OP10\",\"type\":\"string\"}]",
|
||
"token": "token值"
|
||
}
|
||
```
|
||
|
||
流程:
|
||
|
||
```text
|
||
MESCommonBase.ashx default
|
||
-> SqlWebCall(11,jsonData,dataobj)
|
||
-> InitSystemReg 读取连接字符串
|
||
-> ExePROCEDURE_Type11
|
||
-> GetString_JsonData 读取 name/param/token
|
||
-> token 非空则 CheckToken
|
||
-> Param 转 SqlParameter[]
|
||
-> SQLCommon.ExecuteStoredProcedure
|
||
-> JsonHelper.DataTableToJson
|
||
-> Response.Write(JSON)
|
||
```
|
||
|
||
### 12.2 Type=111 分页型存储过程
|
||
|
||
请求:
|
||
|
||
```json
|
||
{
|
||
"type": "111",
|
||
"name": "MES_工位_分页查询",
|
||
"param": "[{\"name\":\"@关键字\",\"value\":\"OP\",\"type\":\"string\"}]",
|
||
"pageSize": 20,
|
||
"pageList": 1
|
||
}
|
||
```
|
||
|
||
内部自动追加分页参数:
|
||
|
||
```text
|
||
PageCurrent = 1
|
||
PageSize = 20
|
||
PageCount = 输出参数
|
||
ItemCount = 输出参数
|
||
```
|
||
|
||
响应:
|
||
|
||
```json
|
||
{
|
||
"rows": [],
|
||
"total": "100"
|
||
}
|
||
```
|
||
|
||
### 12.3 Type=1002 SQL 查询
|
||
|
||
请求:
|
||
|
||
```json
|
||
{
|
||
"type": "1002",
|
||
"name": "select * from Test where f1=@f1",
|
||
"param": "[{\"name\":\"@f1\",\"value\":\"T002\",\"type\":\"string\"}]"
|
||
}
|
||
```
|
||
|
||
流程:
|
||
|
||
```text
|
||
SqlWebCall(1002)
|
||
-> ExecuteSelectMesWork(jsonData)
|
||
-> SQLCommon.ExecuteSelectMesWork(sql, params, connectionString, out dt)
|
||
-> DataTableToJson
|
||
```
|
||
|
||
说明:参数值通过 `SqlParameter` 绑定,但 SQL 文本本身来自请求。
|
||
|
||
## 13. 结论
|
||
|
||
流程图中的 `响应输出` 是整个 `MESCommonBase.ashx` 的 HTTP 出口,负责二进制下载、JSON 文本、IP 文本和异常兜底输出。`DataLink.SqlWebCall 默认分支` 是普通业务请求的二次分发中心,它按 Type 调用不同数据库执行方法,覆盖登录、存储过程、SQL 查询、SQL 增删改、分页和新结构返回。
|
||
|
||
这部分程序的核心问题不是流程复杂,而是执行权过于通用:客户端可通过 `type + name + param` 控制大量数据库行为。维护时应优先关注统一鉴权、Type/Name 白名单、异常日志和响应格式统一。
|