Web developer from Sydney Australia. Currently using asp.net, mvc where possible.

Thursday, May 13, 2010

Jobping Open Source URL Shortener. Goes to Version 0.5

We have updated our url shortener for Jobping. jobp.in has moved along to Version 0.5. While maintaining as little features as possible!

Since the initial version we have placed a restriction(web.configurable) on the domain names that the shortener will shorten. We did this so only domains under jobping.com would be processed by the shortener. We wanted to maintain the integrity of the jobp.in domain. With this in place you can be sure that if you click a link shortened by http://jobp.in  it will always go to our site jobping.com site.

A database migration to add case sensitivity to the short url column was added to the migration scripts. Previously this needed to be added manually after the install. Now it all runs out of the box, just x-copy, point it to a database and your good to go.

This version also includes a new custom style, which looks awesome by the way, for the site. The interface is now slick and styled inline with our main site Jobping.

The new version can be downloaded from codeplex here: http://jpurlshortener.codeplex.com/

The introduction blog post can be found here: http://markkemper1.blogspot.com/2010/05/announcing-jp-url-shortener-open-source.html
kick it on DotNetKicks.com

Tuesday, May 11, 2010

Quickly Trim all your model's string properties

While developing a internal application for http://www.jobping.com using ASP.NET MVC 2 I had the old problem of trimming all string properties on the model before saving to the database.

My first reaction was to start adding trim() everywhere but this causes problems when properties are null. Checking for null pushed me over the edge and I quickly came up with some code to do what I needed via reflection.

The code is below, it will quickly trim any string properties on your model object (no promises of course)



With this code, you can simply trim all string properties, lets say you have a blog model object called myBlog, just go myBlog.NullSafeTrimStrings(), job done.

Be warned this is quite an expansive little operation. DO NOT go putting it in a for each loop

kick it on DotNetKicks.com

Sunday, May 2, 2010

Announcing: Jobping URL Shortener. Open source MVC.NET 2 C#

Jobping is a niche job site for listing Microsoft related vacancies (Australia only for now). Jobping sends out tweets (1 for each job posted on the site) and we wanted to brand our short URLs e.g. http://jobp.in/g.

We could have used bit.ly pro but the pro accounts are in invite only (check it out though, they offer additional reports etc). We only very basic requirements we decided just to go ahead and implement our own url shortener.

Own URL shortener has a ‘less is more’ approach and was the quickest solution for us to get our own branded short URLs.

The site requires IIS 7, .net 3.5, asp.net mvc 2 and a mssql database.

Apart from creating our own URL shortener we also wanted to make it open source to help out other people looking for a bare bones URL shortener. You can find the source code on Codeplex here: http://jpurlshortener.codeplex.com/

For anyone new to MVC or interested in how it works, I’ll go through the basic functions of the site below.

Technical Description

The URL shortener has 2 functions
    1. Create a new short URL
    2. Redirect anyone requesting a short URL
Part 1 – Creating a short URL
Get request for the root URL “/”
ControllerIndexGet

This method on the Home controller simply returns the view. The view contains a form that allows someone or something to post a long URL and have a short URL returned.

The view for the GET request is shown below (This uses a common master page).

HomeIndexViewGet 

When the form is posted back to the “/” route the follow method is invoked.

ControllerIndexPost

You can see that this method is attributed with Http Post, this means that this method is only applicable when a HTTP Post is issued to the server. The method first checks if a short URL already exists for the long URL requested, if so the existing short URL is used. Otherwise a new URL object is created and saved into the database. You will notice that we first create a URL object in the database then use the Identity column as the seed to generate our short URL. Once we have generated our short URL we save this into the database. These operations exist inside a transaction to make sure we do not end up with a blank Short URL column in our DB.

The other point is that if the format parameters is present and contains the value “txt” a text only view is returned. This allows the same method to be used for API calls. Clients simply pass the format parameters and read the short URL from the result.

The Result view for the html result is shown below (This uses a common master page).

HomeIndexViewSuccess
The Result.TXT view is shown below, here no master page is used and the only the short URL is returned
HomeIndexViewSuccessTXT

I skipped over the actual creation of the short URL. The creation happens in our ShortUrlEncoder object. Basically this object base encodes the long integer value using a series of 64 characters that are suitable for URLs.

