上回合中,我们不痛不痒的把小泥鳅的数据库从只能供在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 == )
{
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 == )
{
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”方法中接下来的代码。看清楚幕后的“元凶”之后,想要对付它也变得简单起来,我们只需手动的让它终止运行方法内接下来的代码就可以了。

在Redirect下方加上Return(方法内所有的Redirect都加上):

然后再次编译发布,我们就可以看到我们的登陆页面了。

2、如果遇到大量大小写问题怎么办?

我们进入后台管理页面之后,尝试添加一篇文章:

为了添加图片,我们需要点击“插入图片/文件”:

然后就……,上面说“'UserControls/upfilemanager.ascx”不存在。

典型的大小写问题,"UserControls"文件夹的大小写。解决办法很简单,把文件夹名按照大小写改好就ok。

正如我上回合中说的,小泥鳅的大小写还是比较严格的,基本上都是大小写敏感,但是,如果我们现在面对的不是小泥鳅,而是一个比较麻烦项目,里面充斥着大量的大小写问题,我们再对此进行地毯式的搜索并修正就显得可行性极低了(还不如推倒重写呢)。

面对这种情况,我们也是有“作弊码”可以行的,我们可以通过修改jws的脚本文件,让Mono对文件目录不区分大小写(注意,是文件目录,SQL语句还是区分的,因为解析SQL语句是数据库的事情,而不是Mono的事情)。

我们只需打开“jws”文件,并把Mono的IOMAP设置为all即可(jws中只需删掉“#”号)。

重启一下Jexus,再尝试添加:

ok,我们又搞定了一个问题。

3、requestValidationMode?

也不记得从什么时候开始,大概是.NET FrameWork 4.0 吧,我们使用富文本编辑器的时候,提交时会出现“Form表单有危险……”之类的提示,.NET也自动的帮我们验证从页面中Post回来的报文,有尖括号之类的敏感字串会自动的被.NET拒绝接收,解决的办法也很容易,网上是大把大把的,基本上就是把验证的模式从“4.0”(或更高)改为“2.0”就ok了。

但是,在我们这里,小泥鳅是基于.NET 2.0哦,所以应该就不会出现上述这种情况吧?!我们先试着添加一点东西:

然后再点击提交:

片刻的广告之后,我们只能说“嘿嘿~~”了,Mono把小泥鳅用ASP.NET 4.0 来运行了,既然它是使用.NET FrameWork 4.0 的模式来运行,那么我们也只需使用相应的解决办法即可。

我在“web.config”中的“httpruntime”节点中加上“requestValidationMode”(在这里,我直接就用VI加了,无需重新编译发布):

然后我们重新添加文章并保存,就可以在首页中找到了我们的文章了:

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

整好了小泥鳅在Linux.NET中的运行异常之后,我们还需要增加一个对Sqlite数据库的支持,虽然平时经常听到这一款的数据库,但是却从来都没有真正的接触过,直到开始增加此扩展的时候还是第一次(初体验?!),多亏了通用性高的SQL语句和ADO.NET,使我在仅仅知道它有五种数据类型并从Nuget哪里获得驱动的情况下就做好了对Sqlite的扩展(于是悲剧来了)。

在Windows中测试通过之后,我们迫不及待的往Linux中发布,然后就:

在Linux.NET中,如果遇到DLL明明就在bin目录中,但是程序却报找不到或者无法加载之类的,一般要么就是DLL真的找不到(文件大小写问题)、还有其他依赖的DLL没有成功加载或者是直接不兼容造成的。排除了大小写问题之后,我们找找Mono中有没有带有对Sqlite作驱动的dll。

可以发现,Mono中已经自带了Sqlite的驱动,并且与在MS.NET中是处于两个不同的命名空间。此外,这里还有一个图片需要让读者们看看的:

不仅命名空间不同,大小写也是有点点差别,所以,千言万语都不说了,改吧~~!


至此,小泥鳅的改造基本上完成了,需要代码的读者可以在GitHub中找到(地址在上集中有说),能力有限,如果写得有不对的欢迎各位读者留言,有建议或者意见的也欢迎留言,我们下回见~~!

PS:今年是“码”年,在这里小蝶惊鸿给各位读者拜年,祝各位读者新年快乐,恭喜发财。

Linux.NET实战手记—自己动手改泥鳅(下)的更多相关文章

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

    各位读者大家好,不知各位读者有否阅读在下的前一个系列<Linux.NET 学习手记>,在前一个系列中,我们从Linux中Mono的编译安装开始,到Jexus服务器的介绍,以及如何在Linu ...

  2. Linux.NET学习手记(7)

    前一篇中,我们简单的讲述了下如何在Linux.NET中部署第一个ASP.NET MVC 5.0的程序.而目前微软已经提出OWIN并致力于发展VNext,接下来系列中,我们将会向OWIN方向转战. 早在 ...

  3. Linux.NET学习手记(8)

    上一回合中,我们讲解了Linux.NET面对OWIN需要做出的准备,以及介绍了如何将两个支持OWIN协议的框架:SignalR以及NancyFX以OwinHost的方式部署到Linux.NET当中.这 ...

  4. 关于《Linux.NET学习手记(8)》的补充说明

    早前的一两天<Linux.NET学习手记(8)>发布了,这一篇主要是讲述OWIN框架与OwinHost之间如何根据OWIN协议进行通信构成一套完整的系统.文中我们还直接学习如何直接操作OW ...

  5. 嵌入式Linux启动优化手记2&nbsp;U…

    参考一下 原文地址:U-boot优化">嵌入式Linux启动优化手记2 U-boot优化作者:ZhaoJunling 既然不能使用新的U-boot,那就优化一点是一点,慢慢干吧. 1. ...

  6. LR添加Windows和Linux压力机实战

    添加Windows和Linux压力机实战 既然Controller是LoadRunner的“心脏”,那么压力产生也必然是它发起的,通过压力机来对被测系统产生压力.一般压力机分为Windows和Linu ...

  7. Linux系统实战项目——sudo日志审计

    Linux系统实战项目——sudo日志审计   由于企业内部权限管理启用了sudo权限管理,但是还是有一定的风险因素,毕竟运维.开发等各个人员技术水平.操作习惯都不相同,也会因一时失误造成误操作,从而 ...

  8. 【Linux】将Oracle安装目录从根目录下迁移到逻辑卷

    [Linux]将Oracle安装目录从根目录下迁移到逻辑卷 1.1  BLOG文档结构图 1.2  前言部分 1.2.1  导读和注意事项 各位技术爱好者,看完本文后,你可以掌握如下的技能,也可以学到 ...

  9. Linux游(1): diff, patch和quilt (下一个)

    Linux游(1): diff, patch和quilt (下一个) 2 quilt 我们自己的项目可以用cvs或svn管理所有代码.但有时我们要使用其它开发人员维护的项目.我们须要改动一些文件.但又 ...

随机推荐

  1. .NET跨平台之旅:将示例站点升级至 ASP.NET Core 1.1

    微软今天在 Connect(); // 2016 上发布了 .NET Core 1.1 ,ASP.NET Core 1.1 以及 Entity Framework Core 1.1.紧跟这次发布,我们 ...

  2. ASP.NET Core 1.1.0 Release Notes

    ASP.NET Core 1.1.0 Release Notes We are pleased to announce the release of ASP.NET Core 1.1.0! Antif ...

  3. Android注解使用之注解编译android-apt如何切换到annotationProcessor

    前言: 自从EventBus 3.x发布之后其通过注解预编译的方式解决了之前通过反射机制所引起的性能效率问题,其中注解预编译所采用的的就是android-apt的方式,不过最近Apt工具的作者宣布了不 ...

  4. 开发者接入 基本配置 服务器配置 out.aspx

    页面代码: 前段为默认的,什么都不用写,后台如下: 即可 来自为知笔记(Wiz)

  5. warensoft unity3d 更新说明

    warensoft unity3d 组件的Alpha版本已经发布了将近一年,很多网友发送了改进的Email,感谢大家的支持. Warensoft Unity3D组件将继续更新,将改进的功能如下: 1. ...

  6. iOS controller解耦探究实现——第一次写博客

    大学时曾经做过android的开发,目前的工作是iOS的开发.之前自己记录东西都是通过自己比较喜欢的笔记类的应用记录下了.直到前段时一个哥们拉着我注册了一个博客.现在终于想明白了,博客这个东西受众会稍 ...

  7. bzoj1079--记忆化搜索

    题目大意:有n个木块排成一行,从左到右依次编号为1~n.你有k种颜色的油漆,其中第i种颜色的油漆足够涂ci个木块.所有油漆刚好足够涂满所有木块,即c1+c2+...+ck=n.相邻两个木块涂相同色显得 ...

  8. BPM配置故事之案例9-根据表单数据调整审批线路2

    老李:好久不见啊,小明. 小明:-- 老李:不少部门有物资着急使用,现在的审批流程太慢了,申请时增加一个是否加急的选项吧.如果选加急,金额1000以下的直接到我这里,我审批完就通过,超过1000的直接 ...

  9. 虚拟机VMware12.05下安装Ubuntu16.04几个关键地方

    在踩了自己按照网上的教程安装Ubuntu之后,仍然踩了不少坑,鼓捣了一段时间,才达到自己想要的界面.   下面就来说说,大家可能也会遇到的情况:   1.安装ISO镜像时候,路径直接选择 你从Ubun ...

  10. 二叉树的创建和遍历(C版和java版)

    以这颗树为例:#表示空节点前序遍历(根->左->右)为:ABD##E##C#F## 中序遍历(左->根->右)为:#D#B#E#A#C#F# 后序遍历(左->右-> ...