DbEntry 开发实践:Wiki 系统(七)

流水帐记到现在,系统基本完成,不过还有几点需要解决。

  首先,我们来添加一个“记住我”的功能。当用户登录的时候,如果选择了“Remember Me”,那么就同时把用户名密码等信息保存到用户的cookie中,用户访问时,先检测cookie,如果cookie中有保存的信息,则验证此信息,如果验证通过,则建立相应的session,否则导向Login页面。

  先在SysUser中建立序列化和反序列化的两个函数:

代码
public static string SerializeToString(string name, string password)
{
    var s 
= string.Format("{0}\n{1}", name, password);
    var bs 
= Encoding.UTF8.GetBytes(s);
    
return Base32StringCoding.Decode(bs);
}

public static SysUser DeserializeFromString(string source)
{
    var bs 
= Base32StringCoding.Encode(source);
    var s 
= Encoding.UTF8.GetString(bs);
    var ss 
= s.Split('\n');
    
if (ss.Length == 2)
    {
        
return GetUserForLogin(ss[0], ss[1]);
    }
    
return null;
}

  然后修改Main.master中的OnInit以修改验证逻辑加入cookie部分:

代码
protected override void OnInit(EventArgs e)
{
    var u 
= this.GetLoginUser();
    
if (null == u)
    {
        var luc 
= Request.Cookies[Const.LoginCookie];
        
if (luc != null)
        {
            u 
= SysUser.DeserializeFromString(luc.Value);
        }
        
if (null == u)
        {
            var url 
= new UrlBuilder("Login.aspx").Add(Const.BackToUrl, Request.Url.ToString());
            Response.Redirect(url.ToString());
        }
        
this.SetLoginUser(u);
    }
    UserName.Text 
= u.Name;
    
base.OnInit(e);
}

  然后修改Login.aspx加入Remember Me按钮:

代码
protected void Page_Load(object sender, EventArgs e)
{
    ErrMsg.Visible 
= false;
    
if (!IsPostBack)
    {
        SysUser u 
= this.GetLoginUser();
        
if (u != null)
        {
            
this.SetLoginUser(null);
        }
        FormsAuthentication.SignOut();
        var luc 
= Request.Cookies[Const.LoginCookie];
        
if (luc != null)
        {
            luc.Expires 
= DateTime.Now.AddDays(-1);
            luc.Value 
= "";
            Response.Cookies.Set(luc);
        }
    }
}

protected void SignIn_Click(object sender, EventArgs e)
{
    ProcessLogin(UserName.Text, Password.Text);
}

private void ProcessLogin(string name, string password)
{
    SysUser u 
= SysUser.GetUserForLogin(name, password);
    
if (u != null)
    {
        
this.SetLoginUser(u);
        
if (RememberMe.Checked)
        {
            var cookie 
= new HttpCookie(Const.LoginCookie, SysUser.SerializeToString(u.Name, password)) { Expires = DateTime.Now.AddDays(30) };
            Response.Cookies.Add(cookie);
        }
        var url 
= Request[Const.BackToUrl];
        
if(!string.IsNullOrEmpty(url))
        {
            Response.Redirect(url);
        }
        
else
        {
            Response.Redirect(
"Default.aspx");
        }
    }
    
else
    {
        ErrMsg.Text 
= "User name or password error!";
        ErrMsg.Visible 
= true;
    }
}

  运行程序,验证如果选择了Remember Me,则再次直接访问页面,不再需要登录,而且,Welcome信息显示名称正确;如果点击了Logout,再次运行程序,则仍然需要登录。现在,这个功能开发完毕。

  第二个要完成的功能,是Wiki和用户关联。既然我们现在有登录用户了,自然可以记录下究竟是谁修改了哪个Wiki页。

  修改Article和ArticleHistory,增加User:

[BelongsTo]
public abstract SysUser User { getset; }

  然后修改它们两个的Init函数和SaveArticle函数,增加对于user的支持,编译程序,将以前只提供两个参数的Init和SaveArticle函数调用,都改成也提供user的形式。现在我们实际上已经修改了数据库表结构,所以删除App_Data目录下的wiki.db文件,再次运行程序,创建用户、编辑文章,还和以前一样的操作,不过,我们已经记录了编辑者,只是还没有显示出来而已。

  在History.aspx里增加user列:

<asp:TemplateField HeaderText="User">
    
<ItemTemplate><%# ((ArticleHistory)(Container.DataItem)).User.Name %></ItemTemplate>
</asp:TemplateField>

  在Show.aspx里增加一个tip变量,一个msg变量,这个msg变量,标记为InMaster,以便我们访问母板页里的msg控件:

[InMaster] public Lephone.Web.Common.NoticeLabel msg;
private string tip;

  然后在Page_Load里对tip进行赋值,再override OnPreRender函数,添加这个tip(之所以需要这样,而不是直接在Page_Load里操作,也是因为asp.net奇怪的事件加载顺序):

protected override void OnPreRender(EventArgs e)
{
    
if(!string.IsNullOrEmpty(tip))
    {
        msg.AddTip(tip);
    }
}

  现在,运行程序,Show页面底边将会显示编辑者的名字和编辑的时间,History页面也会显示编辑者的名字了。

  最后,我们再考虑一下性能。对于Show页面而言,问题不大,不过,History页面,并不显示Content,却需要加载它,是有些浪费的,所以,我们标记ArticleHistory的Content为延迟加载:

[LazyLoad]
public abstract string Content { getset; }

  另外,History里显示用户名,Show页面显示用户名,都需要访问数据库,History更是多次访问数据库读取sys_users表,只为得到用户名,对于这一点,我们给它加个缓存,在web.config里添加:

<add key="CacheEnabled" value="true" />

  在SysUser上标记可以被缓存:

[Cacheable]
public abstract class SysUser : LinqObjectModel<SysUser>

  为了运行时可以跟踪生成的SQL语句,也为了验证一下我们缓存的效果,修改web.config中的log设置,将它设置为将生成的sql写入数据库:

<add key="SqlLogRecorder" value="Lephone.Data.Logging.DatabaseLogRecorder, Lephone.Data" />

  分别以打开和关闭缓存的方式访问相同的网页,之后检查数据库lephone_logs表里的SQL语句,验证有缓存时生成SQL数量较少。另外,ArticleHistory相关的Select语句在History页面不会有Content在内。

  好的,这个VisualWiki系列文章到这里算是全部结束了,虽然这个Wiki系统,还有一些功能有待完善,比如历史比对功能等,不过,它已经是一个可以运行,并且用起来也颇顺手的Wiki了,作为我前一个公司的内部Wiki系统,它一直运行,而且表现不错。最终代码:VisualWiki7.7z

  全文完。
posted @ 2009-12-07 23:46  梁利锋  阅读(1725)  评论(0编辑  收藏  举报