|
 Thursday, April 19, 2007

Forcing a download in ASP.NET - How and Why

I really did think this was something every ASP.NET Developer knew how to do. I'd also like to add that I was also under the assumption (which is false) that most all ASP.NET developers know the security risk associated with persisting sensitive (financial, personal, etc) information to a directory available to the web. The example I'm talking about is when a user views a report from their account online and they choose to "export" it for download, this export should NEVER EVER EVER be persisted on the disk (unless you have some major security backing it up or unless its getting deleted right away, but still, I dont recommened it).

I'm going to demonstrate how to force a download, and yes, I'm aware that this has been done by thousands of other sites already. The problem is, apparently there are not enough sites out there that stress the importance of this, so dont flame me for reiterating a supposed well known simple implementation. :) This fits into the same category as "why is your entire business logic in your code behind area", but thats another post, for another day.

The reason is, if we persist a file, lets call it "BobsFinancials.txt" to a directory called "Exports" in the website and then we give Bob a link to download it; he can download it, but so can everyone else. Lets say that "Susy" logs into the system, Susy is Bob's Ex-Girlfriend and she can't stand him, so she has it out for him. Susy knows that Bob uses the same bank as her so she knows that he has the same account screens she does. Susy performs a export for her own account, she see's the url is http://www.example.com/exports/SusysFinancials.txt . She is able to figure out (since she knows that Bob is a user on the system) that Bob's financials might also be in the "exports" folder. So she types in the url: http://www.example.com/exports/BobsFinancials.txt, and blammo, now she has Bobs information.

You'd be surprised how many times I've seen this happen. DOZENS upon DOZENS upon DOZENS. 

So, if we are not to persist the data to the disk, what are we to do? How can we give the data to the user?

HOW TO FORCE A DOWNLOAD AND NOT PERSIST

Well, the data has to come from somewhere, a datastore of some type. You have to build the export somehow (through code). And then you want to give it to the user. Here's how.

1. Buid your export (how ever you want, for example this could be a string of random data)

2. Send it to the user, by forcing a download. We will not be

We've all see it before the dialog box that has a "Save" button present. (Again, I was under the false impression that everyone knew how to do this.) So we need to force a download. In the background whats going on is that the report is stored in memory, we're going to take it from the server's memory and pass it directy to the user through the response stream.

Code

// textExportBuilder - A String Builder that was used to build the report.

sFileName = "BobsFinancials.txt";
Response.ContentType = "application/octet-stream";
Response.AddHeader("Content-Disposition", string.Format("attachment; filename={0}", sFileName));
Response.Write(textExportBuilder);
Response.End();

This code uses the AddHeader method of the Response Object to force the user to download the file. The file has not been saved to the server, it was generated on the fly and given to the user when they needed it and now its gone. There is no actual physical file persisted on the disk that other users can download.

This is a very simple, very beginner thing that A LOT of developers are not aware of.

#    Comments [1] |
 Tuesday, April 17, 2007

Unit Testing .NetTiers Processors with Dependency Injection

Recently my company decided it was time to automate some of the plumbing work, such as the data access layer (thankfully). I checked out a few options (LLBLGen, MyGeneration, CodeSmith and SubSonic) and finally decided on CodeSmith because I had used it before and I felt it was the best fit for the company (in many different areas).

I'm using .NetTiers to create the plumbing code for our applications.

In .NetTiers, Processors are used to add custom business logic into the processing of your data. For example, perhaps you want to make sure that a country only exists ONCE in your system, you could add this functionality here. This thread covers the topic of how to add a a processor to your service layer.

The only problem is that if you want to test your processor using Unit Testing, you are now also testing the data access, database, etc. Its tightly coupled, there's not an easy way to test this. But in actually, there is. Here's how:

The problem lies in the ValidateIfCountry method. It has a dependency on the "CountryService". We cannot mock this out. Its tightly coupled with the Processor. We can eliminate this coupling by injecting a reference to a "CountryService" through constuctor injection. Here's how. (please refer to this thread for full doc on this class).

Previous Constructor Code (with private members shown):

private Entities.Country _country;

/// <summary>
/// Country Processor
/// </summary>
/// <param name="country"></param>
public VerifyCountryProcessor(Entities.Country country)
{
   this._country = country;
}

New Constructor Code, injecting a CountryService:

private Entities.Country _country;
private CountryServiceBase _serviceBase;

/// <summary>
/// Country Processor
/// </summary>
/// <param name="country"></param>
public VerifyCountryProcessor(Entities.Country country, CountryServiceBase serviceBase)
{
   this._country = country;
   this._serviceBase = serviceBase;
}

How to Inject it from the original CountryServiceClass

In the example provided by the .NetTiers team, they said to override the Insert Method with this code, to get the processor to run:

Old Code

