·您现在的位置: 云翼网络 >> 文章中心 >> 网站建设 >> 网站建设开发 >> ASP.NET网站开发 >> Linux.NET实战手记—自己动手改泥鳅(下)

Linux.NET实战手记—自己动手改泥鳅(下)

作者:佚名      ASP.NET网站开发编辑:admin      更新时间:2022-07-23

linux.NET实战手记—自己动手改泥鳅(下)

在上回合中,我们不痛不痒的把小泥鳅的数据库从只能供在Windows下运行的access数据库改为支持跨平台的MySQL数据库,毫无营养的修改,本回合中,我们将把我们修改后得来的项目往Linux中部署、调试,让它适应Linux.NET的运行环境。

在本回合中,我们将讨论研究:

  1、由一个谎言引出另一个谎言

  2、遭遇大量大小写问题怎么办

  3、requestValidationMode?

  4、同一个房顶,却是不同的房间


1、由一个谎言引出另外一个谎言

当我们把小泥鳅部署上Linux之后,首页一般是没有问题的(首页能够打开,并且能够阅读里面的文章),但是当我们点击后台管理时,页面就开始变得奇怪起来,它没有像我们想象那样,出现一个填写用户名密码的界面,而是如下图所示的“问题”页面:

通过阅读堆栈跟踪,我们大概知道程序用一个“AdminPage.CheckLoginAndPermission”的地方进去之后就开始报错,为此,我们需要先确定这个叫做“CheckLoginAndPermission”的东西到底是Mono或其他三方类库里的东西还是我们代码里的。

判断的方法也挺简单,对着Visual Studio 按“Ctrl+Shirt+F”,没错,就是查找功能,只要我们能够在项目中找到相关的代码,那就证明改方法是我们项目中自己的东西。

通过搜索,结果还真让我们找到症结的所在,于是,我们就顺藤摸瓜的进入到该方法里面,该方法的代码如下:

/// <summary>    /// 检查登录和权限    /// </summary>    PRotected void CheckLoginAndPermission()    {        if (!PageUtils.IsLogin)        {            HttpContext.Current.Response.Redirect("login.aspx?returnurl=" + HttpContext.Current.Server.UrlEncode(RequestHelper.CurrentUrl));        }        UserInfo user = UserManager.GetUser(PageUtils.CurrentUserId);        if (user == null)       //删除已登陆用户时有效        {            PageUtils.RemoveUserCookie();            HttpContext.Current.Response.Redirect("login.aspx?returnurl=" + HttpContext.Current.Server.UrlEncode(RequestHelper.CurrentUrl));        }        if (StringHelper.Getmd5(user.UserId + HttpContext.Current.Server.UrlEncode(user.UserName) + user.PassWord) != PageUtils.CurrentKey)        {            PageUtils.RemoveUserCookie();            HttpContext.Current.Response.Redirect("login.aspx?returnurl=" + HttpContext.Current.Server.UrlEncode(RequestHelper.CurrentUrl));        }        if (PageUtils.CurrentUser.Status == 0)        {            ResponseError("您的用户名已停用", "您的用户名已停用,请与管理员联系!");        }        string[] plist = new string[] { "themelist.aspx", "themeedit.aspx", "linklist.aspx", "userlist.aspx", "setting.aspx" ,"categorylist.aspx","taglist.aspx","commentlist.aspx"};        if (PageUtils.CurrentUser.Type == (int)UserType.Author)        {            string pageName = System.IO.Path.GetFileName(HttpContext.Current.Request.Url.ToString()).ToLower();            foreach (string p in plist)            {                if (pageName == p)                {                    ResponseError("没有权限", "您没有权限使用此功能,请与管理员联系!");                }            }        }    }
CheckLoginAndPermission

咋眼一看,一个验证账户登录与权限的方法,如果不满足则自动的跳转到各自的页面,没有什么特别的,也没有什么问题。但是,程序的异常就是出现在这里,因此我们需要把他找出来。

各位读者第一时间想到的可能是马上按“F5”或者“附加到进程”,依赖Visual Studio 这个强大的IDE来定位哪一步出了问题。但是,别忘了,我们的程序在Windows下是没有问题的,并且当前的操作系统也不是Windows,因此Visual Studio的功能我们是无法使用的。或许,有些读者还知道有“Mono Develop”这个IDE,该IDE可以在Linux中使用,可惜,我们的Linux中并没有安装这个工具,甚至连Xwindows也没有安装,Linux的运行级别也只是“init-3”级别,要弄“Mono Develop”太麻烦了,我们需要一些有趣的手段来定位我们的问题。

