Tuesday, September 20, 2011

Force SSL in VB.NET

Today I'm wrapping up a project to convert an existing web site to use SSL communication only. This required adding HTTPS detection in various places to prevent mixed-content errors. I'm not entirely happy with my solution for that yet, so I'm not going to go into that right now.

What I do want to talk about is actually forcing the user over to HTTPS once your site is SSL-ready. The code I had used for many years on classic ASP sites was not working reliably; it would redirect, but to the home page instead of the page you tried to access.

ASP.NET has beefed up the Request object with some additional information we didn't have back in the old days (Request.IsSecureConnection and Request.Url). We also have a new way of including our function libraries, the App_Code folder.

  1. NameSpace myApplication
  2.     Public Class myLibrary
  3.         Public Shared Sub ForceSSL()
  4.             If Not System.Web.HttpContext.Current.Request.IsSecureConnection Then
  5.                 System.Web.HttpContext.Current.Response.Redirect(System.Web.HttpContext.Current.Request.Url.AbsoluteUri.Replace("http://", "https://"))
  6.             End If
  7.         End Sub
  8.     End Class
  9. End NameSpace


Then from each page on the site I import my custom library and run the subroutine:

  1. Imports myApplication.myLibrary
  2. Sub Page_Load(sender as Object, e as EventArgs)
  3.     ForceSSL()
  4.     'Rest of code goes here...
  5. End Sub


Supposedly there's a way to do this without code by modifying some settings in IIS, but I didn't have any success with that. The particular situation I'm dealing with has some fairly severe cohesion/coupling issues with certain pages being accessed from different subdomains.

Download the VB.NET source code for ForceSSL from Snipplr.com

Tuesday, May 10, 2011

Temperature Conversion Revisited

I recently completed another ASP.NET project and learned about the app_code folder, which lets you share code between pages. It's slightly similar to the #include directive in Classic ASP, but a lot more structured. I couldn't find much good documentation on it, so most of what I've learned has been through experimentation.

Any files placed into the app_code folder are automatically available from any page in your web application, so you can write: myNamespace.myClass.myFunction(someVariable) and the magic will happen. Alternatively, you can add Imports myNamespace.myClass in the top of your page and call myFunction without all the prefixing.

Though I'm still not a big fan of ASP.NET, this app_code thing is pretty slick and it's inspired me to write more reusable code. I had previously written a collection of functions for temperature conversion, which I've since grown unhappy with after reading Code Complete 2.0 by Steve McConnell because I named my functions like this: CelsiusToFahrenheit, instead of like this: FahrenheitFromCelsius.

I had been planning to write more conversion functions for various units of measurement, but was never satisfied enough with how it was turning out to make it publically available. Besides learning to appreciate ASP.NET, I'm also learning to appreciate object-oriented programming. This whole namespace/class combination with app_code seems to be appropriately suited for doing unit of measurement conversion.

So here's my plan. I've defined a namespace called UOM, which is the manufacturing industry standard abbreviation for unit of measure. Each type of measurement will be a class: temperature, distance, volume, mass, etc. Each class will accept string input so that you can specify both the numeric quantity and the unit of measure simultaneously. The class will use the specified unit of measure to convert values to a common unit of measure, and be able to return values in any equivalent unit of measure.

VB.NET

  1. Imports UOM
  2. Dim outside as Temperature
  3. outside = New Temperature("10 C")
  4. Response.Write(outside.getFahrenheit) 'Displays "50"

And so I proudly announce the release of the first installment in the UOM namespace: UOM.Temperature, which handles Celsius, Delisle, Fahrenheit, Kelvin, Newton, Rankine, Réaumur, and Rømer temperature scales.

Download the VB.NET source code for UOM.Temperature from Snipplr.com

Tuesday, September 14, 2010

Scalable Site Structure

If you've been developing web sites for a few years, you've probably developed a lot of sites with a structure similar to the following:

  • /css/
  • /images/
  • /scripts/
  • index.php
  • about.php
  • products.php

One of my colleagues likes to keep the root directory as clean as possible, so his site structures look like this:

  • /content/about.php
  • /content/products.php
  • /common/
  • /images/
  • index.php