Here is a sample of what our encoder does to some sample numbers (in powershell)

Encoder

We simply just append the output of the encode to the current domain name, so “qCc” would be a short URL of “http://jobp.in/qCc”
2. Redirect anyone requesting a short URL
The redirection is really quite straight forward, we simple route all that are not “/” to our “Follow” method on the Home controller.

ControllerHomeFollow

As you can see, we simply retrieve the short URL from the database. If its not found, then return a 404, otherwise we redirect the user to the long URL destination. You can also see that we are using the current URL as the base when forming our short URL.

That’s it for now, you can check out the code on Codeplex here: http://jpurlshortener.codeplex.com/
We would welcome any feedback on the project.

kick it on DotNetKicks.com

Saturday, April 17, 2010

ASP.NET Force the Browser to Really CACHE Content


Recently I have been optimizing the load time for our site Jobping. Getting the browser to really cache a file (not request that file for the duration specified) is something that always takes some time to get right.

So I just wanted to share the code to achieve it and hope it helps someone else out.

TimeSpan duration = TimeSpan.FromMinutes(cacheDurationInMinutes);
HttpCachePolicy cache = HttpContext.Current.Response.Cache;
cache.SetCacheability(HttpCacheability.Public);
cache.SetExpires(DateTime.Now.Add(duration));
cache.SetMaxAge(duration);
cache.AppendCacheExtension("must-revalidate, proxy-revalidate");

FieldInfo maxAgeField = cache.GetType().GetField("_maxAge", BindingFlags.Instance | BindingFlags.NonPublic);
maxAgeField.SetValue(cache, duration);

This will instruct the browser to store a copy of the url andfor the duration given. So be sure that this is applicable for your content before using this code (YMMV).




kick it on DotNetKicks.com

Sunday, June 15, 2008

Mvc.Net preview 3 validation - server and client side

WORKING EXAMPLE: http://www.xsation.net.au/Samples/MvcValidation/Comment.aspx
DOWNLOAD SOURCE: http://www.xsation.net.au/Samples/MvcValidation/MvcValidationSample.zip

If you need some basics on MVC check out:

Over the last few weeks I have been toying with .net .mvc framework.

While enjoying the freedom that mvc offers from the 'old school'(too early?) .net forms , there are a few sore points
when working with mvc:

  1. No composite controllers

  2. No built in support for validation on the server and client site

Scott Gu has hinted that we will see support for validation in the upcoming releases of the mvc framework, however I need something now.

The rest of the post describes the approach I am trialling at the moment using jquery

The controller

First on the controller I wanted my action method to simply look like the following:

  1. Is this a get request? yes -> return the form(view)..

  2. It's a post. Is the form validated? no -> return the form with errors

  3. It's a validate post. Save the entity then redirect somewhere

The code example deals with a simple scenario where we collect a comment and displaying the list of comments collection so far on the page.

In my example, the c# code for the controller looks like this


//Initialize the view data
CommentListData vd = new CommentListData();

//add the current list of comments to the view data
vd.Comments = _comments;

//1. Its is a gets , just return the list of comments and form
if (Request.HttpMethod == "GET")
return Index();

//Check if the form is valid...
if (!vd.Form.Validate(Request.Form))
{
//if its not, the form will have all the errors messages populated in the viewddata (vd).
return View("Index", vd);
}

//Form is valid, so we can add our new comment.
Comment comment = new Comment();

//Set all validated properties
vd.Form.UpdateEntity(comment);
_comments.Add(comment);

///Redirects back to the comment list of comments.
return RedirectToAction( "Index");

Although a little long, it achieves the 3 goals above of how a simple form post should be handled.

The definition of the CommentListData object is show below

   public class CommentListData
{
public CommentListData() { this.Form = new CommentForm(); }

public List Comments { get; set; }
public CommentForm Form { get; set; }
}

This is pretty self explanatory, we have a list of comments and a form. The form object deals with the collection of the 3 inputs from the
posted form, 1) A person's name 2) A person's email and 3) the persons comment.

The form object provides the following basic functions

  1. Holds the raw (string) data posted from the raw http form

  2. Performs validation to determine if the data is valid

  3. Transfers the validated data into the real typed entity

The actual implementation of the CommentForm is posted below.


