[C#][翻译-ASP.NET MVC]Contact Manager开发之旅迭代3 - 验证表单

mikel阅读(1048)

[翻译-ASP.NET MVC]Contact Manager开发之旅迭代3 – 验证表单

本翻译系列为asp.net mvc官方实 例教程。在这个系列中,Stephen Walther将演示如何通过ASP.NET MVC framework结合单元测试、TDD、Ajax、软件设计原则及设计模式创建一个完整的Contact Manager应用。本系列共七个章节,也是七次迭代过程。本人将陆续对其进行翻译并发布出来,希望能对学习ASP.NET MVC 的各位有所帮助。由于本人也是个MVC菜鸟,且E文水平亦是平平,文中如有疏漏敬请见谅。
注:为保证可读性,文中Controller、View、Model、Route、Action等ASP.NET MVC核心单词均未翻译。

 

ContactManager开发之旅-索引页

ContactManager开发之旅 迭代1 – 创建应用程序

ContactManager开发之旅 迭代2 – 修改样式,美化应用

迭代3 – 验证表单

这是Contact Manager的第三次迭代,在这次迭代中我们将为Contact Manager添加基本的表单验证。如果用户填写的表单不完整,我们将阻止其表单的提交。另外我们还要验证电话号码和电子邮件地址的合法性。(图1)

 clip_image001

图1

本 次迭代中,我们将验证逻辑直接写在controller的action中,不过这并不是ASP.NET MVC应用所推荐的方式。更好的办法是将这些验证逻辑布置到另外的service层中。下一次迭代的时候我们将重构Contact Manager应用,使其更易维护。

为了让本文看起来直观些,我们将在本次迭代中手写所有的验证代码。当然我们也可以利用某些现成的 验证框架来实现自动生成这些验证代码。比如你可以使用Microsoft Enterprise Library Validation Application Block (VAB)来实现ASP.NET MVC的验证逻辑。欲知更多VAB的信息,请看下面的链接:

http://msdn.microsoft.com/en-us/library/dd203099.aspx

为Create View添加验证规则

现在就让我们开始为Create view添加验证规则吧。在前两次迭代中,我们已经通过VS生成了Create view,拖VS的福我们的Create view已经包含了页面中显示验证消息所需的所有逻辑。Create view如下:

 

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<ContactManager.Models.Contact>" %>
<asp:Content ID="Content1" ContentPlaceHolderID="MainContent" runat="server">
<h2>Create</h2>
<%= Html.ValidationSummary("Create was unsuccessful. Please correct the errors and try again.") %>
    <% using (Html.BeginForm()) {%>
        <fieldset>
<legend>Fields</legend>
<p>
<label for="FirstName">FirstName:</label>
<%= Html.TextBox("FirstName") %>
                <%= Html.ValidationMessage("FirstName", "*") %>
            </p>
<p>
<label for="LastName">LastName:</label>
<%= Html.TextBox("LastName") %>
                <%= Html.ValidationMessage("LastName", "*") %>
            </p>
<p>
<label for="Phone">Phone:</label>
<%= Html.TextBox("Phone") %>
                <%= Html.ValidationMessage("Phone", "*") %>
            </p>
<p>
<label for="Email">Email:</label>
<%= Html.TextBox("Email") %>
                <%= Html.ValidationMessage("Email", "*") %>
            </p>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
<% } %>
    <div>
<%=Html.ActionLink("Back to List", "Index") %>
    </div>
</asp:Content>

注意紧靠HTML表单顶部的Html.ValidationSummary()这个helper方法。如果存在验证产生的错误信息,这个方法将使用无序列表呈现这些消息。

另外,每个表单对象后面调用的Html.ValidationMessage()方法会显示对应这个表单对象所产生的验证错误消息。比如上面所示代码中的情况下,如果存在验证错误,那么这里将会显示一个*号。

最后,如果存在验证错误,Html.TextBox()方法会自动为相关表单对象添加一个名为input-validation-error 的样式

当你新建一个ASP.NET MVC应用程序的时候,Content文件夹中默认生成的Site.css中便包含了这些验证相关的样式定义:

/* messages
-------------*/
div.information, div.error, div.success, ul.validation-summary-errors
{
margin:1em 0;
padding:1em;
}
div.information
{
color:#C60;
background-color:#FF9;
border:1px solid #F90;
}
div.error, ul.validation-summary-errors
{
color: #F00;
background-color:#C99;
border:1px solid #900;
}
div.success
{
color: #060;
background-color:#9C9;
border:1px solid #060;
}
.input-validation-error
{
border: 1px solid #ff0000;
background-color: #ffeeee;
}

 

field-validation-error是用来定义Html.ValidationMessage()呈现时的样式,input- validation-error用来定义textbox(input)的样式,而Vaidation-summary-errors则用来定义 Html.ValidationSummary()方法呈现的无序列表的样式。

你可以通过修改这些默认的样式规则从而美化和自定义这些验证错误消息的表现。

为Create Action添加验证规则

到目前为止,Create view不会显示任何的验证错误信息。因为我们尚未编写其生成消息的逻辑规则。这里你需要向ModelState添加错误消息。

指定的表单对象的值与对应属性发生验证错误时,UpdateModel()方法将自动添加错误消息到ModelState中。例如,如果你打算将 “apple”作为BirthDate属性的值但是该属性只接受DateTime类型,则UpdateModel()方法会添加一个错误至 ModelState中。

修改后的Create()方法代码如下,我们其添加了在新联系人插入数据库前验证联系人属性的相关代码部分:

//
// POST: /Home/Create
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create([Bind(Exclude = "Id")] Contact contactToCreate)
{
//Validation logic
if (contactToCreate.FirstName.Trim().Length == 0)
ModelState.AddModelError("FirstName", "First name is required");
if (contactToCreate.LastName.Trim().Length == 0)
ModelState.AddModelError("LastName", "Last name is required");
if (contactToCreate.Phone.Trim().Length == 0 && !Regex.IsMatch(contactToCreate.Phone, @"((\(\d{3}\) ?)|(\d{3}-))?\d{3}-\d{4}"))
ModelState.AddModelError("Phone", "Invalid phone number");
if (contactToCreate.Email.Trim().Length == 0 && !Regex.IsMatch(contactToCreate.Email, @"^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$"))
ModelState.AddModelError("Email", "Invalid email address");
if (!ModelState.IsValid)
{
return View();
}
else
{
try
{
_entities.AddToContactSet(contactToCreate);
_entities.SaveChanges();
return RedirectToAction("Index");
}
catch
{
return View();
}
}
}
  • FirstName属性的长度必须大于0(并且不计算空格)
  • LastName属性长度必须大于0(并且不计算空格)。
  • 如果Phone属性有值(且长度大于0),则Phone属性必须匹配正则表达式。
  • 如果Email属性有值(且长度大于0),则Email属性必须匹配正则表达式。

如果违反了其中某条验证规则,则一条错误消息将通过AddModelError()方法添加至ModelState中,你只需指定需验证的属性以及 违反相应验证规则时显示的错误提示信息。这条信息将会显示在view中调用Html.ValidationSummary()和 Html.ValidationMessage()方法的地方。

验证规则执行后就可以使用ModelState的IsValid属性了。该属性根据属性是否遵循ModelState中的规则返回一个布尔值。如果未通过验证,Create表单中将显示错误信息。

我是从http://regexlib.com的“正则表达式仓库”中找到验证电话号码和电子邮件地址的正则表达式的,希望它能帮到你。

为Edit Action添加验证规则

Edit() action用来更新一个联系人信息。它需要执行与Create() action中基本相似的验证规则。我们在这里重构Contact controller使得Create()及Edit() action可以复用验证规则。代码如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using System.Web;
using System.Web.Mvc;
using System.Web.Mvc.Ajax;
using ContactManager.Models;
namespace ContactManager.Controllers
{
public class ContactController : Controller
{
private ContactManagerEntities _entities = new ContactManagerEntities();
protected void ValidateContact(Contact contactToValidate)
{
if (contactToValidate.FirstName.Trim().Length == 0)
ModelState.AddModelError("FirstName", "First name is required.");
if (contactToValidate.LastName.Trim().Length == 0)
ModelState.AddModelError("LastName", "Last name is required.");
if (contactToValidate.Phone.Length > 0 && !Regex.IsMatch(contactToValidate.Phone, @"((\(\d{3}\) ?)|(\d{3}-))?\d{3}-\d{4}"))
ModelState.AddModelError("Phone", "Invalid phone number.");
if (contactToValidate.Email.Length > 0 && !Regex.IsMatch(contactToValidate.Email, @"^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$"))
ModelState.AddModelError("Email", "Invalid email address.");
}
//
// GET: /Home/
public ActionResult Index()
{
return View(_entities.ContactSet.ToList());
}
//
// GET: /Home/Details/5
public ActionResult Details(int id)
{
return View();
}
//
// GET: /Home/Create
public ActionResult Create()
{
return View();
}
//
// POST: /Home/Create
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create([Bind(Exclude = "Id")] Contact contactToCreate)
{
//Validation logic
ValidateContact(contactToCreate);
if (!ModelState.IsValid)
{
return View();
}
else
{
try
{
_entities.AddToContactSet(contactToCreate);
_entities.SaveChanges();
return RedirectToAction("Index");
}
catch
{
return View();
}
}
}
//
// GET: /Home/Edit/5
public ActionResult Edit(int id)
{
var contractToEdit = _entities.ContactSet.Where(c => c.Id == id).FirstOrDefault();
return View(contractToEdit);
}
//
// POST: /Home/Edit/5
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(Contact contactToEdit)
{
ValidateContact(contactToEdit);
if (!ModelState.IsValid)
return View();
try
{
var originalContact = _entities.ContactSet.Where(c => c.Id == contactToEdit.Id).FirstOrDefault();
_entities.ApplyPropertyChanges(originalContact.EntityKey.EntitySetName, contactToEdit);
_entities.SaveChanges();
return RedirectToAction("Index");
}
catch
{
return View();
}
}
//
// GET: /Home/Delete/5
public ActionResult Delete(int id)
{
var contactToDelete = _entities.ContactSet.Where(c => c.Id == id).FirstOrDefault();
return View(contactToDelete);
}
//
// POST: /Home/Delete/5
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Delete(Contact contactToDelete)
{
try
{
var originalContact = _entities.ContactSet.Where(c => c.Id == contactToDelete.Id).FirstOrDefault();
_entities.DeleteObject(originalContact);
_entities.SaveChanges();
return RedirectToAction("Index");
}
catch
{
return View();
}
}
}
}

总结

在本次迭代中,我们为Contact Manager应用添加了基本的表单验证。我们的验证逻辑会阻止用户在新增联系人、编辑联系人的情景下将未填写FirstName或LastName属性的表单进行提交。并且用户需要填写有效的电话号码和电子邮箱地址。

本文中我们使用了最简单的方式为Contact Manager应用添加验证逻辑,然而将验证规则与controller耦合将会对应用程序的维护产生深远的负面影响。我们的应用将越来越难维护和修改。

我们会在下一次迭代中重构我们的验证逻辑和数据库存储逻辑并将其与controller解耦。得益于适当设计模式的帮助,应用中耦合将更加松散且程序更易维护。

[C#]P2P网络通讯程序(c#)

mikel阅读(1011)

转载:http://www.cnblogs.com/dudu/archive/2009/03/26/1422677.html

       在网上看了很多程序(QQ、Azureus、Ants、PPStream)都实现了p2p,以前觉得技术很高深。通过这段时间的学习才发现,单纯的实现 p2p在局域网通讯很容易,但是要实现外网穿透(NAT)感觉很困难。最近看了Azureus和emule源码,分别是JAVA和C++版,本人对这两门 语言都不熟悉,看起来很吃力。最后只好根据VC++实现的P2PDemo程序进行了改版,根据设计思路用C#写了一个Demo出来。通过测试,多个客户端 在局域网能脱离服务端实现端到端工作。外网的情况要通过路由器,看了Azureus要实现uPnp进行端口映射,在CodeProject上下载了一个 uPnp源码看,测试结果没有启用uPnp路由器。结果现在郁闷了,不知道下一步怎么测试,是不是用upnp实现了端口自动映射成功就能实现象QQ那样通 讯。

下面是程序说明:

1、公共类

公共类主要定义一些包结构

a、Packet.cs 


[Serializable()]
    
public abstract class Packet
    {
        
/// <summary>
        
/// 命令类型
        
/// </summary>
        
/// <returns></returns>
        public virtual int GetCommandType()
        {
            
return 1;
        }
        
/// <summary>
        
/// 用户名
        
/// </summary>
        public string UserName
        {
            
get;
            
set;
        }
        
public Packet()
        { }
        
public Packet(string username)
        {
            
this.UserName = username;
        }
    }

b、MassTextPacket.cs  –分片传输类


 [Serializable()]
    
public class MassTextPacket:TextPacket
    {
        
private int seqID;
        
/// <summary>
        
/// 包序列
        
/// </summary>
        public int SeqID
        {
            
get { return seqID; }
            
set { seqID = value; }
        }
        
private int seqCount;
        
/// <summary>
        
/// 包数量
        
/// </summary>
        public int SeqCount
        {
            
get { return seqCount; }
            
set { seqCount = value; }
        }
        
private int _CLSD;
        
public int CLSD
        {
            
get { return _CLSD; }
            
set { _CLSD = value; }
        }
    }

 

2、客户端

a、消息传送时进行p2p通讯


        private bool SendMessageTo(string toUserName, Packet packet)
        {
            PeerEntity toUser 
= userList.Single(c => c.UserName == toUserName);
            
if (toUser == null)
            {
                
return false;
            }
            ReceivedACK 
= false;
            
for (int i=0; i<MAXRETRY; i++)            
            {      
                
// 如果对方P2P地址不为0,就试图以它为目的地址发送数据,
                
// 如果发送失败,则认为此P2P地址无效
                if (toUser.P2PAddress != null && toUser.P2PAddress.Port != 0)
                {
                    
if (packet.GetType() == typeof(TextPacket))
                    {
                        TextPacket msgPacket 
= new TextPacket(toUserName, (packet as TextPacket).Message);
                        
byte[] buffer = UtilityHelper.Serialize(msgPacket);
                        
if (buffer.Length > MAXBUFFERSIZE)
                        {
                         
                            MassTextPacket mtp 
= new MassTextPacket();
                            mtp.SeqID 
= 0;
                            mtp.SeqCount 
= (int)System.Math.Ceiling(buffer.Length / (decimal)MAXBUFFERSIZE);
                            mtp.CLSD 
= mtp.GetHashCode();
                            
                            
long pos = 0;
                            
long count = buffer.Length < MAXBUFFERSIZE ? buffer.Length : MAXBUFFERSIZE;
                            
while (pos < buffer.Length && pos > 0)
                            {
                                
byte[] bytes = new byte[count];                          ;
                                
for (int k = 0; k < count; k++)
                                    bytes[k] 
= buffer[pos + k];
                                
//数据组包
                                mtp.SeqID = mtp.SeqID + 1;
                                mtp.Message 
= Convert.ToBase64String(bytes);
                                
//发送数据
                                byte[] buf = UtilityHelper.Serialize(mtp);
                                client.Send(buf, buf.Length, toUser.P2PAddress);
                                Thread.Sleep(
100);
                            }
                        }
                        
else
                            client.Send(buffer, buffer.Length, toUser.P2PAddress);
                    }
                    
else if (packet.GetType() == typeof(FileStreamPacket))
                    {
                        FileStreamPacket fsp 
= packet as FileStreamPacket;
                        System.IO.FileStream fs 
= new System.IO.FileStream(fsp.FileName, System.IO.FileMode.Open, System.IO.FileAccess.Read, FileShare.Read);
                       handle1.Reset();
                        fsp.SeqID 
= 0;
                        fsp.SeqCount 
= (int)System.Math.Ceiling(fs.Length / (decimal)MAXBUFFERSIZE);
                        fsp.CLSD 
= fsp.GetHashCode();
                        
long pos = 0;
                        
long count = fs.Length < MAXBUFFERSIZE ? fs.Length : MAXBUFFERSIZE;
                        
while (pos < fs.Length && count > 0)
                        {
                            
byte[] buffer = new byte[count];
                            fs.Seek(pos, SeekOrigin.Begin);
                            fs.Read(buffer, 
0, (int)count);
                            pos 
+= count;
                            count 
= pos + MAXBUFFERSIZE < fs.Length ? MAXBUFFERSIZE : fs.Length  pos;
                            
//数据组包
                            fsp.SeqID = fsp.SeqID + 1;
                            fsp.Message 
= Convert.ToBase64String(buffer);
                            
//发送数据
                            byte[] buf = UtilityHelper.Serialize(fsp);
                            client.Send(buf, buf.Length, toUser.P2PAddress);
                            Thread.Sleep(
300);
                        }
                        handle1.Set();
                    }
                    
// 等待接收线程将标记修改
                    for (int j = 0; j < 10; j++)
                    {
                        
if (this.ReceivedACK)
                        {
                            
this.ReceivedACK = false;
                            
return true;
                        }
                        
else
                        {
                            Thread.Sleep(
300);
                        }
                    }
                }
                
// 构建P2P打洞封包
                
// 然后通过服务器转发,请求对方向自己打洞
                P2PConnectionPacket transMsg = new P2PConnectionPacket(UserName, toUserName);
                
byte[] msgBuffer = UtilityHelper.Serialize(transMsg);
                client.Send(msgBuffer, msgBuffer.Length, hostPoint);
                
// 等待对方的P2PCONNECTACK消息
                for(int j = 0; j < 10++j)
                {
                    toUser 
= userList.Single(c => c.UserName == toUserName);
                    
if ( toUser.P2PAddress != null && toUser.P2PAddress.Port != 0)
                        
break;
                    Thread.Sleep(
300);
                }
                
            }
            
return false;
        }

b、消息接受线程


/// <summary>
        
/// 接受线程处理
        
/// </summary>
        private void RecvThreadProc()
        {
            
byte[] buffer;
            
while (true)
            {
                
                
                buffer 
= client.Receive(ref remotePoint);
                Packet packet 
= UtilityHelper.Deserialize(buffer) as Packet;      
                Type msgType 
= packet.GetType();
                
if (msgType == typeof(UserListAckPacket))
                {
                    
// 转换消息
                    UserListAckPacket usersMsg = (UserListAckPacket)packet;
                    
// 更新用户列表
                    userList.Clear();
                    
foreach (PeerEntity user in usersMsg.Users)
                    {
                        userList.Add(user);
                    }
                    bUserListComplete 
= true;
                }                
                
else if (msgType == typeof(UserLoginAckPacket))
                {
                    ProcUserLogAckMsg(packet);
                }         
                
else if (msgType == typeof(TextPacket))
                {
                    
// 转换消息
                    TextPacket txtPacket = (TextPacket)packet;
                    printf(
"Receive a message: {0}", txtPacket.Message);
                    
// 发送应答消息
                    P2PAckPacket ackMsg = new P2PAckPacket();
                    buffer 
= UtilityHelper.Serialize(ackMsg);
                    client.Send(buffer, buffer.Length, remotePoint);
                }
                
else if (msgType == typeof(MassTextPacket))
                {
                    
lock (this)
                    {
                        MassTextPacket fPacket 
= (MassTextPacket)packet;
                        
if (packets.ContainsKey(fPacket.CLSD))
                            packets[fPacket.CLSD].Add(fPacket);
                        
else
                            packets.Add(fPacket.CLSD, 
new List<MassTextPacket>() { fPacket });
                        printf(
"PacketID:{0}–SeqNo:{1}–progress:{2}%", fPacket.CLSD, fPacket.SeqID, (int)(System.Math.Round(packets[fPacket.CLSD].Count / (decimal)(fPacket as MassTextPacket).SeqCount, 2* 100));
                        
//组包
                        if ((fPacket as MassTextPacket).SeqCount == packets[fPacket.CLSD].Count)
                        {
                            List
<MassTextPacket> temp = packets[fPacket.CLSD].OrderBy(c => c.SeqID).ToList();
                            List
<byte> values = new List<byte>();
                            
foreach (MassTextPacket mt in temp)
                            {
                                
byte[] buf = Convert.FromBase64String(mt.Message);        
                                values.AddRange(buf);
                            }
                            MassTextPacket value 
= UtilityHelper.Deserialize(values.ToArray()) as MassTextPacket;
                            printf(
"Receive a message: {0}", value.Message);
                           
                            
// 发送应答消息
                            P2PAckPacket ackMsg = new P2PAckPacket();
                            buffer 
= UtilityHelper.Serialize(ackMsg);
                            client.Send(buffer, buffer.Length, remotePoint);
                        }
                    }
                }
                
else if (msgType == typeof(FileStreamPacket))
                {                 
                    
lock (this)                    
                    {
                        FileStreamPacket fPacket 
= (FileStreamPacket)packet;
                        
if (packets.ContainsKey(fPacket.CLSD))
                            packets[fPacket.CLSD].Add(fPacket);
                        
else
                            packets.Add(fPacket.CLSD, 
new List<MassTextPacket>() { fPacket });
                        printf(
"PacketID:{0}–SeqNo:{1}–progress:{2}%", fPacket.CLSD, fPacket.SeqID, (int)(System.Math.Round(packets[fPacket.CLSD].Count / (decimal)(fPacket as FileStreamPacket).SeqCount, 2* 100));
                        
//组包
                        if ((fPacket as FileStreamPacket).SeqCount == packets[fPacket.CLSD].Count)
                        {
                            List
<MassTextPacket> temp = packets[fPacket.CLSD].OrderBy(c => c.SeqID).ToList();
                            System.IO.FileStream fs 
= new System.IO.FileStream((fPacket as FileStreamPacket).FileName + ".tmp", System.IO.FileMode.Create, System.IO.FileAccess.ReadWrite);
                            
foreach (FileStreamPacket mt in temp)
                            {
                                
byte[] buf = Convert.FromBase64String(mt.Message);
                                fs.Write(buf, 
0, buf.Length);
                            }
                            fs.Flush();
                            fs.Close();
                           printf(
"Receive a file: {0}", (fPacket as FileStreamPacket).FileName);
                           
//清除数据包
                           packets[fPacket.CLSD].Clear();
                           
// 发送应答消息
                           P2PAckPacket ackMsg = new P2PAckPacket();
                           buffer 
= UtilityHelper.Serialize(ackMsg);
                           client.Send(buffer, buffer.Length, remotePoint);
                        }
                    }              
                   
                }
                
else if (msgType == typeof(P2PAckPacket))
                {
                    
this.ReceivedACK = true;
                }
                
else if (msgType == typeof(P2PPurchHolePacket))
                {
                    ProcP2PPurchHoleMsg(packet, remotePoint);
                }
                
else if (msgType == typeof(P2PPurchHoleAckPacket))
                {
                    PeerEntity touser 
= userList.SingleOrDefault(c => c.UserName == (packet as P2PPurchHoleAckPacket).ToUserName);
                    
//更改本地的P2P连接时使用的IP地址
                    touser.P2PAddress = touser.RemoteEndPoint;
                }
                Thread.Sleep(
100);
            }
        }

c.建立p2p会话


        private void ProcP2PPurchHoleMsg(Packet packet,IPEndPoint remoteEP)
        {
            
//打洞请求消息           
            P2PPurchHolePacket purchReqMsg = (P2PPurchHolePacket)packet;
            PeerEntity toUser 
= userList.Single(c => c.UserName == purchReqMsg.ToUserName);
            PeerEntity user 
= userList.Single(c => c.UserName == purchReqMsg.UserName);
            toUser.P2PAddress 
= toUser.RemoteEndPoint;
            printf(
"Set P2P Address for {0}->[{1}]", user.UserName, toUser.P2PAddress.ToString());   
         
            
//uPnp实现端口映射
            if(NAT.AddPortMapping(toUser.P2PAddress.Port, ProtocolType.Udp, "AddPortMapping"))
                printf(
"Port mapping successed!");
            
// 发送打洞消息到远程主机
            P2PPurchHoleAckPacket trashMsg = new P2PPurchHoleAckPacket(purchReqMsg.UserName, purchReqMsg.ToUserName);          
            
byte[] buffer = UtilityHelper.Serialize(trashMsg);
            client.Send(buffer, buffer.Length, user.RemoteEndPoint);
        }

3、服务端

a、消息处理线程 


  private void RecvThreadProc()
        {
            IPEndPoint remotePoint 
= null;
            
byte[] msgBuffer = null;
            
while (true)
            {             
                msgBuffer 
= server.Receive(ref remotePoint);
                
try
                {
                    
object msgObj = UtilityHelper.Deserialize(msgBuffer);
                    
switch ((msgObj as Packet).GetCommandType())
                    {
                        
case Command.MSG_USERLOGIN:         //用户登录                            
                            ProcUserLoginMsg(msgObj as UserLoginPacket, remotePoint);
                            
break;
                        
case Command.MSG_USERLOGOUT:        //退出登录
                            ProcUserLogoutMsg(msgObj as UserLogoutPacket, remotePoint);
                            
break;
                        
case Command.MSG_GETUSERLIST:       //所有用户列表                            
                            ProcGetUserListMsg(msgObj as UserListPacket, remotePoint);
                            
break;
                        
case Command.MSG_P2PCONNECT:        //点对点连接信息                           
                            ProcP2PConnectMsg(msgObj as P2PConnectionPacket, remotePoint);
                            
break;
                        
case Command.MSG_USERACTIVEQUERY:   // 用户对服务器轮询的应答                            
                            ProcUserActiveQueryMsg(msgObj as UserActiveQueryPacket, remotePoint);
                            
break;
                    }
                    Thread.Sleep(
100);
                }
                
catch { }
            }
        }

b、服务端请求客户端建立p2p连接


        private void ProcP2PConnectMsg(Packet packet,IPEndPoint remoteEP)
        {
            
// 转换接受的消息
            P2PConnectionPacket transMsg = (P2PConnectionPacket)packet;
            printf(
"{0}({1}) wants to p2p {2}", remoteEP.Address.ToString(), transMsg.UserName, transMsg.ToUserName);
            
// 获取目标用户
            PeerEntity toUser = userList.SingleOrDefault(c => c.UserName == transMsg.ToUserName);
            
            
// 转发Purch Hole请求消息
            P2PPurchHolePacket transMsg2 = new P2PPurchHolePacket(transMsg.UserName, toUser.UserName);
            
//转发消息
            byte[] buffer = UtilityHelper.Serialize(transMsg2);
            server.Send(buffer, buffer.Length, toUser.RemoteEndPoint);
           
        }

    

4、测试

a、服务端

b、客户端

 

困惑:

1、能不能实现外网通讯,要实现像QQ那样通讯要做哪些改进。

2、文件续传如何实现。

3、C#封装的网络操作类(像QQ.NET源码的Net实现)

4、远程协助的实现。

最后,希望大家共同讨论、共同进步!!!

P2PDemo.RAR可执行文件

[JavaScript]javascript 极速:隐藏/显示万行表格列只需 60毫秒

mikel阅读(881)

隐藏表格列,最常见的是如下方式:

td.style.display = "none";

 

这种方式的效率极低。例如,隐藏一个千行表格的某列,在我的笔记本(P4 M 1.4G,768M内存)上执行需要约 4000毫秒的时间,令人无法忍受。例如如下代码:

<body>
<input type=button onclick=hideCol(1) value='隐藏第 2 列'>
<input type=button onclick=showCol(1) value='显示第 2 列'>
<div id=tableBox></div>
<script>
//——————————————————–
//
 时间转为时间戳(毫秒)
function time2stamp(){var d=new Date();return Date.parse(d)+d.getMilliseconds();} 
//——————————————————–
//
 创建表格
function createTable(rowsLen)
{
    
var str = "<table border=1>" +
                
"<thead>" + 
                    
"<tr>" +
                        
"<th width=100>col1<\/th>" +
                        
"<th width=200>col2<\/th>" + 
                        
"<th width=50>col3<\/th>" + 
                    
"<\/tr>" +
                
"<\/thead>" +
                
"<tbody>";
    
var arr = [];
    
for (var i=0; i<rowsLen; i++)
    {
        arr[i] 
= "<tr><td>" + i + "1<\/td><td>" + i + "2</td><td>" + i + "3<\/td></tr>";
    }
    str 
+= arr.join(""+ "</tbody><\/table>"// 用 join() 方式快速构建字串,速度极快
    tableBox.innerHTML = str; // 生成 table
}
//——————————————————–
//
 隐藏/显示指定列
function hideCol(colIdx){hideOrShowCol(colIdx, 0);}
function showCol(colIdx){hideOrShowCol(colIdx, 1);}
// – – – – – – – – – – – – – – – – – – – – – – – – – – – –
function hideOrShowCol(colIdx, isShow)
{
    
var t1 = time2stamp(); // 
    var table = tableBox.children[0];
    
var rowsLen = table.rows.length;
    
var lastTr = table.rows[0];
    
for (var i=0; i<rowsLen; i++)
    {
        
var tr = table.rows[i];
        tr.children[colIdx].style.display 
= isShow ? "" : "none";
    }
    
    
var t2 = time2stamp();
    alert(
"耗时:" + (t2  t1) + " 毫秒");
}
//——————————————————–
createTable(1000); // 创建千行表格

</script>

 

遗憾的是,我们 google 出来的用 JavaScript 隐藏列的方式,都是采用这样的代码。
实际上,我们可以用设置第一行的 td 或 th 的宽度为 0 的方式,来快速隐藏列。
我们把 hideOrShowCol() 函数改为如下代码:

 

function hideOrShowCol(colIdx, isShow)
{
    
var t1 = time2stamp(); // 
    var table = tableBox.children[0];
    
var tr = table.rows[0];
    tr.children[colIdx].style.width 
= isShow ? 200 : 0;
    
    
var t2 = time2stamp();
    alert(
"耗时:" + (t2  t1) + " 毫秒");
}

 

不过,仅这样还达不到隐藏的效果,还需要设置 table 和 td 样式为如下:

<style>
table
{
    border-collapse
:collapse;
    table-layout
:fixed;
    overflow
:hidden;
}
td
{
    overflow
:hidden;
    white-space
: nowrap;
}
</style>

 

重新测试,我们发现,隐藏千行表格的某列,只需要不到 15毫秒的时间。而即使用 createTable(10000) 创建万行表格,再来测试,也只需要 60 毫秒的时间(都是以我的笔记本上的执行时间为参照。实际上,你们大多数人的电脑配置都比我的笔记本高很多,因此时间会更短),效率十分令人满意。

补充:

根据 无常 网友的提议,加上了对 colgroup 处理的代码。奇怪的是,虽然处理原理完全一样,但对 colgroup 进行处理的时间达到了 140毫秒,即延长了一倍。尚不清楚原因。

完整代码:


<style>
table
{
    border
collapse:collapse;
    table
layout:fixed;
    overflow:hidden;
}
td
{
    overflow:hidden;
    white
space: nowrap;
}
</style>
<body>
<input type=button onclick=createTable() value='创建表格:使用 thead'>
<input type=button onclick=createTable(1) value='创建表格:使用 colgroup'>
<br>
<input type=button onclick=hideCol(1) value='隐藏第 2 列'>
<input type=button onclick=showCol(1) value='显示第 2 列'>
&nbsp;&nbsp;
<input type=button onclick=hideCol_fast(1) value='快速隐藏第 2 列'>
<input type=button onclick=showCol_fast(1) value='快速显示第 2 列'>
<div id=tableBox></div>
<script>
var tableRowsLen = 10000// 创建万行表格

//——————————————————–
//
 时间转为时间戳(毫秒)
function time2stamp(){var d=new Date();return Date.parse(d)+d.getMilliseconds();} 
//——————————————————–
//
 创建表格
function createTable(isUseColGroup)
{
    
if (isUseColGroup) // 使用 colgroup 标签
    {
        
var str = "<table border=1>" +
                    
"<colgroup>" + 
                            
"<col width=100 />" +
                            
"<col width=200 />" + 
                            
"<col width=50 />" + 
                    
"<\/colgroup>" +
                    
"<tbody>";
    }
    
else
    {
        
// 使用 thead 标签
        var str = "<table border=1>" +
                    
"<thead>" + 
                        
"<tr>" +
                            
"<th width=100>col1<\/th>" +
                            
"<th width=200>col2<\/th>" + 
                            
"<th width=50>col3<\/th>" + 
                        
"<\/tr>" +
                    
"<\/thead>" +
                    
"<tbody>";
    }
    
var arr = [];
    
for (var i=0; i<tableRowsLen; i++)
    {
        arr[i] 
= "<tr><td>" + i + "1<\/td><td>" + i + "2</td><td>" + i + "3<\/td></tr>";
    }
    str 
+= arr.join(""+ "</tbody><\/table>"// 用 join() 方式快速构建字串,速度极快
    tableBox.innerHTML = str; // 生成 table
}
//——————————————————–
//
 隐藏/显示指定列
function hideCol(colIdx){hideOrShowCol(colIdx, 0);}
function showCol(colIdx){hideOrShowCol(colIdx, 1);}
// – – – – – – – – – – – – – – – – – – – – – – – – – – – –
function hideOrShowCol(colIdx, isShow)
{
    
var t1 = time2stamp(); // 
    var table = tableBox.children[0];
    
var rowsLen = table.rows.length;
    
var lastTr = table.rows[0];
    
if (rowsLen > 1001
    {
        
if (!confirm("将要对 1000 行以上的表格操作,这将非常耗时(甚至导致浏览器死掉)。\n您确定要继续吗?"))
            
return;
    }
    
for (var i=0; i<rowsLen; i++)
    {
        
var tr = table.rows[i];
        tr.children[colIdx].style.display 
= isShow ? "" : "none";
    }
    
    
var t2 = time2stamp();
    alert(
"耗时:" + (t2  t1) + " 毫秒");
}
//——————————————————–
//
 隐藏/显示指定列 – 快速
function hideCol_fast(colIdx){hideOrShowCol_fast(colIdx, 0);}
function showCol_fast(colIdx){hideOrShowCol_fast(colIdx, 1);}
// – – – – – – – – – – – – – – – – – – – – – – – – – – – –
function hideOrShowCol_fast(colIdx, isShow)
{
    
var t1 = time2stamp(); // 
    var table = tableBox.children[0];
    
var thead = table.children[0]; // 可能是 thead 或者 tbody,也可能是 colgroup
    if (thead.tagName.toLowerCase()=="colgroup"// 对 colgroup 特殊处理
    {
        
var td = thead.children[colIdx];
    }
    
else
    {
        
// 注意:如果表格没有 thead 和 tbody 标签,则 table.children[0] 是 tbody
        var tr = thead.children[0];
        
var td = tr.children[colIdx];
    }
    td.style.width 
= isShow ? 200 : 0;
    
    
var t2 = time2stamp();
    alert(
"耗时:" + (t2  t1) + " 毫秒");
}
//——————————————————–
createTable();
</script>

 

[Asp.Net]动态指定网页过期

mikel阅读(812)

*网页过期*

   Response.Expires   =   -1;
   Response.ExpiresAbsolute   =   DateTime.Now;
   Response.AddHeader("pragma",   "no-cache");
   Response.CacheControl   =   "no-cache";

在pageload事件里面加入

 

Response.Expires 指出当前网页经过多少时间后网页过期,单位“分钟”。IIS会根据给出的过期数值添加到HTTP头中。但由于服务器时间与客户端时间的不一致,例如时区的 原因,或者哪个出了问题,设置Response.Expires=0,将不会达到网页立即过期的效果,相反您可以通过设置 Response.ExpireAbsolute来达到立即过期的目的。更好的方式是设置Response.Expires为一个负数,例如:

<% Response.Expires=-1 %>

       也可使网页立即过期。

 

1.2.7.5.       ExpiresAbsolute

Response.ExpiresAbsolute通过设置具体的日期和时间来达到过期的效果。如果仅指定日期而没有指示时间,网页将在指定日期的午夜12:00过期;如果仅指定时间而不指定日期,网页将在脚本运行的当天那个时间过期。

<% Response.ExpiresAbsolute=#May 31,2004 11:11:11# %>

  上例将使网页在2004年5月31日11点11分11秒过期。

1.2.8.       方法

1.2.8.1.       AddHeader

AddHeader方法可以在发送的HTTP数据中添加相应的头信息。

下例将强迫客户端使用基本验证方式:

<% Response.Addheader "WWW-Authenticate", "BASIC"  %>

[Javascript]Javascript页面加载顺序

mikel阅读(780)

一、在HTML中嵌入Javasript的方法

  1. 直接在JavaScript代码放在标记对<script>和</script>之间
  2. 由<script />标记的src属性制定外部的js文件
  3. 放在事件处理程序中,比如:<p onclick="alert("我是由onclick事件执行的JavaScript")">点击我</p>
  4. 作为URL的主体,这个URL使用特殊的JavaScript:协议,比如:<a href="javascript:alert("我是由javascript:协议执行的javascript")">点击我</a>
  5. 利用javascript本身的document.write()方法写入新的javascript代码
  6. 利用Ajax异步获取javascript代码,然后执行

第3种和第4种方法写入的Javascript需要触发才能执行,所以除非特别设置,否则页面加载时不会执行。

二、Javascript在页面的执行顺序

  1. 页 面上的Javascript代码是HTML文档的一部分,所以Javascript在页面装载时执行的顺序就是其引入标记<script />的出现顺序, <script />标记里面的或者通过src引入的外部JS,都是按照其语句出现的顺序执行,而且执行过程是文档装载的一部分。
  2. 每个脚本定义的全局变量和函数,都可以被后面执行的脚本所调用。
  3. 变量的调用,必须是前面已经声明,否则获取的变量值是undefined。
    <script type="text/javscrpt">//<![CDATA[alert(tmp);  //输出 undefinedvar tmp = 1;alert(tmp);  //输出 1//]]></script>
  4. 同一段脚本,函数定义可以出现在函数调用的后面,但是如果是分别在两段代码,且函数调用在第一段代码中,则会报函数未定义错误。
    <script type="text/javscrpt">//<![CDATA[aa();            //浏览器报错//]]></script><script type="text/javscrpt">//<![CDATA[aa();//输出 1 function aa(){alert(1);}//]]></script>
  5. document.write()会把输出写入到脚本文档所在的位置,浏览器解析完documemt.write()所在文档内容后,继续解析document.write()输出的内容,然后在继续解析HTML文档。
    <script type="text/javascript">//<![CDATA[    document.write("<script type=\"text/javascript\" src=\"test.js\" temp_src=\"test.js\"><\/script>");    document.write("<script type=\"text/javascript\">");    document.write("alert(2);")    document.write("alert(\"我是\" + tmpStr);");    document.write("<\/script>");    //]]></script>  <script type="text/javascript">//<![CDATA[    alert(3);    //]]></script>

    test.js的内容是:

    var tmpStr = 1;    alert(tmpStr);
    • 在Firefox和Opera中的弹出值的顺序是:1、2、我是1、3
    • 在IE中弹出值的顺序是:2、1、3,同时浏览器报错:tmpStr未定义

    原 因可能是IE在document.write时,并未等待加载SRC中的Javascript代码完毕后,才执行下一行,所以导致2先弹出,并且执行到 document.write(’document.write(\"我是\" + tmpStr)’)调用tmpStr时,tmpStr并未定义,从而报错。

    解决这个问题,可以利用HTML解析是解析完一个HTML标签,再执行下一个的原理,把代码拆分来实现:

    <script type="text/javascript">//<![CDATA[    document.write("<script type=\"text/javascript\" src=\"test.js\" temp_src=\"test.js\"><\/script>");    //]]></script>  <script type="text/javascript">//<![CDATA[    document.write("<script type=\"text/javascript\">");    document.write("alert(2);")    document.write("alert(\"我是\" + tmpStr);");    document.write("<\/script>");    //]]></script>  <script type="text/javascript">//<![CDATA[    alert(3);    //]]></script>

    这样IE下和其他浏览器输出值的顺序都是一直的了:1、2、我是1、3。

三、如何改变Javascript在页面的执行顺序

  1. 利用onload
    <script type="text/javascript">//<![CDATA[window.onload = f;function f(){alert(1);}alert(2);//]]></script>

    输出值顺序是 2、1。

    需要注意的是,如果存在多个winodws.onload的话,只有最有一个生效,解决这个办法是:

    window.onload = function(){f();f1();f2();.....}

    利用2级DOM事件类型

    if(document.addEventListener){window.addEventListener("load",f,false);window.addEventListener("load",f1,false);...}else{window.attachEvent("onload",f);window.attachEvent("onload",f1);...}
  2. IE中可以利用deferdefer作用是把代码加载下来,并不立即执行,等文档装载完毕之后再执行,有点类似onload,但是没有onload那样的局限性,可以重复使用,但是只在IE中有效,所以上面的例子可以修改成为
    <script type="text/javascript">//<![CDATA[document.write("<script type=\"text/javascript\" src=\"test.js\" temp_src=\"test.js\"><\/script>");document.write("<script type=\"text/javascript\" defer=\"defer\">");document.write("alert(2);")document.write("alert(\"我是\" + tmpStr);");document.write("<\/script>");//]]></script><script type="text/javascript">//<![CDATA[alert(3);//]]></script>

    这样IE就不报错了,输出值的顺序变成:1、3、2、我是1

  3. 利用Ajax。
    因为xmlhttpRequest能判断外部文档加载的状态,所以能够改变代码的加载顺序。

[Flex]AmFast Remoting for Python

mikel阅读(949)

AmFast is a new AMF0/AMF3 encoder/decoder for Python.
• AmFast's core encoder and decoder are written in C, so it's around 18x faster than PyAmf.
• The encoder and decoder accept user-defined Python objects that allow customization of the encoding/decoding process.
• Supports custom class mapping.
• Supports remoting with NetConnection and RemoteObject.
• Remoting headers can be exposed to callable targets to allow for quick implementation of authentication and other AMF features that rely on headers.
• Supports data persistence with SQLAlchemy, including remotely-loadable lazy-loaded attributes.
• Supports Actionscript code generation for mapped classes.
Project Page
It is really great to see how opening the AMF and RTMP protocols have really improved integration with server languages. To exchange objects makes development so much easier.
Cheers,
Ted 🙂

[C#]ASP.NET MVC 1.0系列教程

mikel阅读(1119)

  1. ASP.NET MVC 1.0 – 1. 准备工作

  2. ASP.NET MVC 1.0 – 2. 流程分析 (System.Web.Routing)

  3. ASP.NET MVC 1.0 – 3. 流程分析 (MvcHandler & Controller)

  4. ASP.NET MVC 1.0 – 4. 流程分析 (ControllerActionInvoker)

  5. ASP.NET MVC 1.0 – 5. 流程分析 (ControllerActionInvoker 续)

  6. ASP.NET MVC 1.0 – 6. 流程分析 (ViewResult)

  7. ASP.NET MVC 1.0 – 7. Route Namespace

  8. ASP.NET MVC 1.0 – 8. TempData

  9. ASP.NET MVC 1.0 – 9. ModelBinder

  10. ASP.NET MVC 1.0 – 10. Controller

  11. ASP.NET MVC 1.0 – 11. ViewData

  12. ASP.NET MVC 1.0 – 12. NVelocityViewEngine

  13. ASP.NET MVC 1.0 – 13. OutputCacheAttribute

  14. ASP.NET MVC 1.0 – 14. CompressAttribute

  15. ASP.NET MVC 1.0 – 15. StaticCacheAttribute

  16. ASP.NET MVC 1.0 – 16. NoClientCacheAttribute

  17. ASP.NET MVC 1.0 – 17. Anti Attack

  18. ASP.NET MVC 1.0 – 18. ControllerContext

[C#]ASP.NET MVC : 实现我们自己的视图引擎

mikel阅读(1177)

ASP.NET MVC的一个开源项目MvcContrib中,为我们提供了几个视图引擎,例如NVelocity, Brail, NHaml, XSLT。那么如果我们想在ASP.NET MVC中实现我们自己的一个视图引擎,我们应该要怎么做呢?

我们知道呈现视图是在Controller中通过传递视图名和数据到RenderView()方法来实现的。好,我们就从这里下手。我们查看一下ASP.NET MVC的源代码,看看RenderView()这个方法是如何实现的:

protected virtual void RenderView(string viewName, string masterName, object viewData) {
ViewContext viewContext = new ViewContext(ControllerContext, viewName, masterName, viewData, TempData);
ViewEngine.RenderView(viewContext);
}//这是P2的源码,P3略有不同,原理差不多

从上面的代码我们可以看到,Controller中的RenderView()方法主要是将ControllerContext, viewName, masterName, viewData, TempData这一堆东西封装成ViewContext,然后把ViewContext传递给 ViewEngine.RenderView(viewContext)。嗯,没错,我们这里要实现的就是ViewEngine的 RenderView()方法。

ASP.NET MVC为我们提供了一个默认的视图引擎,这个视图引擎叫做:WebFormsViewEngine. 从名字就可以看出,这个视图引擎是使用ASP.NET web forms来呈现的。在这里,我们要实现的视图引擎所使用的模板用HTML文件吧,简单的模板示例代码如下:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns=""http://www.w3.org/1999/xhtml"">http://www.w3.org/1999/xhtml" >
<head>
<title>自定义视图引擎示例</title>
</head>
<body>
<h1>{$ViewData.Title}</h1>
<p>{$ViewData.Message}</p>
<p>The following fruit is part of a string array: {$ViewData.FruitStrings[1]}</p>
<p>The following fruit is part of an object array: {$ViewData.FruitObjects[1].Name}</p>
<p>Here's an undefined variable: {$UNDEFINED}</p>
</body>
</html>

 

下面马上开始我们的实现。首先,毫无疑问的,我们要创建一个ViewEngine,就命名为 SimpleViewEngine 吧,注意哦,ViewEngine要实现IViewEngine接口:

public class SimpleViewEngine : IViewEngine
{
#region Private members
IViewLocator _viewLocator = null;
#endregion
#region IViewEngine Members : RenderView()
public void RenderView(ViewContext viewContext)
{
string viewLocation = ViewLocator.GetViewLocation(viewContext, viewContext.ViewName);
if (string.IsNullOrEmpty(viewLocation))
{
throw new InvalidOperationException(string.Format("View {0} could not be found.", viewContext.ViewName));
}
string viewPath = viewContext.HttpContext.Request.MapPath(viewLocation);
string viewTemplate = File.ReadAllText(viewPath);
//以下为模板解析
IRenderer renderer = new PrintRenderer();
viewTemplate = renderer.Render(viewTemplate, viewContext);
viewContext.HttpContext.Response.Write(viewTemplate);
}
#endregion
#region Public properties : ViewLocator
public IViewLocator ViewLocator
{
get
{
if (this._viewLocator == null)
{
this._viewLocator = new SimpleViewLocator();
}
return this._viewLocator;
}
set
{
this._viewLocator = value;
}
}
#endregion
}

 

在这里实现了IViewEngine接口提供的RenderView()方法,这里要提供一个ViewLocator的属性。 ViewLocator的主要就是根据控制器中传来的视图名,进行视图的定位。在RenderView()方法中首先获取视图的路径,然后把视图模板读进 来,最后进行模板的解析然后输出。

我们再来看一下ViewLocator是如何实现的。他是IViewLocator类型的,也就是说SimpleViewLocator实现了IViewLocator接口。SimpleViewLocator的实现代码如下:

public class SimpleViewLocator : ViewLocator
{
public SimpleViewLocator()
{
base.ViewLocationFormats = new string[] { "~/Views/{1}/{0}.htm",
"~/Views/{1}/{0}.html",
"~/Views/Shared/{0}.htm",
"~/Views/Shared/{0}.html"
};
base.MasterLocationFormats = new string[] { "" };
}
} 

 

我们的SimpleViewLocator 是继承自ASP.NET MVC的ViewLocator类,而ViewLocator则是实现了IViewLocator接口的。由于ViewLocator已经为了完成了全部的工作,这里我们只需修改下他的ViewLocationFormats 来使用我们自己的模板文件就可以了。

我们再来看一下类图,那就更加清楚了:

image

注:关于模板解析的部分代码这里就不说了,不在讨论范围内,可以自己下载代码来看

现在我们基本完成了我们的视图引擎,那么如何让ASP.NET MVC不要使用默认的web forms视图引擎,而使用我们自定义的视图引擎呢?

在ASP.NET MVC中,所有的请求都是通过一个工厂类来创建Controller实例的,这个工厂类必须实现 IControllerFactory 接口。默认的实现该接口的工厂类是DefaultControllerFactory。这个工厂类就是我们修改默认的视图引擎为我们的视图引擎的入口点。 为了方便,我们创建一个继承自DefaultControllerFactory的SimpleControllerFactory :

public class SimpleControllerFactory : DefaultControllerFactory
{
protected override IController CreateController(RequestContext requestContext, string controllerName)
{
Controller controller = (Controller)base.CreateController(requestContext, controllerName);
controller.ViewEngine = new SimpleViewEngine();//修改默认的视图引擎为我们刚才创建的视图引擎
return controller;
}
} 

这里只要修改controller.ViewEngine为我们自定义的ViewEngine就可以了.最终的类图大概如下:

image

要使我们创建的控制器工厂类SimpleControllerFactory 成为默认的控制器工厂类,我们必须在Global.asax.cs中的Application_Start 事件中添加如下代码:

ControllerBuilder.Current.SetControllerFactory(typeof(SimpleControllerFactory));

到这里,我们已经完成了我们自己的视图引擎。

在ASP.NET MVC中实现自定义的视图引擎是很简单的,难点在于模板的解析,具体大家可以研究MvcContrib中的四个视图引擎的代码。最近要对模板引擎进行研究,大家有什么其他优秀的、成熟的、开源的模板引擎,麻烦给小弟推荐一下,先谢了。

Enjoy!

版权声明:本文首发于博客园,作者为QLeelulu
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则视为侵权。

参考文章:
ASP.NET MVC Preview生命周期分析
Creating a custom ViewEngine for the ASP.NET MVC framework(下面给出的源代码就是这篇文章给出的源代码)

 

本文源码下载: ASP.NET MVC 自定义视图引擎源代码下载

[应用]扩增实境:GE 替代能源网站

mikel阅读(862)

扩增实境是个近来发展得很快的领域,但想不到的是,这么快就已经有大型的公司在网站上利用这个技术了。GE 的 Ecomagination 网站没有真的利用扩增实境来做什么非常有意义的事,但光是看到扩增实境应用在网页上本身就是个很强悍的进步。继续阅读里看我们完整的操作影片吧~

[IE]IE不能显示Flash动画

mikel阅读(673)

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Internet Explorer\ActiveX Compatibility 下建了个
clsid :{D27CDB6E-AE6D-11CF-96B8-444553540000}
Click to Open in New Window
就是它禁用了flashplayer的activex控件,马上删除,
刷新页面,真是立竿见影啊,一切正常。