Blog Stats
  • Posts - 27
  • Articles - 0
  • Comments - 19
  • Trackbacks - 10

 

Wednesday, May 21, 2008

Restricting HTTP methods with an Asp.net MVC ActionFilter

HTTP methods are not often thought about when coding Asp.net webforms applications. Links are GETs, buttons are POSTs and it all happens automatically. With Asp.NET MVC, and other MVC frameworks like Rails, the HTTP method used is more obvious and developers are begining to care about which they use.

The problem is that GET requests tell visitors to your site, including search engines, client-side web optimizers and other automatic tools, that it is safe to make the request. Which is a problem if your checkout button causes a GET. To quote Dave Thomas, paraphrasing Tim Berners-Lee, "Use GET requests to retrieve information from the server, and use POST requests to request a change of state on the server".

To help me correctly control which HTTP methods are used to access my controller actions I created an ActionFilterAttribute. ActionFilters provide a declarative way to access the executing context immediately prior to, and immediately following, the execution of an action. They are an excellent way to introduce aspect oriented programming to an asp.net mvc application. To use my action filter you attribute a controller action like this:

[AllowedHttpMethods(AllowedMethods= new HttpMethods[] {HttpMethods.POST})]
public void Save()
{ ... }
The code for the Action Filter inherits from ActionFilterAttribute and overrides the OnActionExecuting event.

public class AllowedHttpMethodsAttribute : ActionFilterAttribute
{
public HttpMethods[] AllowedMethods { get; set; }

public override void OnActionExecuting(FilterExecutingContext filterContext)
{
int count = AllowedMethods.Count(m => m.ToString().Equals(filterContext.HttpContext.Request.HttpMethod));
if (count == 0) throw new Exception("Invalid http method: " + filterContext.HttpContext.Request.HttpMethod);
}
}

public enum HttpMethods
{
GET,POST
}

By adding the AllowedHttpMethods attribute to all of my controller actions I can assure that http methods are used correctly.

I also use an action filter attribute to authorize which roles can access with actions. The technique I used is based upon the article Securing Your Controller Actions by Rob Conery.

How to Generate URLs with ASP.NET MVC

I have been working with ASP.NET MVC for some time and yet I still had trouble trying to generate a URL in a view. URL generation is particularly important for ASP.NET MVC because it uses a routing engine to map URLs to code. If we hard code a URL then we lose the ability to later vary our routing scheme.

I have found two ways that currently (ASP.NET MVC preview 2) work to generate URLs in a view. The first uses the GetVirtualPath method and seems overly complicated - so I wrapped it in a global helper:

        public static string GenerateUrl(HttpContext context, RouteValueDictionary routeValues)
{
return RouteTable.Routes.GetVirtualPath(
new RequestContext(new HttpContextWrapper2(context), new RouteData()),
routeValues).ToString();
}
But then I found that I could achieve the same result more simply using UrlHelper, accessible via the Url property of the view.
// link to a controller 
Url.Action("Home");

// link to an action
Url.Action("Home", "Index");

// link to an action and send parameters
Url.Action("Edit", "Product", new RouteValueDictionary(new { id = p.Id }));
Or, if you want the url for a hyperlink you can get that in one step using the ActionLink method on the Html property:
Html.ActionLink<HomeController>(c => c.Index(),"Home")
So I no longer see a need for my GenerateUrl method and have removed it from my helper. All of this would be much easier if there was some documentation. Im sure there is a better way so if you can think of an improvement please leave it in the comments.

Cashbacks are a scam

Lately I have noticed a trend in electronics retailing. Shops have started selling a large number of items with cashbacks. Here is an example of what I mean. Cashbacks are provided by the wholesaler to the consumer as a means of passing on discounts in a way that cannot be absorbed by the retailer. For example, if the wholesaler discounts the cost of an item by $250 what is to stop the retailer absorbing the $250 as additional profit? But if the wholesaler offers a $250 cashback then the consumer is theoretically protected from the retailers opportunism.

I strongly dislike cashbacks. They are effectively an interest free, indefinite term, loan from the consumer to the wholesaler. Instead of the vendor asking the consumer for money in exchange for goods the consumer is left asking the vendor for their own money back. Frankly there is very little compelling the vendor to actually pay the cash back. Here is an exerpt I received from Sony after I tried to claim a cashback for my television:

We've sent you this email to let you know that we have received the information you sent us in order to claim CASHBACK.
However, the following problem(s) have been identified with the cash back claim(s):

Claim XXXXXX - $150.00 cash back for BRAVIA TV Model:KDL40D3100 -
We did not receive the section of the product packaging showing the product serial number.

This email was sent without any contact details that might be used to resolve the problem. Of course the problem is that they did receive the serial number in question. I cannot resend the piece of packaging that I have already sent to them. The lack of contact information in their email is what annoys me the most. The message is clearly that they will resist paying as much as they can.

When important things go missing, such as the piece of packaging required to claim a $150 cashback, it makes me wonder if someone has found a way to beat the system.
 

 

Copyright © Liam McLennan