public override bool Insert(Order entity)
{
   ProcessorList.Add(new VerifyCountryProcessor(entity));
   Execute();
   return base.Insert(entity);
}

This is is correct, but this time we need to inject a CountryService object into the Processor.

New Code

public override bool Insert(Order entity)
{
   ProcessorList.Add(new VerifyCountryProcessor(entity, this));
   Execute();
   return base.Insert(entity);
}

Since we are already in the "CountryService" class when we're overriding the Insert Method, and since the CountryService inherits from "CountryServiceBase", we can inject this reference into the InventoryProcessor through the constuctor. We have now eliminated the tightly coupled reference inside of the processor. We can now test the functionality through unit testing.

How To Test

I utilize Rhino Mocks for my mocking so here's how to do it with Rhino Mocks.

#region Using Directives
using System;
using System.Collections.Generic;
using System.Text;

using ExampleCompany.App.Entities;
using ExampleCompany.App.Services;
using NUnit.Framework;
using Rhino.Mocks;
using ExampleCompany.App.Services.Pipeline;

#endregion

namespace ExampleCompany.App.UnitTests
{
   [TestFixture]
   public class CountryProcessorTest
   {
      #region Test Setup
      MockRepository mocks;
      CountryService countryService;

      [SetUp]
      public void SetUp()
      {
         mocks = new MockRepository();
         countryService = mocks.CreateMock<CountryService>();
      }


      [TearDown]
      public void TearDown()
      {
         mocks.VerifyAll();
      }

      #endregion 

      #region Tests
      [Test]
      public void TestCountryProcessorForDuplicateName()
      {
         Country country = new Country();
         country.Country_Name = "FOO";

         // Needed for a return value
         TList<Country> cs = new TList<Country>();
         cs.Add(country);

         string whereClause = String.Format("{1} = '{0}'", CountryColumn.Country_Name.ToString(), country.Country_Name);

         Expect.Call(countryService.Find(whereClause)).Return(cs);

         Country country2 = new Country();
         country2.Country_Name = "FOO";

         VerifyCountryProcessor countryProcessor = new VerifyCountryProcessor(countryService, country2);

         Expect.Call(countryService.ToString()).Repeat.Any();

         mocks.ReplayAll();

         IProcessorResult result = countryProcessor.Process();

         Assert.AreEqual(false, result.Result);
         Assert.AreEqual("The country FOO already exists and cannot be added or updated.", result.BrokenRulesLists[typeof(ExampleCompany.App.Entities.Country)][0].Description);
      }


      #endregion
   }
}

Conclusion

By utilizing dependency injection in the processors, we're able to successfully test the processor utiliting unit testing without having to worry about a database connection at the same time. Very helpful. :)

#    Comments [0] |
 Monday, April 16, 2007

Tree Surgeon Development

As of a couple weeks ago, I joined the Tree Surgeon open source project. Recently, Bil Simser took over the project and opened it up on CodePlex. The development team will be adding many new features to the application to help in the setup of the development tree structure and build integration process. Keep your eyes peeled!

#    Comments [0] |
 Friday, April 13, 2007

The .NET Developer Search Engine

While listenening to a recent episode of DotNetRocks, Dan Appleman noted that he created a Google Custom Search Engine called SearchDotNet.com. This search engine searches only relevant .NET sites that Dan himself deems as "experts" in the fields. THis search engine helps with the topic of Discoverability (see the front page of SearchDotNet.com for more info on Discoverability).

Example:

He gave a good example, go to Google.com and search for "RSS".

The top 5 results are:

  1. Rss (wikipedia)
  2. Xml.com (what is Rss)
  3. Rss 2.0 Specification
  4. New York Times Rss
  5. Rdf Site Summary

As a .NET developer this poses no great info for example: how to implement Rss, or maybe .NET Rss feeds.

Enter Dan's search engine, SearchDotNet.com.

Entering the same exact search phrase returns vastly different topics (in regards to .NET).

  1. Scott Gutheries Blog feed
  2. Raymond Chen's blog
  3. Asp.net blogs
  4. Microsoft Team Rss Blog
  5. The ServerSide Rss Feed

Try searching for other topics on his site, such as the word "Triplet". Mads Kristensen blogged about this class awhile back, but searching for "Triplet" on Google.com would return you all kinds of non-.NET related articles.

Dan recommended that the developer try to search on SearchDotNet.com first, then if you cannot find what you're looking for, then go search on Google.com. Thats what I've done and found it immensely useful.

See for yourself, go try it out now.

kick it on DotNetKicks.com
#    Comments [0] |
 Thursday, April 12, 2007

Codeplex is down?

This morning, I went to log into Codeplex and I got the dreaded YSOD. You'd think that Microsoft would have this under wraps. With all of the redundancy and technology that Microsoft has at its fingertips, you'd think this wouldnt be an issue. Sometimes it's great to know, that even the big guys make mistakes. :)  

#    Comments [0] |