调试代码
我们用登录作为实例,看看mvc如何调试。首先看一段前端代码:
<form name="Login" method="post" target="_parent"> <table width="100%" border="0" align="center" cellpadding="0" cellspacing="0"> <tbody> <tr> <td height="26" colspan="2" nowrap> <img src="@Url.Content("~/Content/images/Login_tit.gif")" width="370" height="42"> </td> </tr> <tr> <td width="31%" height="26" align="right" nowrap> <span class="STYLE3">用户名:</span> </td> <td width="69%" height="26" nowrap> <input name="UserName" type="text" id="UserName" maxlength="20" style=" 160px; border-style: solid; border- 1; padding-left: 4; padding-right: 4; padding-top: 1; padding-bottom: 1" onmouseover="this.style.background='#E1F4EE';" onmouseout="this.style.background='#FFFFFF'" onfocus="this.select(); "> </td> </tr> <tr> <td height="26" align="right" nowrap class="STYLE3"> 密 码: </td> <td nowrap height="26"> <input name="Password" id="Password" type="Password" maxlength="20" style=" 160px; border-style: solid; border- 1; padding-left: 4; padding-right: 4; padding-top: 1; padding-bottom: 1" onmouseover="this.style.background='#E1F4EE';" onmouseout="this.style.background='#FFFFFF'" onfocus="this.select(); "> </td> </tr> <tr> <td height="26" align="right" nowrap class="STYLE3"> 验证码: </td> <td nowrap height="26"> <input name="CheckCode" id="CheckCode" size="6" maxlength="4" style="border-style: solid; border- 1; padding-left: 4; padding-right: 4; padding-top: 1; padding-bottom: 1" onmouseover="this.style.background='#E1F4EE';" onmouseout="this.style.background='#FFFFFF'" onfocus="this.select(); "> <img src="@Url.Action("GetValidateCode")" id="codeImg" onclick="RefreshImage()" alt="点击重刷新" /> </td> </tr> <tr> <td height="26" colspan="2" align="center"> <input type="button" name="Submit" onclick="Check();" value=" 确 认 " style="font-size: 9pt; height: 19; 60; color: #000000; background-color: #E1F4EE; border: 1 solid #E1F4EE" onmouseover="this.style.backgroundColor='#ffffff'" onmouseout="this.style.backgroundColor='#E1F4EE'"> <input name="reset" type="reset" id="reset" value=" 清 除 " style="font-size: 9pt; height: 19; 60; color: #000000; background-color: #E1F4EE; border: 1 solid #E1F4EE" onmouseover="this.style.backgroundColor='#ffffff'" onmouseout="this.style.backgroundColor='#E1F4EE'"> </td> </tr> </tbody> </table> </form>
js代码如下:
<script src="@Url.Content("~/Scripts/jquery-1.7.2.min.js")" type="text/javascript"></script> <script type="text/javascript"> function CheckForm() { if (document.Login.UserName.value == "") { alert("请输入用户名!"); document.Login.UserName.focus(); return false; } if (document.Login.Password.value == "") { alert("请输入密码!"); document.Login.Password.focus(); return false; } if (document.Login.CheckCode.value == "") { alert("请输入您的验证码!"); document.Login.CheckCode.focus(); return (false); } return true; } function Check() { if(!CheckForm()){ return false; } $.post("@Url.Action("Loginin")", { UserName: $("#UserNam").val(),Password:$("#Password").val(), CheckCode:$("#CheckCode").val()},function(data){ if(data.flag){ window.location.href=data.msg; } else{ alert(data.msg); } }); } function RefreshImage() { var el =document.getElementById("codeImg"); el.src=el.src+'?';//这个特别重要 } </script>
做调试时,首先保证后台能够接收到请求。我们在后台代码中打上断点
public ActionResult Loginin(Admin admin, string CheckCode) { object sessionCodeObj= Session["ValidateCode"]; string sessionCode=(sessionCodeObj==null?string.Empty:sessionCodeObj.ToString()); if (!CheckCode.Equals(sessionCode)) { return Json(new { flag = false, msg = "验证码输入错误!" }, JsonRequestBehavior.AllowGet); } Admin old = EntityDMHelper.GetAdminByName(admin.UserName); if (old == null) { return Json(new { flag = false, msg = "不存在该用户!" }, JsonRequestBehavior.AllowGet); } if (!VerifyMD5(admin.Password, old.Password)) { return Json(new { flag = false, msg = "密码错误!" }, JsonRequestBehavior.AllowGet); } FormsAuthentication.SetAuthCookie(admin.UserName, false); return Json(new { flag = true, msg = Url.Action("GetList", "Article") }, JsonRequestBehavior.AllowGet); }
如下图:
开始调试
那么判断一定是前端脚本有问题。打开firebug调试窗口,在js脚本中打上断点,如下图:
图一、firebug断点
我们点击右上角三角或者按键F8,发现断点进入后台:
图二、后台断点
我们在参数admin右键点击,并选择添加监视
图三、添加监视
图四、后台断点无值
在弹出窗口中,点开树查看各个值,主要看Username、Password发现Username是空的,那么Username没有传递过来。
这是查看前台获取Username的代码:
$.post("@Url.Action("Loginin")", { UserName: $("#UserNam").val(),Password:$("#Password").val()
发现
$("#UserNam")
漏掉了一个字母“e”,把这个字母添加上以后,再次调试发现Username值传递过来了:
图六、后台断点值
但是按F5全部执行完后仍然是错误信息(用户名是存在的,有可能是其他错误):
图七、弹出错误
那么其他的错误应该在后台代码的另外的地方,没有法子只能一步步执行。
我们继续将其调试进入后台,然后F10继续执行,当进入到重要函数之后按F11进入函数GetAdminByName():
图八、进入函数
然后继续F10,到了取数据时弹出被捕获异常,查看异常信息:
图九、捕获异常
啊,从上面来看是连接字符串错误。那么我们找到字符串,对照后发现路径的确写错了:
private static string connectionString = @"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" + AppDomain.CurrentDomain.BaseDirectory + @"..Databases#@%$#Databases.asp;Jet OLEDB:Database PassWord=sa";
修改为如下:
private static string connectionString = @"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" + AppDomain.CurrentDomain.BaseDirectory + @"....Databases#@%$#Databases.asp;Jet OLEDB:Database PassWord=sa";
然后重心编译运行,发现成功登录:
图十、登录成功
调试的其他技巧:(转载)
跳到当前光标处(Ctrl+F10)
我经常看到人们为了到达目标代码位置,而在程序中早早设定了断点,然后反复地按F10/F11,一步步走到目标代码处。当程序员的确需要仔细观察每一步的状态变化时, F10/F11是合理的选择。然而多数情况下,人们只想快速到达他们真正关心的代码处,这时候F10/F11就不是最佳选择了。
这时,你应该利用“跳到当前光标处”这个功能。先把光标定位在要测的目标代码行上,再同时按Ctrl和F10,被测程序将直接跳到该行停下。你再也不用按许多次F10/F11了。即使目标代码位于独立的类或方法中,你仍然可以从当前正在检查的地方跳过去。
条件中断
另一种常见的情况是:开发人员设置断点,运行程序,利用不同的输入触发断点,然后在断点处手工检查是否满足某些特定的条件,从而决定是否继续调查。如果当前场景不是他们想要的,按F5继续运行程序,尝试别的输入,手动重复刚才的过程。
针对上述情况,Visual Studio提供了一个方便得多的功能——“条件中断”。只有当程序满足了开发人员预设的条件后,条件断点才会被触发,调试器中断。这将避免频繁地手工检查/恢复程序运行,大量减少调试过程中的手工和烦琐工作。
如何设置条件断点
设置条件断点非常容易。在特定的行上,按F9设置断点。
然后右击断点–编辑窗口左侧的红点,在上下文菜单上选择“Condition…”。
这时弹出一个对话框供你设置激活该断点所需的条件。比如:我们希望只有当局部变量paginatedDinners的尺寸小于10时,调试才中断。我们可以写出如下的表达式:
现在我再运行这个程序,实现搜索,只有返回值小于10时,程序运行才会被中断。对于大于10的值,该断点将被跳过。
记录到达断点次数
有时你希望,只有当第N次满足条件的运行到达断点时,才中断程序运行。例如:当第五次返回少于10份晚餐的查询结果时,中断程序运行。
可以通过右击断点,然后在弹出菜单上选择“Hit count…”菜单命令实现。
这时系统弹出一个对话框,它允许你指定:(1)当满足条件,而且进入断点的累计次数等于N时,断点命中一次。(2)当满足条件,而且进入断点的累计次数是N的倍数时,断点命中一次。(3)当满足条件,而且进入断点的累计次数大于N时,每次命中断点。
机器/线程/进程过滤
设置如下:右击断点;在弹出菜单上选择“Filter…”菜单命令;然后指定命中断点的特定条件:在指定的机器上、或指定的进程中、或指定的线程中。
跟踪点—进入断点时的自定义操作
许多人不知道“跟踪点(TrackPoints)”这个调试功能。“跟踪点“是种特殊的断点,当它被命中时,它会触发一系列自定义操作。如果你想观察程序的行为,而又不想中断调试的时候,这个功能尤其有用。
我将用一个简单的控制台程序来演示如何使用“跟踪点”。如下是斐波那契数列的一个递归实现:
以上程序中,我们使用Console.WriteLine() 输出针对特定输入值生成的最终斐波那契数列。如果希望在调试器里观察操作中每一次递归运算后的数列而又不实际中断程序运行,该怎么办呢?“跟踪点”可以轻松实现。
设置跟踪点
你可以在特定的行上,按F9加跟踪点。然后
右击断点,在上下文菜单中选择“When Hit…”:
在弹出对话框上,你可以设置命中该断点时,所触发的事件。
在上面例子中,我们设定一旦命中断点时就打印追踪信息。注意,我们已经把局部变量“x”的值,作为追踪信息的一部分输出。局部变量可以通过{变 量名}语法输出。你还可以利用系统内置的命令($CALLER, $CALLSTACK, $FUNCTION等等),在追踪信息中输出常用的调试值。
在上例中,我们同时选中了底端的“continue execution“选项,这说明我们不希望程序中断调试状态,而是继续运行。唯一的不同是:每次断点条件满足时,我们的自定义追踪信息都将被输出。
现在当我们运行程序时,会发现自定义追踪信息自动显示在Visual Studio的“输出“窗口里。这让我们很容易看到程序的递归调用过程:
你也可以选择往应用程序中添加一个自定义追踪信息的监听器。这时追踪点的输出信息将通过它输出,而不是VisualStudio的“输出“窗口。
好了,本节讲到这里。下一节将讲解mvc的Razor视图代码。