public class CommentForm : Form
{
public FormField Name = new FormField("Comment_Name");
public FormField EmailAddress = new FormField("Comment_EmailAddress");
public FormField Comments = new FormField("Comment_Comments");

public RequiredFieldValidator NameRequired;
public RequiredFieldValidator EmailAddressRequired;
public RequiredFieldValidator CommentsRequired;
public RegexFieldValidator EmailAddressRegex;

public override void AddValidators(List validators)
{
this.NameRequired = new RequiredFieldValidator("Please enter your name", Name);
this.EmailAddressRequired = new RequiredFieldValidator("Please enter your email address", EmailAddress);
this.CommentsRequired = new RequiredFieldValidator("Please enter your comments", Comments);
this.EmailAddressRegex = new RegexFieldValidator("Please check the format of your email address", EmailAddress, "..*@..*\\...*");

validators.Add(this.NameRequired);
validators.Add(this.EmailAddressRequired);
validators.Add(this.CommentsRequired);
validators.Add(this.EmailAddressRegex);
}
}

Here we define our 3 fields that we wish to collection form the raw http form. Then we add all our validators to the form

The CommentForm inherits from a generic Form object, we pass our Comment object in as the generic. The base form object can handle the following functions now it has a Comment object

  1. It can validate the posted form using the validates we have described

  2. It can set the form up with initial values from a Comment object( used for editing purposes)

  3. It can updated the Comment entity based on a valid form

The Form object uses reflection and some exiting MVC helpers that also use reflection to achieve these functions.

The implementation details for the form object can be browsed in the download available at the top of this article.

The html view layer

The full html for the comment form is posted below. Its should be pretty straight forward for those familiar with .net and html.

For those keeping up to date with mvc you will notice a lack of the Html Helper methods, this is a personal preference. I like to have my html out in open for everyone to see, not hidden in some server method.


<fieldset class="comment">
<legend>Add your comment</legend>
<div class="inner">
<form id="addCommentForm" method="post" action="<%= Url.AddCommentUrl() %>">


<ul id="error_list" class="error" <%= Html.RenderDisplayNone(this.ViewData.Model.IsValid) %> >
<h6>There were errors!</h6>
<% foreach(var e in this.ViewData.Model.Validators){ %>
<li <%= Html.RenderDisplayNone(e.IsValid ?? true) %> class=" <%= e.ClientId %>" ><%= e.Message %></span></li>
<% } %>
</ul>
<div class="row">
<label for="Comment_Name">Name:</label>
<input id="Comment_Name" name="Comment_Name" class="text" value="<%= this.ViewData.Model.Name %>" type="text" />
<span class="error Comment_Name_Required" style="display: none" >« <%= this.ViewData.Model.NameRequired.Message %></span>
</div>

<div class="row">
<label for="Comment_EmailAddress">Email:</label>
<input id="Comment_EmailAddress" name="Comment_EmailAddress" value="<%= this.ViewData.Model.EmailAddress %>" type="text" />
<span class="error Comment_EmailAddress_Required" style="display: none" >« <%= this.ViewData.Model.EmailAddressRequired.Message%></span>
<span class="error Comment_EmailAddress_Regex" style="display: none" >« <%= this.ViewData.Model.EmailAddressRegex.Message%></span>
</div>

<div class="row">
<label for="Comment_Comments">Comments:</label>
<textarea id="Comment_Comments" name="Comment_Comments" rows="10" cols="60"
><%= this.ViewData.Model.Comments %></textarea><span class="error Comment_Comments_Required" style="display: none" >« !</span>
</div>

<div class="row button">
<input value="Add Comment" type="submit" />
</div>

<val:ClientValidators FormId="addCommentForm" Form="<%# this.ViewData.Model %>" runat="server" />
</form>
</div>
</fieldset>

There are a couple of things to note here, firstly the ClientValidators server control a the bottom. This can be viewed in the download
but basically it initializes a bunch of javascript objects to help with client side validation (output of the control shown below). The script attaches the validators to the actual html form DOM object


