Sunday, March 11, 2012

UpdatePanel and URL Rewriting

Are you using an HttpHandler? Can you post the code?

Happy Turkey Day


Yes I am, here is the various components

It starts here

Imports

Microsoft.VisualBasic

Imports

System.Web

Namespace

RegExUrlMapping_HTTPModulePublicClassRegExUrlMappingBaseModuleImplementsSystem.Web.IHttpModuleSubInit(ByValappAsHttpApplication)ImplementsIHttpModule.InitAddHandlerapp.AuthorizeRequest,AddressOfMe.BaseModuleRewriter_AuthorizeRequestEndSubSubDispose()ImplementsSystem.Web.IHttpModule.DisposeEndSubSubBaseModuleRewriter_AuthorizeRequest(ByValsenderAsObject,ByValeAsEventArgs)DimappAsHttpApplication =CType(sender,HttpApplication)Rewrite(app.Request.Path,app)EndSubOverridableSubRewrite(ByValrequestedPathAsString,ByValappAsHttpApplication)EndSubEndClass

End

Namespace

Continues here

Imports

Microsoft.VisualBasic

Imports

System.Configuration

Imports

System.Text.RegularExpressions

Imports

System.Xml

Namespace

RegExUrlMapping_HTTPModulePublicClassRegExUrlMappingConfigHandlerImplementsIConfigurationSectionHandlerDim_SectionAsXmlNodePublicFunctionCreate(ByValparentAsObject,ByValconfigContextAsObject,ByValsectionAsSystem.Xml.XmlNode)AsObjectImplementsSystem.Configuration.IConfigurationSectionHandler.Create_Section =sectionReturnMeEndFunction''' Get whether url mapping is enabled in the app.configFriendFunctionEnabled()AsBooleanIf_Section.Attributes("enabled").Value.ToLower = "true"ThenReturnTrueElseReturnFalseEndIfEndFunction''' Get the matching "mapped Url" from the web.config file if there is one.FriendFunctionMappedUrl(ByValurlAsString)AsStringDimxAsXmlNodeDimoRegAsRegexForEachxIn_Section.ChildNodesoReg =NewRegex(x.Attributes("url").Value.ToLower)IfoReg.Match(url).SuccessThenReturnoReg.Replace(url,x.Attributes("mappedUrl").Value.ToLower)EndIfNextReturn ""EndFunctionEndClass

End

Namespace

and here

Imports

Microsoft.VisualBasic

Imports

System.Web

Imports

System.Configuration

Namespace

RegExUrlMapping_HTTPModulePublicClassRegExUrlMappingModuleInheritsRegExUrlMappingBaseModuleOverridesSubRewrite(ByValrequestedPathAsString,ByValappAsHttpApplication)''Implement functionality here that mimics the 'URL Mapping' features of ASP.NET 2.0DimconfigAsRegExUrlMappingConfigHandler =CType(ConfigurationManager.GetSection("system.web/RegExUrlMapping"),RegExUrlMappingConfigHandler)DimpathOldAsString,pathNewAsString = ""Ifconfig.EnabledThenpathOld =app.Request.RawUrl''Get the request page without the querystring parametersDimrequestedPageAsString =app.Request.RawUrl.ToLowerIfrequestedPage.IndexOf("?") > -1ThenrequestedPage =requestedPage.Substring(0,requestedPage.IndexOf("?"))EndIf''Format the requested page (url) to have a ~ instead of the virtual path of the appDimappVirtualPathAsString =app.Request.ApplicationPathIfrequestedPage.Length >=appVirtualPath.LengthThenIfrequestedPage.Substring(0,appVirtualPath.Length).ToLower =appVirtualPath.ToLowerThenrequestedPage =requestedPage.Substring(appVirtualPath.Length)IfrequestedPage.Substring(0, 1) = "/"ThenrequestedPage = "~" &requestedPageElserequestedPage = "~/" &requestedPageEndIfEndIfEndIf''Get the new path to rewrite the url to if it meets one''of the defined virtual urls.pathNew =config.MappedUrl(requestedPage)''If the requested url matches one of the virtual one''the lets go and rewrite it.IfpathNew.Length > 0ThenIfpathNew.IndexOf("?") > -1Then''The matched page has a querystring definedIfpathOld.IndexOf("?") > -1ThenpathNew += "&" &Right(pathOld,pathOld.Length -pathOld.IndexOf("?") - 1) & "&OriginalUrl=" &System.Web.HttpContext.Current.Server.UrlEncode(pathOld)ElsepathNew += "&OriginalUrl=" &System.Web.HttpContext.Current.Server.UrlEncode(pathOld)EndIfElse''The matched page doesn't have a querystring definedIfpathOld.IndexOf("?") > -1ThenpathNew +=Right(pathOld,pathOld.Length -pathOld.IndexOf("?")) & "&OriginalUrl=" &System.Web.HttpContext.Current.Server.UrlEncode(pathOld)ElsepathNew +=Right(pathOld,pathOld.Length -pathOld.IndexOf("?")) & "?OriginalUrl=" &System.Web.HttpContext.Current.Server.UrlEncode(pathOld)EndIfEndIf''Rewrite to the new urlHttpContext.Current.RewritePath(pathNew)EndIfEndIfEndSubEndClass