Which I find slightly irritating because I prefer to keep my CSS separate from my JavaScript, and the /content/ folder is just taking the mess from one place and moving it to another.

Of course, the Holy Grail of site structures is to have clean URLs like this:

  • /about/
  • /css/
  • /images/
  • /products/
  • /scripts/
  • index.php

If you have a dynamic site, this is easily achieved using mod_rewrite, so /about/ becomes index.php?page=about, but sometimes the client doesn't want to pay for a dynamic site. In that case we use the Poor Man's Clean URLs™ wherein we actually create the /about/ and /products/ folders and put an index.php file in each with the static content. The PHP is then only really used to include a header, footer, navigation, etc.

As a site grows, performance and scalability become a (greater) concern. Popular site speed analysis tools often recommend using a content distribution network. Whether you outsource this or try to do it in-house, it sounds like a lot of work.

I hadn't had the privilege of working on a site that experienced enough traffic to be concerned about scalability until earlier this year. The company was running a national television commercial. They had hired another company to develop a micro-site on a separate server for the campaign, but viewers were being directed to the regular corporate site where a gigantic ad enticed them to visit the micro-site. If you're shaking your head right now, me too. The corporate web server was going down almost daily.

The web statistics package indicated that the most downloaded files were the product videos. I still don't know why they aren't using YouTube or Vimeo for this purpose. On a hunch, I copied the video files to the micro-site's web server and changed all the links. They experienced zero downtime after that.

This experience made me think about how to build scalability into a site from the beginning. Something in the back of my mind twigged a memory of reading something about serving static content from a cookieless domain. Cookies aside, it turns out that serving content from multiple subdomains has certain performance advantages.

Any discussion of clean URLs would not be complete without mentioning www.example.com vs. example.com. Dropping the www seems to be the "cool" thing to do, but my personal feeling is that www.example.com is more descriptive of what you will find there. The mail server is mail.example.com, so the web server should be www.example.com.

So here's the site structure I'm planning to use for future projects:

  • /css/ - css.example.com
  • /images/ - images.example.com
  • /images/logos/ - images.example.com/logos/
  • /images/products/ - images.example.com/products/
  • /js/ - js.example.com
  • /www/ - www.example.com
  • /www/about/ - www.example.com/about/
  • /www/products/ - www.example.com/products/
  • /www/index.php - the actual home page
  • /index.php - file which redirects to www.example.com

The subdomains can be hosted from the primary web server while traffic is small, and migrated to dedicated servers when the traffic increases. The URLs to the images, scripts, and stylesheets don't change, so the PHP files don't need to be updated. Use plenty of folders to keep things organized. You can see I made folders at images.example.com to keep the logos separate from product pictures. If there will be a lot of pictures for each product, I would make folders within the products folder to keep them segregated.

Sunday, September 5, 2010

Roman Numerals, Part 4

Keith Alexander of Albuquerque, New Mexico writes:
“I like your Roman Numeral library. I needed a function to test for Roman numerals, so I wrote this one.
  1. // Check to see if the string is a Roman Numeral
  2. // NOTE: this doesn't check for fractions, overbars, the Bede "N" (zero) etc.
  3. // NOTE: It also doesn't check for a well-formed Roman Numeral.
  4. function is_roman_numeral( $roman )
  5. {
  6.     // Strip every non-word character
  7.     // - A-Z, 0-9, apostrophe and understcore are what's left
  8.     $roman = preg_replace( "/[^A-Z0-9_']/iu", "", $roman );
  9.     // if it contains anything other than MDCLXVI, then it's not a Roman Numeral
  10.     $result = preg_match( "/[^MDCLXVI]/u", $roman );
  11.     if( $result )
  12.     {
  13.         return FALSE;
  14.     }
  15.     return TRUE;
  16. }

Who knows if blogger is going to show it properly. If not, just contact me and I'll send it you in email or something. Anyway, it's something I wrote in 5 minutes. If you want to add it to your library, modified or otherwise, please feel free.”

