Controllers
Controllers are used to handle URLs in a predefined way. The URL are splitted up into parts on each slash. For example: "/user/edit/1" says that we should invoke the method "edit" in UserController (may also be called just User). "1" are put in a property called Id.
The following controller would handle the above mentioned URL:
public class UserController : RequestController
{
public string Edit()
{
return "Ohh, you wanted to edit " + Id + "?";
}
}
That's all code needed to handle that url.
Send binary
Returning a string works great for most of the time, but sometimes you need to send a file or stream something.
You can do this by tagging a method with the RawHandler attribute, which tells the controller module to add a method not returning anything.
public class UserController : RequestController
{
\[RawHandler\]
public string File()
{
Response.ContentType = "application/octet-stream";
// pseudo code
Response.ContentLength = File.Size("mypath" + Id);
file = File.Open("mypath" + Id);
byte[] buffer = new byte[8192];
while (file.read(buffer))
Response.SendBody(buffer, 0, 8192);
}
}
Streaming
Streaming is not 100% done, it requires the use of chucked encoding.
Before filters
Before filters are invoked before each handler method. They can be used to do any additional processing or just determine if the handler method should be invoked at all. They are therefore perfect to determine if a user have been logged in or not.
Example 1: Class filter
public class UserController : RequestController
{
TemplateManager _mgr;
public UserController(TemplateManager mgr)
{
_mgr = mgr;
}
public string Login()
{
return _mgr.Render("public\\views\\user\\login.haml", "username", string.Empty);
}
// called by login form
public string DoLogin()
{
//Using my favorite ORM Layer (coded by me ;)) Can be found on www.assembla.com in project Tiny Library
User usr = Program.DataManager.Fetch<User>(new string[] {"UserName = ? AND Password = ?", Request.Form["user"]["name"].Value, Request.Form["user"]["password"].Value});
if (usr == null)
return _mgr.Render("public\\views\\user\\login.haml", "username", Request.Form["user"]["name"].Value);
Session["user"] = usr;
Response.Redirect("/user/index/");
return null;
}
// All non logged in users will be redirected to login page thanks to the before filter.
public string Index()
{
_mgr.Render("public\\views\\user\\index.haml", "user", Session["user"]);
}
[[BeforeFilter("ValidateUser")]]
protected bool ValidateLogin()
{
if (MethodName == "login" || MethodName == "dologin")
return true; // should always be able to login =)
User user = (User)Session["user"];
if (user == null || user.Status < UserStatus.RegularUser)
{
Response.Redirect("/user/login/");
return false; // should not be able to access the page.
}
return true;
}
}
Normally I would put the rendering and the before filter in a base controller which all other controllers derive. In that way you will never have to think about login handling or how the rendering is done.