End

Namespace

Also, I'm using this code to rewrite the form tag

Imports

System.IO

Imports

System.Web.UI

Public

ClassFormFixerHtmlTextWriterInheritsHtmlTextWriterPrivateinFormAsBoolean =FalsePrivate_actionAsStringPublicSubNew(ByValwAsTextWriter,ByValaAsString)MyBase.New(w)_action =aEndSub'NewPublicOverridesSubRenderBeginTag(ByValtagNameAsString)inForm =[String].Compare(tagName, "form") = 0MyBase.RenderBeginTag(tagName)EndSub'RenderBeginTagPublicOverridesSubWriteAttribute(ByValnameAsString,ByValvalueAsString,ByValfEncodeAsBoolean)If[String].Compare(name, "action",True) = 0Thenvalue =_actionEndIfMyBase.WriteAttribute(name,value,fEncode)EndSub'WriteAttribute

End

Class'FormFixerHtmlTextWriterFinally, I'm using this code to rerender the form tag in a base class all of my pages inherit from

Imports

System.Web.UI

Imports

System.Web

Public

ClassbasepageInheritsSystem.Web.UI.Page

ProtectedOverridesSubRender(ByValwriterAsHtmlTextWriter)DimactionAsString =CStr(HttpContext.Current.Request.QueryString("OriginalUrl"))IfNot (actionIsNothing)Thenwriter =NewFormFixerHtmlTextWriter(writer,action)EndIfMyBase.Render(writer)EndSub'Render

End

Class

My last post was potentially a long post of gibberish. Also, back when I appropriated this code off of someone's blog post a long time ago (thanks whoever you are!), I had modified it to use a querystring originalURL when apparantly all I had to do was use request.rawurl. When was that added? Suffice it to say, its a hodgepodge of code I've been adding to/changing for quite a while..

So, to make this a bit more straightforward I'm going to try and point out the highlights for my newly updated code (very similar to the above just edited to use the rawurl rather than the originalurl querystring).

I define a regular expression in my web.config, like so

<

addurl="~/mng(.*)/(.*)/(.*).aspx"mappedUrl="~/manage$1/$3.aspx?pid=$2"/>

The code compares all URLs to the regular expressions defined in the web.config, using the config.mappedurl function.

Friend Function MappedUrl(ByVal urlAs String)As String Dim xAs XmlNodeDim oRegAs RegexFor Each xIn _Section.ChildNodes oReg =New Regex(x.Attributes("url").Value.ToLower)If oReg.Match(url).SuccessThen Return oReg.Replace(url, x.Attributes("mappedUrl").Value.ToLower)End If Next Return""End Function

If it finds it to be a match, it rewrites the URL using this code.

