26 November 2007

String extension methods

C# 3.0 features the new extension methods. This basically seems to allow you to add your own methods to sealed classes. That seems a bit weird, and Microsoft gives the following advice in the Visual Studio 2008 help
In general, we recommend that you implement extension methods sparingly and only when you have to. Whenever possible, client code that must extend an existing type should do so by creating a new type derived from the existing type.
Full article Then try to make a List<> and bring up the Intellisense box. You will see 40-50 new methods, and by now you will guess they are all extension methods. Practiced what thy preached, Microsoft! Anyway, if Microsoft makes a lot of extension methods, so can we. We can now finally get rid of our 'utility' classes and make something like this:
namespace LocalJoost.Utilities
{
    public static class StringExtensions
    {
        public static string Reverse(this string s)
        {
            StringBuilder builder = new StringBuilder();
            for (int i = s.Length - 1; i >= 0; i--)
            {
                builder.Append(s.Substring(i, 1));
            }
            return builder.ToString();
        }
    }
}
And then we can do something like this:
string test = "Hello hello";
string reversed = test.Reverse();
The string "reversed" will now contain the string "olleh olleH". Notice the "this" keyword before the string s in method Reverse of class StringExtensions - that is the thing that defines Reverse as an extension method of string. The only further requirements are that both class and method implementing the extension are static.

19 November 2007

Creating a simple proxy

Sometimes you want to give web browsers access to web resources that are best not opened indiscriminatly to the world. A good example are WMS servers: you want the users to be able to see maps, but putting a WMS server directly online is like putting a read-only sql client online. The solution is put the web resource on a server that is not accessible by the internet, but is accessible by the web application - i.e. the limited resource is put inside the DMZ, and create some kind of proxy page that streams data from the server inside the DMZ to the outside world - after doing some validations, of course. A setup for a proxy page (that only does the streaming, not the validation) may be as follows: Create an aspx page MyProxyPage.aspx and add the following code:
void ProcessProxyRequest( string request, HttpResponse response)
 {
   HttpWebRequest webRequest = HttpWebRequest.Create(request) as HttpWebRequest;
 
   // Important! Keeps the request from blocking after the first time!
   webRequest.KeepAlive = false;
   webRequest.Credentials = CredentialCache.DefaultCredentials;
   using (HttpWebResponse backendResponse = (HttpWebResponse)webRequest.GetResponse())
   {
     Stream receiveStream = backendResponse.GetResponseStream();
 
     // Clear whatever is already sent
     response.Clear();
     response.ClearContent();
     response.ClearHeaders();
 
     // Copy headers
     // Check if header contains a contenth-lenght since IE
     // goes bananas if this is missing
     bool contentLenghtFound = false;
     foreach (string header in backendResponse.Headers)
     {
       if (string.Compare(header, "CONTENT-LENGTH", true) == 0) 
       { 
         contentLenghtFound = true;
       }
       response.AppendHeader(header, backendResponse.Headers[header]);
     }
     // Copy content           
     byte[] buff = new byte[1024];
     long length = 0;
     int bytes;
     while ((bytes = receiveStream.Read(buff, 0, 1024)) > 0)
     {
       length += bytes;
       response.OutputStream.Write(buff, 0, bytes);
     }
 
     // Manually add content-lenght header to satisfy IE
     if (!contentLenghtFound) 
     {
       response.AppendHeader("Content-Length", 
         length.ToString());
     }
 
     receiveStream.Close();
     backendResponse.Close();
     response.Flush();
     response.Close();
   }
 }
Now of course, in the Page_Load you have to do something first:
protected void Page_Load(object sender, EventArgs e)
{
   string requestInsideDMZ = "http://somewhereinyourdmz/somepage.aspx";
   ProcessProxyRequest( requestInsideDMZ, 
                        Response);
}
If the user accesses MyProxyPage.aspx he gets the contents of http://somewhereinyourdmz/somepage.aspx instead.

02 November 2007

Expanding the length of JSON data returned from ASP.NET web services

A very small example this time, but one that can be very time consuming to find out. The basic example I described earlier works fine, but just try to send back a larger string. Suppose, you change the Hello World Method into this:
[WebMethod]
public string HelloWorld( string ToSomeone)
{
    StringBuilder b = new StringBuilder();
    while (b.Length < 50000)
    {
       b.AppendFormat("Hello World {0}. ", ToSomeone);
    }

    return b.ToString();
}
You get al LOT of "Hello Worlds". Now change 50000 into 200000... you get no error message, but in fact, you get no response at all. To solve this, you need to delve into your web.config. Make sure the JSON configuration declaration at the top looks like this. I highlighted the important part:
<configSections>
  <sectionGroup name="system.web.extensions" type="System.Web.Configuration.SystemWebExtensionsSectionGroup, System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35">
    <sectionGroup name="scripting" type="System.Web.Configuration.ScriptingSectionGroup, System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35">
        <section name="scriptResourceHandler" type="System.Web.Configuration.ScriptingScriptResourceHandlerSection, System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" requirePermission="false" allowDefinition="MachineToApplication"/>
      <sectionGroup name="webServices" type="System.Web.Configuration.ScriptingWebServicesSectionGroup, System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35">
        <section name="jsonSerialization" type="System.Web.Configuration.ScriptingJsonSerializationSection, System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" requirePermission="false" allowDefinition="Everywhere" />
        <section name="profileService" type="System.Web.Configuration.ScriptingProfileServiceSection, System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" requirePermission="false" allowDefinition="MachineToApplication" />
        <section name="authenticationService" type="System.Web.Configuration.ScriptingAuthenticationServiceSection, System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" requirePermission="false" allowDefinition="MachineToApplication" />
      </sectionGroup>
    </sectionGroup>
  </sectionGroup>
</configSections>
This may look a bit intimidating, but just copy it. In the system.web part you can define the following tag:
<system.web.extensions>
  <scripting>
    <webServices>
      <jsonSerialization maxJsonLength="999999999"/>
    </webServices>
  </scripting>
</system.web.extensions>
And there you go. The number 999999999 is the absolute max value - so if you send stuff to your client longer than that, you have to think of something else. But for the mapping applications I made on Google Maps this works fine. Bear in mind that sending VERY large portions of data can degrade your user experience considerably, although the asynchronous nature of the ASP.NET JSON service might diminish that problem. By the way, in the latest templates for ASP.NET Ajax enabled web sites the whole configuration declaration is already included, as well as the declaration for the json serialization - but it is commented out. It looks something like this.
<!-- Uncomment this line to customize maxJsonLength and add a custom converter -->
<!--
<jsonSerialization maxJsonLength="500">
  <converters>
    <add name="ConvertMe" type="Acme.SubAcme.ConvertMeTypeConverter"/>
  </converters>
</jsonSerialization>
-->
You have to delete the part that I printed in italics, because that is just an example that does not point to an actual class