<script type="text/javascript">var form = document.getElementById('addCommentForm');
form.validators = new Validators();
form.validators.array[0] = new Validator('Please enter your name', 'validate_RequiredField', 'Comment_Name_Required', 'Comment_Name' , '');
form.validators.array[1] = new Validator('Please enter your email address', 'validate_RequiredField', 'Comment_EmailAddress_Required', 'Comment_EmailAddress' , '');
form.validators.array[2] = new Validator('Please enter your comments', 'validate_RequiredField', 'Comment_Comments_Required', 'Comment_Comments' , '');
form.validators.array[3] = new RegexValidator('Please check the format of your email address', 'Comment_EmailAddress_Regex', 'Comment_EmailAddress' , '', '..*@..*\...*');
</script>

Secondly, the html inputs are initialized from the form values, this allows an invalid value to be posted to the server and then redisplayed with an error message. (it allows us to keep invalid values during a round trip)

Thirdly, there is a simple Html helper, to render "style="display: none" based on a boolean value.

JQuery is used to perform the client side validation. It makes use of the array of validators to perform client side validation. A portion of the jquery is show below.


//when ready
$(document).ready(function(){

//get every form in this document (1 in this case)
$("form").each(
function()
{
//if validators exist
if(this.validators)
{
//attach the validates to the inputs
attachValidator(this.validators);

//attach efferts to the inputs
attachValidatorEffects(this.validators);
}

$(this).submit(function()
{
if(this.validators)
{
var isValid = this.validators.validate();
var errorList = $('#error_list');
errorList.hide();

for(var i=0; i < this.validators.array.length; i++)
{
UpdateValidatorDisplay(this.validators.array[i]);
}

if(!isValid)
errorList.fadeIn();
else
$(this).css("visibility","hidden");

return isValid;
}
});
});
});


