Pass Values Between Web Pages
Dino Espositoshows you how and when to use the Execute and Transfer methods.
October 30, 2009
CoreCoder
LANGUAGES: VB
TECHNOLOGIES:Redirection | Page Directives | Server.Execute | Passing Values
Pass Values Between Web Pages
Run external pages and passthem arguments.
By Dino Esposito
When developing Web applications, you often need to linkto an external page programmatically. In some cases, you can accomplish thiswith a little help from the user by kindly requesting he or she click on abutton to submit the contents of the current HTML form to an external page.Page redirection is another possibility. Normally, you redirect the user to adifferent page by using the Redirect method on the Response object. TheRedirect method requires a round trip to the client. Basically, the methodcauses the Web server to return an HTTP 302 status code to the browser thatindicates the new URL to request. As a result, you control the URL beingaccessed programmatically, but at the cost of an extra round trip. With Windows2000 Server and ASP 3.0, Microsoft enriched the programming interface of theServer object with a pair of new methods: Execute and Transfer.
The Execute method executes a request to another page,then continues the execution of the original page after the new page isprocessed. The Transfer method provides a similar functionality but with a keydifference: It passes the control of execution to the new page unconditionally.In other words, you use Execute if the external page is considered as a Sub ora Function in a piece of Visual Basic .NET code; you resort to Transfer if youneed a full page redirection. Note, however, that Transfer implements the pageredirection in a more effective way than Response.Redirect. In fact, theTransfer method doesn't post back to the browser but simply performs aserver-side redirection.
In the transition to ASP.NET, both methods have beenenhanced slightly to respond to user needs that have emerged in the meantime.In doing so, however, some gotchas have been introduced. In this article, I'llshow you what became of both methods and what the recommended model of passingvalues from one page to the next is in ASP.NET. But before going any further, Ishould clarify a couple of things about these methods. Server.Execute andServer.Transfer were essential techniques in ASP 3.0, but they are lessrelevant in ASP.NET, in which you often can employ other techniques to solvethe same issues. More often than not, you can devise the whole functionalityfrom a wider perspective and solve it by using custom server controls andclasses in what turns out to be a superior and more object-oriented approach.Yielding to external pages is still a valuable technique, especially if you'removing from ASP. In general, if you catch yourself using either Transfer orExecute too often, it's probably about time for you to rethink the functionality.
The Server.Execute Method
In ASP.NET, the functionality of ASP's intrinsic Serverobject is implemented by the HttpServerUtility class. An instance of this typeis created when the ASP.NET runtime begins to process the request, then it isstored as part of the request context. The class provides a bunch of helpermethods that are exposed publicly to modules and handlers through theHttpContext object's Server property. Modules and handlers include global.asax,Web pages, and Web services. In addition, to maintain ASP.NET coding as closelyas possible to the ASP programming style, several other commonly used ASP.NETobjects also expose their own Server properties. For example, both Page andHttpApplication show off Server properties that simply return that instance ofthe HttpServerUtility class.
Such an apparent redundancy of references has a subtle yetinvolved explanation. In ASP.NET, software entities - such as ASP's Server,Request, Response, and the like - are really more intrinsic properties thanthey are intrinsic objects. In ASP, they were instances of COM componentspublished to the ActiveX Scripting engine with easy-to-remember names such asServer, Request, and Response. To make them visible throughout all levels ofcode blocks, the objects were marked as global and thereby gained the label of"intrinsically available objects."
In ASP.NET, the architecture is radically different. Thespecific functionalities have been maintained and even enhanced, but they noware integrated in the context of a truly component-based model. The visibilityof an object in a certain scope depends on strict syntax rules. Wide-rangevisibility, such as that of intrinsic objects in ASP, can be obtained only byreplicating the objects in all scopes in which they are needed. Hence, theconcept of intrinsic properties.
Server.Execute does more or less the same in ASP.NET thatit did in ASP. As mentioned, the method passes the control to the specifiedpage for execution:
Server.Execute("/update.aspx")
The child page executes like a subroutine, and the controlis regained by the caller page as soon as the child request terminates. Anychange to the surrounding environment is detected, and the context of the childrequest is the same as the original request. This technique is not recommendedin 100 percent pure ASP.NET applications, in which using a middle-tiercomponent is highly preferable. If you're migrating from an ASP system,however, this technique might save you some time and help you ship version 1.0sooner. There's always a patch or service pack you can use to fix things up,right?
The big difference with ASP is all in the way in which youcan treat the output of the child request. In ASP.NET, the HTML outputgenerated by the child page can be flushed automatically in the parent-responsebuffer (as in ASP) or retained in a writer object. This second possibilityleads me to think of Server.Execute as a sort of Visual Basic .NET Functioncall.
When the execution flow reaches the Server.Execute call,the control is passed to the specified page and the execution of the currentpage is suspended. The response text the child execution generates is capturedand processed according to the particular overload of Execute that has beenused:
Public Sub Execute(url As String)
Public Sub Execute(url As String, _
writer AsTextWriter)
If a TextWriter object is specified, the response text ofthe child execution is accumulated into the writer and left at the disposal ofthe page. Otherwise, the response text is embedded in the response text of themain page automatically. For example, if you cache the output of the page, youcan parse it to extract specific information. The URL you pass to execute cancontain embedded parameters but must be part of the same domain. How can youpass values to the page that executes? As I mentioned, the query string iscertainly an option:
Dim writer As StringWriter = New StringWriter()
Server.Execute("/update.aspx?Batch=yes", writer)
Response.Write(writer.ToString())
Unlike what happens if you redirect using theResponse.Redirect method, the browser in no way is involved with this operation- everything happens on the server. In the end, the browser simply receives anHTTP 200 status code (that is, all is OK) for the request it made to theoriginal page.
The Server.Transfer Method
The Transfer method differs from Execute in that itterminates the current page after executing the specified one. The new pageruns as if it were the page requested originally. One problem worth noting withthe Server.Transfer method is that the URL in the browser is the URL of theoriginal page, not the page to which the user has been transferred. This couldresult in some confusion for the user if the technique is misused.
The Transfer method has two overloads:
Public Sub Transfer(url As String)
Public Sub Transfer(url As String, _
preserveForm AsBoolean)
The first overload redirects to the specified page; thesecond also clears or preserves the QueryString and Form collections, based onthe value of the Boolean argument. When you call the first overload, thecollections always are cleared unless the IsPostBack property on the originalpage is set to True.
The Execute and Transfer methods have similararchitectures. Both methods use the same internal procedure to execute thecall. When you call Transfer, the new page request is served immediately.Transfer can control programmatically whether the QueryString and Formcollections are to be preserved. The previous request is terminated, however,only when the internal call used to implement the redirect has returned. Allthe code that might follow the call to Transfer in the caller page neverexecutes.
Neither Transfer nor Execute causes a round trip to theclient. More importantly, to carry on the child request, they use the sameHttpApplication that was serving the original request. This makes the childrequest somewhat of a lightweight request because it limits the impact on theASP.NET infrastructure. But this same feature has a drawback that might be evenmore critical to your system's health. When you use either Transfer or Executeto redirect to a page the user is not authorized to view, the page is processedanyway. Instead of making another request from the server, which would forcereauthorization, both methods use a different handler within the sameHttpApplication to process the page. You gain direct access to the pageregardless of any security barrier placed in between. To avoid this flaw (whichprobably will be addressed in the next version of ASP.NET), you should checkthe role of the user before you yield the control. Dropping Execute andTransfer in favor of, say, Response.Redirect is another solution. (This problemalso has been addressed in article Q320976 of Microsoft's Knowledge Base: http://support.microsoft.com.)
Publish Values
If you're going to invoke an external page, chances areyou'll have to pass values, too. The query string, Session, or HttpContext ofthe calling page are all good places to store values the child page retrieves.An alternative approach is based on the fact that in ASP.NET, each running pageis an instance of a particular class. Subsequently, a call across pagesactually is resolved in a class-to-class call. How can the caller page passvalues down to the callee? You can expose the values through public propertieson the caller page and reference the assembly of the caller page into therun-time environment of the callee.
The first step is to define an alias for the class thatrepresents the caller page. The base class for running an ASP.NET page is Page,and all classes actually used Inherit, directly or indirectly, from that.Whatever the base class is, the real name of the class that represents the runningpage responds to a precise pattern: ASP.file_aspx, where "file" is the page'sfilename. From within the child page, the caller page is seen and managed as aninstance of the ASP.file_aspx class. An alias allows you to use a moreevocative name. You define a page's class alias by using the ClassNameattribute on the @Page directive:
<%@ Page ClassName="SearchPage" %>
Figure 1 illustrates a page - Search.aspx - that containsa public property and performs the search using an internal page named Do_Search.aspx.The output then is gathered and inserted in the existing user interface.
<%@ Page Language="VB"ClassName="SearchPage" %>
<%@ Import Namespace="System.IO" %>
Public ReadOnly Property TextToSearch() As String Get Return searchText.Text End GetEnd PropertyProtected Sub Search(sender As Object, e As EventArgs) Dim writer AsStringWriter = New StringWriter() Server.Execute("do_search.aspx", writer) Results.Text =writer.ToString()End Sub
Search for:
id="searchText" text="ASP.NET" />
Figure 1. The page exposes some values as publicproperties. A child page run through Execute or Transfer can retrieve themusing a direct, strong-typed binding.
Aside from the public property that exposes the string tosearch for, the ClassName attribute is the only special aspect of thesearch.aspx page. Note that the ClassName attribute doesn't change the name ofthe type being created for an instance of the page. It represents only anadditional piece of information for the ASP.NET runtime.
Retrieve Values
The ASP.NET model of passing arguments from page to pageis based on the assumption that the child page references the assembly thatcontains the caller page's class. A page's assembly is referenced using the@Reference directive. For example, you reference the assembly that definessearch.aspx using this code:
<%@ Reference Page="search.aspx" %>
The @Reference directive is an indirect way of linking thepage to the assembly that is dynamically generated for a requested ASPX page.The name of that assembly is random, not known in advance and not foreseeable.The @Reference directive links to the assembly for the specified page, whateverits run-time name is. After that, you can declare a global variable of the typeset in the ClassName alias:
Protected m_callerPage As SearchPage
If you didn't set the ClassName attribute, you must usethe real name of the class - in this case, ASP.search_aspx. The final step isassociating the variable name with a valid instance of the SearchPage class.Nicely enough, ASP.NET maintains a living instance of the caller page in theHandler property of the request context. You access the context of the currentrequest by using the Context property on the Page class:
m_callerPage = CType(Context.Handler, SearchPage)
At this point, you hold a valid instance of the class thatrepresents the caller. Everything that is declared public in the class isfreely accessible. Figure 2 shows the source code of the do_search.aspx pagethat retrieves some input text and performs the search.
<%@ Page Language="VB" %>
<%@ Reference Page="search.aspx" %>
Protected m_callerPage As SearchPageSub Page_Load(sender As Object, e As EventArgs) m_callerPage =CType(Context.Handler, SearchPage) Response.Write(GetResults(m_callerPage.TextToSearch)) End SubFunction GetResults(textToSearch As String) As String Response.Write("Results for '") Response.Write(textToSearch) Response.Write("'") Response.Write("...") End Function
Figure 2. The child page binds to the caller pageusing the Handler property on the HttpContext object. After that, it retrievesany values the parent class exposes publicly. For this technique to work,though, you must know about the caller.
Note that this method assumes the called page knows aboutthe type and programming interface of the caller. If you find this constrainttoo limiting, consider adding the arguments to the HttpContext object. Then,the callee simply could pull the value out of the HttpContext without knowing anythingabout which page put it there:
HttpContext.Items.Add("TextToSearch",searchText.Text)
Server.Execute("do_search.aspx")
Although less elegant, this solution is equally effectiveand allows the executed page to be called from anywhere. HttpContext is preferableto Session because the context is a buffer of memory with a limited lifetime,meaning it doesn't survive the current request.
When I first tested the code in this article, I wasworking with a beta of ASP.NET 1.1 already, and it worked great. Later, Itested it with ASP.NET 1.0, but a message popped up about the view state beinginvalid or corrupted. Making things more intricate and intriguing was the factthat the error seemed to affect Execute but not Transfer.
As I mentioned, both the main and the child pages are runby the same HttpApplication object because they were the same request. Whathappens under the hood is a sort of context switch. First, the internal methodused in both cases obtains an HTTP handler from the application factory andprepares to serve the new request. The original handler of the main request iscached (the Context.Handler property) and replaced with the new handler. Thespawned page inherits the context of the parent; when finished, anymodification made to Session or Application is immediately visible to theparent page.
The handler switch makes the whole operation extremelyfast because there's no need to create a new object to serve the request. Whenthe child page returns, the original handler is restored. The execution of themain page continues from the point at which it was stopped, but it uses thecontext inherited from the child page. This implementation speeds up executionbut might lead to the aforementioned security flaw if no countermeasures aretaken. Unlike Execute, the Transfer method doesn't preserve the contents of theQueryString and Form collections by default. (You can control this parameter byusing one of the Transfer overloads.) For this reason, when the page handlerprocesses the new sub-request that Execute originates, the Form collectionstill contains the view state of the original page. By default, the pagehandler always checks the view state to detect possible corruption. Thisbehavior is controlled by the EnableViewStateMac attribute that, for securityreasons, you should never disable. If the Form collection is not cleared, thehandler attempts to use the view state of the original page to validate thechild page. Because view state is page-scoped and can't be transferred acrosspages, the check fails and originates the view-state corruption warning. Thisdoesn't happen with Transfer because the method clears the Form collection andquery string by default. It also would fail for Transfer if this overload wereused:
Server.Transfer(url, true)
This is a bug in ASP.NET 1.0 that has been fixed inASP.NET 1.1. For more information, see Microsoft's Knowledge Base article316920. Note that the article doesn't mention Execute, but you can reproducethe problem easily by running this article's sample code.
The sample code in thisarticle is available for download.
Dino Esposito is a trainer and consultant whospecializes in ASP.NET, ADO.NET, and XML. A member of Wintellect and co-founderof http://www.VB2TheMax.com,Dino wrote BuildingWeb Solutions with ASP.NET and ADO.NET and the upcoming Programming ASP.NET,both from Microsoft Press. Write to him at mailto:[email protected].
More in asp.netNOW
Learn to use an overload of Execute to cache all thegenerated output in a TextWriter object. It's a great technique, especially ifyou're trying to reuse as much of your old ASP-based applications as possible.Be sure to look for this tip in an upcoming issue of asp.netNOW, theFREE e-companion to asp.netPRO. Sign up at http://www.aspnetPRO.com today!
Yield to Pages in ASP.NET
Although in ASP.NET you often can devise your applicationto do without the Execute or Transfer methods, these are valuable techniques,especially if you're building from the ashes of an existing ASP application.You can pass values to an external page in a variety of ways.
If you take a look at the source code that accompaniesthis article, you'll see you can spawn the child page either by using Transferor Execute. If you use Execute and run the code in ASP.NET 1.0, you'll have aview-state corruption error. Run the same code on a machine on which you haveASP.NET 1.1 installed (even the beta build 1.1.4322), and it will work great.If you replace the call to Execute with an identical call to Transfer, it'llwork both in ASP.NET 1.0 and ASP.NET 1.1.
Unfortunately, there's no known way to force Execute towork the right way in ASP.NET 1.0. If Transfer simply doesn't work for you, theonly safe alternative I can think of is rewriting the code not to use externalpages.
Tell us what you think! Please send any comments aboutthis article to [email protected] include the article title and author.
About the Author
You May Also Like