Thanks for writing in Keith, and sorry for the late response. There are two ways to validate a Roman number, using regular expressions like you did, and converting back to an Arabic number (if the conversion fails, it's not a Roman number).

I'm not sure about using a regular expression to remove non-word characters. My gut tells me that anything containing such characters should fail validation as a Roman number. Also, I would reverse the match and eliminate the if statement by directly returning the result of the match.

PHP

  1. function isRoman($roman)
  2. {
  3.     return preg_match("/[MDCLXVI]/u", $roman);
  4. }

ASP

  1. function isRoman(roman)
  2.     dim regEx
  3.     set regEx = new RegExp
  4.     with regEx
  5.         .IgnoreCase = true
  6.         .Global = true
  7.         .Pattern = "[MDCLXVI]"
  8.     end with
  9.     if regEx.Test(roman) then
  10.         isRoman = true
  11.     else
  12.         isRoman = false
  13.     end if
  14.     set regEx = nothing
  15. end function


Calculating annual salary for employees paid hourly

Last time I explained how to calculate the annual salary for an employee who is paid by the hour. Today I'm going to show you by writing a function for it, and we'll write one for monthly salary too.

  1. function annualSalary(someYear, hourlyPay)
  2.     const hoursPerDay = 8
  3.     annualSalary = 0
  4.     for i = 1 to 12
  5.         annualSalary = annualSalary + WorkingDays(someYear, i) * hoursPerDay * hourlyPay
  6.     next
  7. end function
  8. function monthlySalary(someYear, someMonth, hourlyPay)
  9.     const hoursPerDay = 8
  10.     monthlySalary = 0
  11.     monthlySalary = monthlySalary + WorkingDays(someYear, someMonth) * hoursPerDay * hourlyPay
  12. end function


View ASP implementation on Snipplr

Wednesday, September 1, 2010

Number of working days in a month

I didn't realize it's been over a year since my last post. Most of the code I've written this past year has been too specialized to be considered useful to anyone outside of my present employer, but this week I was presented with a problem with our Intranet application where the annual salary of employees who are paid hourly was not being calculated correctly. I'm working with the application vendor to get this corrected in the next version, and learned some interesting things about how to calculate this correctly.

A common formula to calculate annual salary for hourly employees is hourly pay × 40 hours/week × 52 weeks/year. This is very close to accurate, but 365/366 days per year does not divide evenly by 7 days/week.

A more accurate result can be obtained by multiplying the hourly pay by the number of working hours in a year. One way to calculate this is based on the last day of the year. In a non-leap year, there are 2080 working hours if the last day of the year is a Saturday or Sunday, and 2088 working hours if the last day of the year is a weekday. In the case of a leap year, there are 2080 working hours if the last day of the year is a Sunday, 2088 working hours if the last day of the year is a Saturday or Monday, and 2096 working hours for any other weekday.

At our company, we work 8 hours each day, so we can calculate the number of working days rather than the number of working hours and multiply by 8 hours/day. Since the number of days per month is variable, I decided to calculate working days per month separately and sum them up for working days per year.


  1. Function WorkingDays(someYear, someMonth)
  2.     WorkingDays = 0
  3.     For i = 1 To MonthDays(someYear, someMonth)
  4.         If Weekday(DateSerial(someYear, someMonth, i)) <> 1 And Weekday(DateSerial(someYear, someMonth, i)) <> 7 Then
  5.             WorkingDays = WorkingDays + 1
  6.         End If
  7.     Next
  8. End Function

You can take the result and multiply by 8 hours/day to get the number of working hours in a month, and then multiply by the hourly pay to get the monthly salary. The annual working days can be obtained by calling the function in a for i = 1 to 12...next loop, multiplied by 8 hours/day to get the number of working hours in a year, and then multiplied by the hourly pay to get the annual salary.

We ignore holidays and vacation days since the employee is paid for those as well, though technically vacation pay is accured at 4% so you could subtract vacation days (probably either 10 or 15, depending on number of years of service) and multiply by 1.04 to get an even more accurate amount for annual salary.


View ASP implementation on Snipplr.

Saturday, June 6, 2009

Publishing schedule change

As I mentioned last week, I ran out of code samples that are suitable for publishing. All that remains is code in various states of partial completion. I was trying to avoid this, but have decided I need to change the publishing schedule of this blog from weekly to whenever I have something finished. I have some fairly large projects on the go right now that I need to focus on if I hope to complete them in my own lifetime. I'm not even sure that anyone is ever reading this besides myself as I type it, so I don't expect anyone will be upset by this.