Simon Green's Developer Blog
Developing .NET in the cold white north ...

"Bad Request" error when using friendly URLs with special characters in ASP.NET

Sunday, 7 May 2006 16:45 by Simon

Our ASPRedirector.NET product can be used to re-write query-string URLs to friendly URLs with ID parameters converted to text. This is typically done for Search Engine Optimisation to improve page ranking because words appearing in the URL are taken into account by search engines. We have developed re-writing configuration rules for many eCommerce / shopping-cart software packages such as BV Software's BVCommerce and MediaChase's eFC.

However, if the category and / or product name contain certain reserved characters then you can run into a problem. Even if the characters such as $. %, ? are encoded correctly the page may not be returned and all you see is "bad request" (error 400). It isn't URLScan that is causing this though but the behavior of the ASP.NET ISAPI filter which doesn't pass the request on.

There is a Microsoft KB Article 826437 about the issue

The solution given is incorrect though and the correct registry key entry is shown below:

Windows Registry Editor Version 5.00
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\ASP.NET]
  "VerificationCompatibility"=dword:00000001

This fixes the issue and allows you to have nice, optimised URLs on your eCommerce website - regardless of the characters in you category and product names.

 


Tags:   , ,
Categories:   .NET
Actions:   E-mail | del.icio.us | Permalink | Comments (0) | Comment RSSRSS comment feed

Windows 2003 SP1 .NET Encoding Issue

Tuesday, 11 October 2005 16:31 by Simon

We recently came across an issue with one of our components (ASPRedirector.NET) where a customer who had installed Windows 2003 SP1 started getting errors on their website. The errors only happened if they used a certain encoding in the globalisation settings:


<globalization requestEncoding="iso-8859-15" responseEncoding="iso-8859-15"/>

The error being thrown was:

NotImplementedException: The method or operation is not implemented.
System.Web.CodePageNoBestFitEncoding.GetCharCount(Byte[] bytes, Int32 index, Int32 count)

If another encoding was used then everything worked fine (but they wanted to use this particular encoding)

Our component needed to use the encoder / decoder objects returned from Response.ContentEncoding in order to decode the bytes passed into a filter to look at the page content and encode them again so that any changes were output in the chosen encoding.

Creating a simple filter highlighted the problem:

public class Filter : Stream
 {
  private Encoder encoder;
  private Decoder decoder;
  private Stream baseStream;

  public Filter(HttpContext context)
  {
   Encoding encoding = context.Response.ContentEncoding;
   this.encoder = encoding.GetEncoder();
   this.decoder = encoding.GetDecoder();
   this.baseStream = context.Response.Filter;
  }

  public override bool CanRead
  {
   get { return false; }
  }

  public override bool CanWrite
  {
   get { return true; }
  }

  public override bool CanSeek
  {
   get { return false; }
  }

  public override void Close()
  {
   baseStream.Close();
  }

  public override void Flush()
  {
   baseStream.Flush();
  }

  public override long Length
  {
   get { throw new NotSupportedException(); }
  }

  public override long Seek(long offset, SeekOrigin origin)
  {
   throw new NotSupportedException();
  }

  public override void SetLength(long value)
  {
   throw new NotSupportedException();
  }

  public override long Position
  {
   get { throw new NotSupportedException(); }
   set { throw new NotSupportedException(); }
  }

  public override int Read(byte[] buffer, int offset, int count)
  {
   throw new NotSupportedException();
  }

  public override void Write(byte[] buffer, int offset, int count)
  {
   // get number of characters that the byte array will be decoded into
   int charCount = this.decoder.GetCharCount(buffer, offset, count);
   // create character array to hold them
   char[] chars = new char[charCount];
   // decode byte array into characters
   int charCountDecoded = decoder.GetChars(buffer, offset, count, chars, 0);

   // we now have characters and can process them
   // converting to a string should be the readable content
   string content = new string(chars);

   // get number of bytes that the character array will be encoded into
   int byteCount = encoder.GetByteCount(chars, 0, charCountDecoded, true);
   // create byte array to hold them
   byte[] bytes = new byte[byteCount];
   // encode character array into bytes
   int byteCountEncoded = encoder.GetBytes(chars, 0, charCountDecoded, bytes, 0, true);

   // write bytes to output stream - this should be exactly what came in
   baseStream.Write(bytes, 0, byteCountEncoded);
  }
 }

This fails if the responseEncoding is set to "iso-8859-15" after Windows 2003 SP1 has been applied but works before SP1 and if a different encoding is used (the default "utf-8" for instance).

So what's the problem? I found a lone post in a newsgroup that explained it:

SP1 of Windows Server introduced a new "feature" in the GlobalizationConfig class called EnableBestFitResponseEncoding.

Effect is that when you set anything other than UTF8 in web.config, e.g. <globalization requestEncoding="iso-8859-1" responseEncoding="iso-8859-1" />, many functions will break (including HttpUtility.URLDecode) with a MethodNotImplementedExecption in the new internal class CodePageNoBestFitEncoding.

Analyzing with the reflector reveals, that new code was added to the HttpResponse.ContentEncoding property:

                  if (!this._encoding.Equals(Encoding.UTF8))
                  {
                        string text1 = this._encoding.GetType().FullName;
                        if ((config1 == null) || !config1.EnableBestFitResponseEncoding)
                        {
                              if (text1 == "System.Text.MLangCodePageEncoding")
                              {
                                    this._encoding = Encoding.GetEncoding("mlang");
                              }
                              else if ((text1 == "System.Text.CodePageEncoding") || (text1 == "System.Text.Latin1Encoding"))
                              {
                                    int num1 = this._encoding.CodePage;
                                    this._encoding = new CodePageNoBestFitEncoding(num1);
                              }
                        }

Assuming that EnableBestFitResponseEncoding is false by default, this property will return a new instance of CodePageNoBestFitEncoding when you set a responseEncoding="iso-8859-1" in web.config.

Many of the normal Encoding methods in CodePageNoBestFitEncoding will throw the MethodNotImplemented exception which explains the above mentioned bahavior.

Some additional analysis reveals that there is a new attribute for <globalization > called "enableBestFitResponseEncoding". After setting this attribute to "true", everthing works again as normal with a responseEncoding other than UTF8.

A search in Microsoft's site for "enableBestFitResponseEncoding" returns 0 hits. Thank you Microsoft!

 


Tags:   , ,
Categories:   .NET
Actions:   E-mail | del.icio.us | Permalink | Comments (0) | Comment RSSRSS comment feed