先回想一下,既然成功发布,那就证明项目是成功的编译,而在运行时出现却报错,则表示,这个是一个运行时异常。运行时异常,其实我们也会经常遇到,譬如让程序读一个不存在的文件、数据库连接字串写错之类的,这些都属于运行时异常,只有程序运行到这一步出现错误的时候,程序才终止继续运行并提示错误。

根据这一原理,我们可以自己定义一些“谎言”(手动的添加一些运行时错误),让程序运行到此处终止并提示错误,通过比较程序提示的错误,我们就可以定位到项目中发生错误的哪一行代码了,通过一个谎言来引出另外一个谎言。

譬如我在检查是否登陆这里添加一个“谎言”。

/// <summary>    /// 检查登录和权限    /// </summary>    protected void CheckLoginAndPermission()    {        if (!PageUtils.IsLogin)        {            HttpContext.Current.Response.Redirect("login.aspx?returnurl=" + HttpContext.Current.Server.UrlEncode(RequestHelper.CurrentUrl));        }        UserInfo user = UserManager.GetUser(PageUtils.CurrentUserId);        //在这里添加谎言        var a = decimal.Parse("小蝶惊鸿");        if (user == null)       //删除已登陆用户时有效        {            PageUtils.RemoveUserCookie();            HttpContext.Current.Response.Redirect("login.aspx?returnurl=" + HttpContext.Current.Server.UrlEncode(RequestHelper.CurrentUrl));        }        if (StringHelper.GetMD5(user.UserId + HttpContext.Current.Server.UrlEncode(user.UserName) + user.Password) != PageUtils.CurrentKey)        {            PageUtils.RemoveUserCookie();            HttpContext.Current.Response.Redirect("login.aspx?returnurl=" + HttpContext.Current.Server.UrlEncode(RequestHelper.CurrentUrl));        }        if (PageUtils.CurrentUser.Status == 0)        {            ResponseError("您的用户名已停用", "您的用户名已停用,请与管理员联系!");        }        string[] plist = new string[] { "themelist.aspx", "themeedit.aspx", "linklist.aspx", "userlist.aspx", "setting.aspx", "categorylist.aspx", "taglist.aspx", "commentlist.aspx" };        if (PageUtils.CurrentUser.Type == (int)UserType.Author)        {            string pageName = System.IO.Path.GetFileName(HttpContext.Current.Request.Url.ToString()).ToLower();            foreach (string p in plist)            {                if (pageName == p)                {                    ResponseError("没有权限", "您没有权限使用此功能,请与管理员联系!");                }            }        }    }
谎言的CheckLoginAndPermission

编译发布后再刷新页面

我们得到了这个运行时异常,图明显的跟之前的不同,那就证明,刚才的异常在此“谎言”的下方。

我们不断的把我们的“谎言”(手动添加的运行时错误)往下挪,直到它把真正的“谎言”(原本的运行时错误)引出。

通过这种方法的迭代,我们大概定位到这里:

再结合它报给我们的错误“Object reference not set to an instance of an object”(未将对象实例化),我们可以推演出,这里有东西为null。在这里,只有user为需要实例化的类(PageUtils.CurrentKey是一个static的属性),我们可以猜或许是user为null。

为了验证,我们可以做如下动作:

通过验证,我们发现我们的推理是对的,就是user为null造成了此处的失败。

但是,为什么user为空呢?或者说,难道说小泥鳅中原程序中没有对user作出checknull的判断吗?我们先追述一下user的来源,user来自于本方法中:

通过传入一个CurrentUserId,来获得user类,而在GetUser方法中,代码如下:

        /// <summary>        /// 获取用户        /// </summary>        /// <param name="userId"></param>        /// <returns></returns>        public static UserInfo GetUser(int userId)        {            foreach (UserInfo user in _users)            {                if (user.UserId == userId)                {                    return user;                }            }            return null;        }
GetUser

CurrentId实际上还是一个userid,通过比较两个userid的值来获得user实例。还在想为什么没有得到null吗?这里是一个陷阱,我们根本就没有登陆,所以根本就没有CurrentUserId(或者说userid的值为null),因此,GetUser方法的输出东西应该也是为null。

难道小泥鳅没有对输出为null的情况作出处理吗?答案是否定的,小泥鳅中已经有判断,不然在Windows中就已经报错了,如果用户没有登陆(没有登陆就必定没有CurrentUserId了),页面就跳转到“login.aspx”页面(登陆页面)。

逻辑上当然是这样的,但是现实却并非这样,页面没有发生跳转,或者更确切的说,程序没有到达Redirect方法之后进行重定向并终止“CurrentUserId”方法中接下来的代码。看清楚幕后的&ldqu