function attachValidatorEffects(validators)
{
//loop through each validotor
for(var i=0; i < input =" $(validators.array[i].targetDom);" j="0;">

I'll be the first to admit my javascript skills are lacking but I did get the job done. The form in action can be viewed here
http://www.xsation.net.au/Samples/MvcValidation/Comment.aspx.

This is a rough start to validation using mvc and I am sure Scott and the team will come up with something far better. But for now, I hope it either helps you out a bit or provides some discussions points moving forward.

kick it on DotNetKicks.com

Monday, November 26, 2007

Forget Page Methods move on to Script Services

This entry is to guide you through the transition from the page method to the script service. Don't worry it won't hurt a bit...

Full Sample Code

Using page methods is a great idea for getting more performance out of your ajax apps but if they have a few draw backs

  1. They don’t promote reusability, user controls methods would but sadly they missed the boat.

  2. Getting them to actually work on a hosted server is somewhat of a mistory (see: http://www.west-wind.com/WebLog/posts/152493.aspx)

The best thing about page methods is that the converted me to using Web Services or Script Services. Script services are regular web services with a few extra attributes. The good thing is that you can have a javascript proxy built for you just like a page method. Better still, the web service is actually usable from anywhere...

The hello world of the script service.

Fire up visual studio and start a new Web Project (or web site if you must) .

Add a new Web Service

The Hello world template should be already present, as below .... (I changed the method name to ‘Hello’ so it’s different from the class name)

/// <summary>
/// Summary description for HelloWorld
/// </summary>
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[ToolboxItem(false)]
public class HelloWorld : System.Web.Services.WebService
{

[WebMethod]
public string Hello()
{
return "Hello World";
}
}

The add a reference to the System.Web.Extensions library and add the script service attribute to the class as below.
using System;
using System.Web.Services;
using System.Web.Services.Protocols;
using System.ComponentModel;

using System.Web.Script.Services;

namespace ScriptServiceExample
{
/// <summary>
/// Summary description for HelloWorld
/// </summary>
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[ToolboxItem(false)]
[ScriptService]
public class HelloWorld : System.Web.Services.WebService
{

[WebMethod]
public string Hello()
{
return "Hello World";
}
}
}
That’s it. Done. We have converted out web service into a script service. Now we will move onto the default.aspx file and consume our script service.

Firstly check you web.confg has the necessary handlers defined.

<httphandlers>
<remove verb="*" path="*.asmx">
<add verb="GET,HEAD" path="ScriptResource.axd" type="System.Web.Handlers.ScriptResourceHandler, System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" validate="false">
<add verb="*" path="*.asmx" type="System.Web.Script.Services.ScriptHandlerFactory" validate="false">
</add>

</add></remove></httphandlers>
Now, Add a ScriptManger to the page and define the script service as follows.
<asp:scriptmanager id="ScriptManager1" runat="server">
<services>
<asp:servicereference path="/HelloWorld.asmx">
</asp:servicereference>
</services>

</asp:scriptmanager>
The web page will compile and run at this stage but viewing a blank page less than impressive.

If you got firebug or other means you can run the page and check that the following javascript is actually generated and linked to the page..

Type.registerNamespace('ScriptServiceExample');
ScriptServiceExample.HelloWorld=function() {
ScriptServiceExample.HelloWorld.initializeBase(this);
this._timeout = 0;
this._userContext = null;
this._succeeded = null;
this._failed = null;
}
ScriptServiceExample.HelloWorld.prototype={
Hello:function(succeededCallback, failedCallback, userContext) {
return this._invoke(ScriptServiceExample.HelloWorld.get_path(), 'Hello',false,{},succeededCallback,failedCallback,userContext); }}
ScriptServiceExample.HelloWorld.registerClass('ScriptServiceExample.HelloWorld',Sys.Net.WebServiceProxy);
ScriptServiceExample.HelloWorld._staticInstance = new ScriptServiceExample.HelloWorld();
ScriptServiceExample.HelloWorld.set_path = function(value) {
var e = Function._validateParams(arguments, [{name: 'path', type: String}]); if (e) throw e; ScriptServiceExample.HelloWorld._staticInstance._path = value; }
ScriptServiceExample.HelloWorld.get_path = function() { return ScriptServiceExample.HelloWorld._staticInstance._path; }
ScriptServiceExample.HelloWorld.set_timeout = function(value) { var e = Function._validateParams(arguments, [{name: 'timeout', type: Number}]); if (e) throw e; if (value < _timeout =" value;" get_timeout =" function()" set_defaultusercontext =" function(value)" _usercontext =" value;" get_defaultusercontext =" function()" set_defaultsucceededcallback =" function(value)" e =" Function._validateParams(arguments," _succeeded =" value;" get_defaultsucceededcallback =" function()" set_defaultfailedcallback =" function(value)" e =" Function._validateParams(arguments," _failed =" value;" get_defaultfailedcallback =" function()" hello=" function(onSuccess,onFailed,userContext)" href="http://www.blogger.com/post-edit.g?blogID=3340352991227979961&postID=790319424207465397">Ping the server
This will not work yet because the function is defined as “ScriptServiceExample.HelloWorld.Hello” but just to encapsulate this call I am going to add an helper javascript file called HelloWorldHelper.js

The final html looks like this..

<form id="form1" runat="server">


<div>
<asp:scriptmanager id="ScriptManager1" runat="server">
<services>
<asp:servicereference path="/HelloWorld.asmx">
</asp:servicereference>
</services>

<script src="helloWorldHelper.js" type="text/javascript"></script>
<a href="javascript: Hello()">Ping the server</a>

</asp:scriptmanager><div id="processing" style="display: none;">
fetching
</div>
<div id="results">
</div>

</div>
</form>
NB: You should move the script include into the head of your document

The javascript file looks like this..

function Hello()
{
$get('processing').style.display = 'block';
ScriptServiceExample.HelloWorld.Hello(SuccessCallBack);
}

function SuccessCallBack(result)
{
$get('results').innerHTML = result;
$get('processing').style.display = 'none';
}

That’s it, consuming your script service couldn’t be easier.

Sending complex results to your web page is as easy as returning an object from your web service. The object will automatically be serilised using JSON (although you can specify XML if you want).

Quick code for returning an object.

[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[ToolboxItem(false)]
[ScriptService]
public class HelloWorld : System.Web.Services.WebService
{

[WebMethod]
public HelloWorldResponse Hello()
{
HelloWorldResponse r = new HelloWorldResponse();
r.Message = "Hello World";
r.Time = DateTime.Now;
return r;
}
}

[Serializable]
public class HelloWorldResponse
{
public string Message;
public DateTime Time;
}

Comsuming the object, client side

function Hello()
{
$get('processing').style.display = 'block';
ScriptServiceExample.HelloWorld.Hello(SuccessCallBack);
}

function SuccessCallBack(result)
{
var resultObject = eval(result)
$get('results').innerHTML = "The Message is:" + resultObject.Message + " The message was created on " + resultObject.Time;
$get('processing').style.display = 'none';
}

Visit the offical site for more info