IIS部署ASP.NET应用偶发登录空引用异常(本地运行正常)
ASP.NET 登录功能本地正常、IIS部署偶发空引用问题排查
错误现象
本地运行应用时登录功能完全正常,部署到IIS后偶发登录故障:部分请求可正常登录,部分请求抛出空引用异常,报错位置为:Line 58: if (ds.Tables[0].Rows.Count > 0)
报错详情
对象引用未设置到对象的实例。说明:当前Web请求执行过程中出现未处理异常,请检查堆栈跟踪信息,定位错误来源及触发代码位置。
异常详情:System.NullReferenceException: 对象引用未设置到对象的实例。
堆栈跟踪:
StudForm.Klogin.btn_Login_Click(Object sender, EventArgs e) 位于 C:\inetpub\wwwroot\StudForm\RIForm\Klogin.aspx.cs:59 System.Web.UI.WebControls.Button.OnClick(EventArgs e) +9767618 System.Web.UI.WebControls.Button.RaisePostBackEvent(String eventArgument) +204 System.Web.UI.WebControls.Button.System.Web.UI.IPostBackEventHandler.RaisePostBackEvent(String eventArgument) +12 System.Web.UI.Page.RaisePostBackEvent(IPostBackEventHandler sourceControl, String eventArgument) +15 System.Web.UI.Page.RaisePostBackEvent(NameValueCollection postData) +35 System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +1738
补充说明
- 数据库
V_NAME字段的值包含括号、冒号、分号等特殊符号,取值代码为:vnamtxt.Text = dr["V_NAME"].ToString() - 核心业务代码如下:
// 数据库查询方法 public static DataSet ORSQLSelectRecord(string strselect) { OracleConnection Ocon = new OracleConnection(); Ocon.ConnectionString = "User Id=abc;Password=abc;Data Source=ghk"; try { Ocon.Open(); OracleCommand ocmd = new OracleCommand(); ocmd.Connection = Ocon; ocmd.CommandType = CommandType.Text; ocmd.CommandText = strselect; OracleDataAdapter oda = new OracleDataAdapter(ocmd); DataSet ds = new DataSet(); oda.Fill(ds); return ds; } catch { return null; } finally { Ocon.Close(); Ocon.Dispose(); } } // 登录按钮点击事件 protected void btn_Login_Click(object sender, EventArgs e) { DataSet ds = ORSQLSelectRecord("select* from studDETAILS where USER_NAME = '" + txtUserName.Text + "' and USER_PWD = '" + txtPassword.Text + "' "); if (ds.Tables[0].Rows.Count > 0) { DataRow dr = ds.Tables[0].Rows[0]; vnamtxt.Text = dr["V_NAME"].ToString(); string ve_code = Convert.ToString(ds.Tables[0].Rows[0]["v_code"]); Session["vname"] = vnamtxt.Text; Session["vcode"] = ve_code; Response.Redirect("SumForm.aspx?vcode=" + vendor_code); } else { txtPassword.Text = ""; txtUserName.Text = ""; lbl_Msg.Visible = true; lbl_Msg.Text = "Invalid Login! Please try again"; } }
根因分析
1. 偶发空引用的核心原因
ORSQLSelectRecord的catch块直接返回null,没有做任何异常处理或日志记录:只要数据库连接超时、SQL执行报错(比如用户输入带单引号导致SQL语法错误)、网络偶发抖动,这个方法就会返回null。- 下游
btn_Login_Click直接调用ds.Tables[0],完全没有做空判断,此时ds为null就会直接抛出空引用异常,和堆栈报错位置完全匹配。 - 本地运行正常的原因:本地调试时数据库连接稳定,测试用的账号没有带会破坏SQL语法的特殊字符(比如单引号),几乎不会触发
catch块;IIS部署后,生产环境的数据库连接池可能有超时限制、用户输入的内容不可控,就会偶发触发上述异常。
2. 特殊符号的影响说明
V_NAME字段里的括号、冒号、分号属于普通字段值,赋值给文本框、存储到Session时不需要额外转义,本身不会导致运行时错误。- 真正受特殊符号影响的是拼接SQL的部分:如果用户输入的用户名/密码带单引号,直接拼接的SQL会出现语法错误,进而触发
ORSQLSelectRecord返回null,这也是偶发故障的重要诱因之一。 - 原代码里的
Response.Redirect("SumForm.aspx?vcode=" + vendor_code)存在变量名错误,实际定义的变量是ve_code,此处引用了未定义的vendor_code,也可能导致二次报错。
修复方案
1. 优化数据库查询方法的异常处理
不要直接返回null,要么返回空DataSet,要么记录异常后抛出,避免吞掉错误:
public static DataSet ORSQLSelectRecord(string strselect, params OracleParameter[] parameters) { // 用using自动释放连接资源,不需要手动Close/Dispose using (OracleConnection Ocon = new OracleConnection("User Id=abc;Password=abc;Data Source=ghk")) { try { Ocon.Open(); using (OracleCommand ocmd = new OracleCommand(strselect, Ocon)) { ocmd.CommandType = CommandType.Text; // 支持传参数,替代拼接SQL if (parameters != null) { ocmd.Parameters.AddRange(parameters); } OracleDataAdapter oda = new OracleDataAdapter(ocmd); DataSet ds = new DataSet(); oda.Fill(ds); return ds; } } catch (Exception ex) { // 可在此处补充错误日志记录逻辑 // 返回空DataSet,不要返回null return new DataSet(); } } }
2. 补充下游空判断,修复变量错误
在使用DataSet前先判断是否合法,避免空引用,同时修复原代码的变量名错误:
protected void btn_Login_Click(object sender, EventArgs e) { // 用参数化查询替代拼接SQL,从根源解决特殊字符问题,同时防SQL注入 string sql = "select * from studDETAILS where USER_NAME = :UserName and USER_PWD = :UserPwd"; DataSet ds = ORSQLSelectRecord(sql, new[] { new OracleParameter(":UserName", txtUserName.Text.Trim()), new OracleParameter(":UserPwd", txtPassword.Text.Trim()) }); // 新增合法性判断 if (ds != null && ds.Tables.Count > 0 && ds.Tables[0].Rows.Count > 0) { DataRow dr = ds.Tables[0].Rows[0]; vnamtxt.Text = dr["V_NAME"].ToString(); string ve_code = Convert.ToString(dr["v_code"]); Session["vname"] = vnamtxt.Text; Session["vcode"] = ve_code; // 修复原代码变量名错误,同时对URL参数做编码处理 Response.Redirect("SumForm.aspx?vcode=" + Server.UrlEncode(ve_code)); } else { txtPassword.Text = ""; txtUserName.Text = ""; lbl_Msg.Visible = true; lbl_Msg.Text = "Invalid Login! Please try again"; } }
3. 特殊符号的正确处理逻辑
- 对于用户输入的查询条件:必须用参数化查询,不要手动拼接SQL,单引号、括号等特殊符号会被Oracle参数自动处理,不会破坏SQL语法。
- 对于查询返回的字段值(比如
V_NAME):直接使用即可,不需要额外转义;如果要作为URL参数传递,用Server.UrlEncode处理特殊字符,避免URL截断或解析错误。
内容的提问来源于stack exchange,提问作者abc。
相关产品推荐
相关产品推荐

