23 KiB
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)。
流程图下半部分对应的是:
switch(type)
|
|-- 文件下载分支 2001/2002/2003/2004/16 -> 响应输出:二进制下载
|-- 文件上传分支 15 -> 响应输出:JSON 文本
|-- IP 查询分支 4000 -> 响应输出:文本 IP
|-- default -> DataLink.SqlWebCall(...) -> 响应输出:JSON 文本
也就是说,响应输出 是所有分支最终面向 HTTP 客户端的出口;DataLink.SqlWebCall 默认分支 是普通业务请求的二次路由器。
3. 响应输出:程序流程
3.1 默认响应头
ProcessRequest 进入后先执行:
context.Response.ContentType = "application/json";
因此除文件下载分支外,默认都会以 application/json 作为响应类型。需要注意:Type=4000 实际写出的是纯文本 IP,但 Content-Type 没有改成 text/plain。
3.2 二进制下载响应
适用 Type:
200120022003200416
典型代码模式:
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 下载前有一层空判断:
DataLink.ExePROCEDURE_Type16(jsonData, out bytes, out fileName, out suffix);
if (bytes == null) return;
如果下游没有返回文件二进制,则直接 return,不会写 JSON 错误,也不会写文件响应。调用方看到的可能是空响应。
3.4 Type=15 上传文件后的 JSON 响应
Type=15 是上传文件分支,不返回下载流。
流程:
读取 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 来自下游存储过程调用,常见返回:
[{"result":"1"}]
或:
[{"result":"0"}]
3.5 Type=4000 的文本响应
Type=4000 返回请求 IP:
context.Response.Write(userIP);
当前 IP 判断逻辑:
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:
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:
string responseText = "NULL";
try
{
...
}
catch(Exception err)
{
context.Response.Write(responseText);
}
异常后的响应行为:
- 不记录异常。
- 不设置 HTTP 状态码。
- 写出当前
responseText。 - 如果异常发生在下游调用前,返回
"NULL"。 - 如果异常发生在
responseText已赋值之后,可能返回旧结果。
4. DataLink.SqlWebCall 默认分支:入口流程
入口签名:
public static string SqlWebCall(int type, JsonData jsonData, jsonobj dataobj)
调用来源:
DataLinkMesWork.DataLink.SqlWebCall(type, jsonData, dataobj)
调用时机:MESCommonBase.ashx 的 switch(type) 没有匹配到 2001/2002/2003/2004/15/16/4000 时进入。
4.1 初始化数据库连接
SqlWebCall 开始时检查静态字段 initSystemIsOk:
if (!initSystemIsOk)
{
if (!InitSystemReg(out resultReg))
{
return resultReg;
}
}
InitSystemReg 当前实际逻辑:
connectionString = ConfigurationManager.AppSettings["ConnectionString"];
initSystemIsOk = true;
return true;
说明:
- 第一次进入
SqlWebCall时,从Web.config读取ConnectionString。 - 成功后设置静态标记,后续调用复用。
- 原先注册号、序列号、加密连接串等校验逻辑已经被注释。
4.2 二次分发
初始化后进入 switch(type):
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.csSQLCommon.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=... |
登录成功后会调用固定存储过程:
权限管理_Token_增加数据
校验 token 时调用:
权限管理_Token_查询数据
5.2 旧协议存储过程类
旧协议使用 BasicData.jsonobj dataobj,字段为:
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` |
旧协议参数解析在 SQLCommon.GetCmdParam(...) 中完成:
多个参数:用 & 分隔
单个参数:参数名=值=类型
输出参数:参数名=值=类型=output
支持类型:
intstringbooleandatetime- 默认按字符串处理
token 行为:
Type=1/2/3/4/5都是“如果 token 非空则校验”。- 不传 token 时通常不会拒绝执行。
5.3 新协议存储过程类
新协议使用 JsonData jsonData,字段从请求 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:查询型存储过程
方法:
ExePROCEDURE_Type11(jsonData)
流程:
读取 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:服务端分页存储过程
方法:
ExePROCEDURE_Type111(jsonData)
它解决“通讯服务器分页”的问题。
如果请求中包含:
{
"pageSize": 20,
"pageList": 1
}
程序会自动向参数数组追加 4 个参数:
| 参数名 | 值 | 说明 |
|---|---|---|
PageCurrent |
pageList |
当前页 |
PageSize |
pageSize |
每页条数 |
PageCount |
1111,type=int,output=1 |
输出参数 |
ItemCount |
1111,type=int,output=1 |
输出参数,总记录数 |
执行后返回:
{
"rows": [],
"total": "总记录数"
}
其中 total 来自输出参数 ItemCount。
Type=12:执行型存储过程
方法:
ExePROCEDURE_Type12(jsonData)
特点:
- 执行
Name/name指定存储过程。 - 不关注 DataSet。
- 成功返回:
[{"result":"1"}]
- 失败返回:
[{"result":"0"}]
- 如果有输出参数,返回:
{
"result": [{"result":"1"}],
"output": [{"参数名":"参数值"}]
}
Type=13:返回 DataSet JSON
方法:
ExePROCEDURE_Type13(jsonData)
特点:
- 执行
Name/name指定存储过程。 - 返回整个
DataSet的 JSON,而不是只返回第一张表。 - 有输出参数时包装为:
{
"result": { "DataSet序列化结果": "..." },
"output": [{"参数名":"参数值"}]
}
Type=21:新响应结构的存储过程
方法:
ExePROCEDURE_Type21(jsonData)
特点:
- 仍然执行
Name/name指定存储过程。 - 返回结构改为:
{
"code": "200",
"message": "",
"data": {}
}
或:
{
"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 数组字符串
常见格式:
{
"type": "11",
"name": "存储过程名",
"param": "[{\"name\":\"@工位号\",\"value\":\"OP10\",\"type\":\"string\"},{\"name\":\"@数量\",\"value\":\"10\",\"type\":\"int\"}]"
}
处理逻辑:
param 字符串
|
|-- JsonMapper.ToObject(param)
|
|-- 遍历数组
|-- name:SqlParameter 名
|-- value:参数值;如果是数组,转为逗号字符串并做 Unicode 解码
|-- type:存在时做类型转换
|-- output == "1":改为输出参数
|
|-- SQLCommon.ExecuteStoredProcedure / ExecuteSelectMesWork / ExecuteInsertMesWork
支持类型主要为:
intstringbooleanbooldatetime- 默认字符串
6.2 旧协议 Param 拼接字符串
Type 1/2 的旧协议格式:
@p1=value1=string&@p2=10=int&@out=0=int=output
SQLCommon.GetCmdParam(...) 处理逻辑:
Param
|
|-- 按 & 分割参数
|-- 过滤包含 == / null / undefined 的项
|-- 每项按 = 分割
|-- 长度 2:直接字符串参数
|-- 长度 3:按类型转换
|-- 长度 4 且第 4 项为 output:设为输出参数
Type 5 的旧协议格式不同:
@p1&value1&String|@p2&10&Int
7. 底层数据库执行链路
7.1 存储过程查询
典型调用链:
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 查询
典型调用链:
Type 1002 / 3 / 22
-> SQLCommon.ExecuteSelectMesWork 或 ExecuteDataTable / ExecuteDataset
-> SqlDataAdapter(sql, conn)
-> Fill(DataTable/DataSet)
-> JSON
7.3 SQL 增删改
典型调用链:
Type 1001 / 4
-> SQLCommon.ExecuteInsertMesWork 或 ExecuteNonQuery
-> SqlCommand.CommandText = SQL 文本
-> ExecuteNonQuery / ExecuteScalar
-> result=1 或 result=0
8. 返回格式汇总
8.1 成功返回首表 JSON
常见于:
Type=1Type=3Type=5Type=11Type=1002
示例:
[
{
"字段1": "值1",
"字段2": "值2"
}
]
8.2 成功/失败标志
常见于:
Type=2Type=4Type=12Type=1001
成功:
[{"result":"1"}]
失败:
[{"result":"0"}]
8.3 token 失败
部分方法在 token 非空且校验失败时返回:
[{"result":"3"}]
8.4 输出参数包装
普通输出参数:
{
"result": [{"result":"1"}],
"output": [{"@out":"123"}]
}
分页输出参数:
{
"rows": [],
"total": "123"
}
8.5 新结构响应
常见于:
Type=21Type=22
{
"code": "200",
"message": "",
"data": {}
}
失败:
{
"code": "500",
"message": "错误信息",
"data": {}
}
8.6 空或未支持 Type
SqlWebCall 对未匹配 Type 没有 default 处理,result 初始为空字符串。
因此 MESCommonBase.ashx default 分支可能返回空响应体:
""
如果进入外层异常,则可能返回:
NULL
9. 鉴权与 token 逻辑
CheckToken(token) 逻辑:
token == "" -> true
token 非空:
调 权限管理_Token_查询数据
|
|-- 存储过程报错 -> false
|-- 返回空表 -> false
|-- 返回有数据 -> true
关键影响:
- 多数业务方法只有在
token非空时才调用CheckToken。 - 如果请求不传 token,常见路径不会拒绝。
SqlWebCall入口层没有统一鉴权。- 因此权限控制依赖调用方是否传 token,以及下游存储过程是否自行校验。
10. 风险点与维护注意事项
10.1 响应层风险
Response.End()位于大 try 内,可能触发ThreadAbortException,随后外层 catch 写出responseText。- 下载失败时没有统一 JSON 错误,
Type=16的bytes == null会直接空返回。 Type=4000返回纯文本,但 Content-Type 仍可能是application/json。- 外层异常不记录日志,定位问题困难。
- 多种响应格式混用,前端必须按 Type 分别处理。
10.2 SqlWebCall 风险
- 客户端可通过
Name/name指定存储过程名、SQL 文本或表名。 Type=3/4/22/1001/1002/3001存在直接 SQL 执行能力。Type=7可根据请求表名执行DROP TABLE和CREATE TABLE。- token 校验不是统一强制的。
CommandTimeout = 0可能导致长时间阻塞。- 失败返回经常只有
result=0,缺少错误原因。
10.3 参数层风险
- 新协议
Param是 JSON 字符串,不是 JSON 对象;调用方需要二次转义。 - 输出参数类型转换依赖
type和output字段,格式错误会进入 catch 并返回result=0。 - 数组参数被拼成逗号字符串,数组元素里如果本身包含逗号,语义会丢失。
- 旧协议分隔符为
&、=、|,参数值包含这些字符时容易解析错误。
11. 建议改进方案
11.1 低成本修复
- 在
MESCommonBase.ashxdefault 分支前统一校验 token。 - 未支持 Type 返回明确错误:
{"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 返回结构:
{
"code": "200",
"message": "",
"data": {}
}
- 将 SQL 执行类 Type 与普通业务接口分离。
11.3 长期改造
- 按业务模块拆分接口,减少万能网关。
- 数据库账号按能力拆分权限。
- 移除明文连接字符串,使用部署环境密钥。
- 对文件下载、上传、SQL 执行加入审计日志和限流。
12. 典型流程示例
12.1 Type=11 查询型存储过程
请求:
{
"type": "11",
"name": "MES_工位_查询",
"param": "[{\"name\":\"@工位号\",\"value\":\"OP10\",\"type\":\"string\"}]",
"token": "token值"
}
流程:
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 分页型存储过程
请求:
{
"type": "111",
"name": "MES_工位_分页查询",
"param": "[{\"name\":\"@关键字\",\"value\":\"OP\",\"type\":\"string\"}]",
"pageSize": 20,
"pageList": 1
}
内部自动追加分页参数:
PageCurrent = 1
PageSize = 20
PageCount = 输出参数
ItemCount = 输出参数
响应:
{
"rows": [],
"total": "100"
}
12.3 Type=1002 SQL 查询
请求:
{
"type": "1002",
"name": "select * from Test where f1=@f1",
"param": "[{\"name\":\"@f1\",\"value\":\"T002\",\"type\":\"string\"}]"
}
流程:
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 白名单、异常日志和响应格式统一。