If pathNew.IndexOf("?") > -1Then''The matched page has a querystring definedIf pathOld.IndexOf("?") > -1Then pathNew +="&" & Right(pathOld, pathOld.Length - pathOld.IndexOf("?") - 1)'& "&OriginalUrl=" & System.Web.HttpContext.Current.Server.UrlEncode(pathOld)Else' pathNew += "&OriginalUrl=" & System.Web.HttpContext.Current.Server.UrlEncode(pathOld)End If Else''The matched page doesn't have a querystring definedIf pathOld.IndexOf("?") > -1Then pathNew += Right(pathOld, pathOld.Length - pathOld.IndexOf("?"))'& "&OriginalUrl=" & System.Web.HttpContext.Current.Server.UrlEncode(pathOld)Else pathNew += Right(pathOld, pathOld.Length - pathOld.IndexOf("?"))'& "?OriginalUrl=" & System.Web.HttpContext.Current.Server.UrlEncode(pathOld)End If End If
In the render event of my page class, I have this code.

Protected Overrides Sub Render(ByVal writerAs HtmlTextWriter) writer =New FormFixerHtmlTextWriter(writer, Request.RawUrl)MyBase.Render(writer)End Sub

The code for the formfixerhtml class is here

Public Class FormFixerHtmlTextWriterInherits HtmlTextWriterPrivate inFormAs Boolean =False Private _actionAs String Public Sub New(ByVal wAs TextWriter,ByVal aAs String)MyBase.New(w) _action = aEnd Sub'NewPublic Overrides Sub RenderBeginTag(ByVal tagNameAs String) inForm = [String].Compare(tagName,"form") = 0MyBase.RenderBeginTag(tagName)End Sub'RenderBeginTagPublic Overrides Sub WriteAttribute(ByVal nameAs String,ByVal valueAs String,ByVal fEncodeAs Boolean)If [String].Compare(name,"action",True) = 0Then value = _actionEnd If MyBase.WriteAttribute(name, value, fEncode)End Sub'WriteAttributeEnd Class'FormFixerHtmlTextWriter

I think thats it. Something I've noticed: If I have a page whose button is NOT in an ajaxpanel, say a "Save Changes" button on the same page as one with an ajaxpanel somewhere else, the button will operate normally until something inside the ajaxpanel changes.

Example.

I go to an Edit Page, I move my mouse over the save changes button. the URL in the status bar says: /mngtourpages/1/default.aspx. I can hit save an unlimited number of times and its fine. On that same edit page, I have a repeater where each time you hit a linkbutton called "Add New Item" it increases a collection being bound to a repeater then rebinds the repeater. I hit the "Add New Item" linkbutton, it works, I move my mouse over the Save Changes button, now it says "default.aspx?pid=1" Each time I interact with anything inside the ajaxpanel, it will increment the pid=1 by 1, for example, default.asp?pid=1&pid=1, default.aspx?pid=1&pid=1&pid=1, and so on, eventually overloading my integer property and crashing the page.

All pages work fine without an updatepanel other ajax tools work fine (to my knowledge, seeing as I only noticed this oddity after beta 2). I'm not really sure how my handler could be at fault, unless somehow ajax generates its own URLs without a .aspx string then tries to translate them back to normal urls? In which case perhaps I can somehow intercept that request and do the same data manipulation I'm doing to the .aspx? Maybe as simple as adding another regular expression with the alternate file extension?

I appreciate anyone who can help with this, hope everyone is having a good holiday weekend! (especially those who get it off unlike me. :()


Ok, 3rd post in a row, but I'm impatient and kept working at it. I finally found someone else with an identical issue here:http://forums.asp.net/thread/1437627.aspx

So I rewrote his C# code in vb.net, merged it into my HTTPModule, and voila! Fixed! I had to keep playing with the pages for a while just to make sure it was really gone because I couldn't believe it would be so easy, but it was!

As an aside, rather than whatever complicated way he wrote of storing the original URL, I just used request.rawurl and it worked beautifully. Is there some sort of negative to using rawurl in this situation?

Is that solution burdensome on the server?

Thanks


HttpModules are awesome!

No comments:

Post a Comment