Page refresh features

Source: Internet
Author: User
Tags classic asp
To analyze the actual situation, that is, filtering requests before the HTTP processing program processes the request, which helps to achieve an originally impossible feature. The sending-back mechanism has a serious defect-if the user refreshes the current display page, the last action taken on the server will be repeated blindly. For example, if a new record is added as the result of the previous sending, the application tries to insert an identical record at the next sending. Of course, this will lead to the insertion of identical records, so an exception should occur. This defect already exists when web programming first appeared, and ASP. NET will not introduce it. To implement non-repetitive actions, some countermeasures must be taken to convert any key server-side operations into idempotence. In algebra, if the result of an operation does not change no matter how many times it is executed, the operation is idempotent. For example, take a look at the following SQL command:

Delete from employees where employeeid = 9

We can execute this command 1000 times in a row, but only one record is deleted at most, that is, the record that meets the standards set in the WHERE clause. Consider the following command:

Insert into employees values (...)

Every time you execute this command, you may add a new record to the Employees table. This is especially true if key columns or non-unique columns are automatically encoded. If the table design requires that the key be unique and clearly specified, an SQL exception will be thrown when the command is run for 2nd times.

Although the special situations just now are usually solved at the data access layer (DAL), its basic mode represents a common solution for most web applications. Therefore, the question to be studied is: how to identify whether a page is returned because of an explicit user operation, or because the user presses the F5 key or the page refresh toolbar button?

1. Basic principles of page refresh

Page refresh is an internal browser operation that does not provide any external notifications based on events or callbacks. Technically, page refresh is composed of the "simple" repetition of the latest request. The browser caches the latest requests it serves and re-displays them when the user presses the page refresh key or button. Browsers I know do not provide any types of notifications for page refresh events-even if there are, they are undoubtedly not a recognized standard.

Therefore, server-side code (for example, ASP. NET, classic ASP, or isapi dll) cannot distinguish a refresh request from a common submission or sending back request. To help ASP. NET detect and process page refreshes, we need to create a peripheral mechanism to make two identical requests look different in other aspects. All known browsers refresh by resending the last HTTP request. To make this copy different from the original request, an additional service must add other parameters, whereas ASP. NET page must be able to capture them.

I have considered some additional requirements. The solution should not depend on the session status, and should not overload the server memory. It should be relatively easy to deploy and should be as invisible as possible.

2. Brief description of the Solution

This solution is based on the following idea: each request is assigned a tag number, and the HTTP module tracks the tag of the last service on each different page it processes. If the tag number held on the page is smaller than the tag of the last service on the page, it can only indicate that the same request is served-that is, the page is refreshed. The solution consists of two construction blocks: an HTTP module and a custom page class. The former performs a preliminary check on the tag number, the latter automatically adds a progressive tag number to each Service Page. Two steps are involved to make the feature work: register the HTTP module first, and change the basic code hiding class for each page in the relevant application to detect browser refreshing.

The HTTP module is located in the middle of the HTTP Runtime Library environment and registers each request for a resource in the application. When the page is requested for the first time (not when it is sent back), no tags are assigned. The HTTP module generates a new tag number and stores it in the items collection of the httpcontext object. In addition, this module initializes the internal counter of the last service tag to 0. When the page is requested, the module compares the final service tag with the page tag. If the page tag is updated, the request is considered a normal sending request; otherwise, the request is marked as a page refresh. Table 2.6 summarizes the two scenarios and their related operations.

Table 2.6 scenarios and actions

Scenario

Animation

No related tags on the page

● Non-Refresh

The counter of the last service tag is set to 0.

Generates tags for the next request on the current page and stores them in the items collection.

The page has an associated tag

● If the tag associated with the page is smaller than the tag of the last service, the page is refreshed.

Finally, set the counter of the Service tag to the tag associated with the page.

Generates tags for the next request on the current page and stores them in the items collection.

To ensure that each request (except for the first time) has an appropriate tag, you need some help from the page class. This is why we need to set the code hidden class of each page that intends to support this feature to a specific class-this is a process we will discuss later. The page class receives two different types of information from the HTTP module: The next tag in a hidden field to be stored along with the page, and whether the request is a page refresh information. As a value-added service for developers, the Code hiding class provides an additional Boolean attribute: isrefreshed, allowing developers to understand whether a request is a page refresh or a regular sending back.

Note: The items set in the httpcontext class is a carrier set that is specially set up to allow the HTTP module to transmit information to pages and HTTP processing programs that are actually responsible for service requests. The HTTP module used here sets two data items in the items collection. One data item allows the page to know whether the request is refreshing the page; the other data item allows the page to know what the next tag number is. Let the HTTP module pass the next tag number to the page to make the page class behavior as simple and linear as possible, so as to transfer most of the implementation and execution burden to the HTTP module.

3. Implementation of the Solution

The solution I just outlined has several issues to be studied. First, the status is required. Where do we save it? Next, an HTTP module is called for each input request. How do I differentiate requests for the same page? How to pass information to the page? How smart do you want the page to be?

Obviously, every problem listed here can be designed and implemented using different methods described here. To get a feasible solution, all the design choices made here should be considered arbitrary. If you need to re-process the code to better satisfy your own purposes, you can replace it with an equivalent policy. The code version provided in the next instance incorporates the most valuable suggestions I have ever collected. As described in the previous important tip, try to move the code to the HTTP module.

The following code shows the implementation of the HTTP module:

Public class refreshmodule: ihttpmodule

{

Public void Init (httpapplication APP ){

App. beginrequest + = new eventhandler (onacquirerequeststate );

}

Public void dispose (){

}

Void onacquirerequeststate (Object sender, eventargs e ){

Httpapplication APP = (httpapplication) sender;

Httpcontext CTX = app. context;

Refreshaction. Check (CTX );

Return;

}

}

This module listens to the beginrequest event and ends calling the check method on the refreshaction auxiliary class.

Public class refreshaction

{

Static hashtable requesthistory = NULL;

// Other string constants defined here

M

Public static void check (httpcontext CTX ){

// Initialize the ticket slot

Ensurerefreshticket (CTX );

// Read the last ticket served in the Session (from session)

Int lastticket = getlastrefreshticket (CTX );

// Read the ticket of the current request (from a hidden field)

Int thisticket = getcurrentrefreshticket (CTX, lastticket );

// Compare tickets

If (thisticket> lastticket |

(Thisticket = lastticket & thisticket = 0 )){

Updatelstrefreshticket (CTX, thisticket );

CTX. items [pagerefreshentry] = false;

}

Else

CTX. items [pagerefreshentry] = true;

}

// Initialize the internal data store

Static void ensurerefreshticket (httpcontext CTX)

{

If (requesthistory = NULL)

Requesthistory = new hashtable ();

}

// Return the last-served ticket for the URL

Static int getlastrefreshticket (httpcontext CTX)

{

// Extract and return the last ticket

If (! Requesthistory. containskey (CTX. Request. Path ))

Return 0;

Else

Return (INT) requesthistory [CTX. Request. Path];

}

// Return the ticket associated with the page

Static int getcurrentrefreshticket (httpcontext CTX, int lastticket)

{

Int ticket;

Object o = CTX. request [currentrefreshticketentry];

If (O = NULL)

Ticket = lastticket;

Else

Ticket = convert. toint32 (O );

CTX. items [refreshaction. nextpageticketentry] = ticket + 1;

Return ticket;

}

// Store the last-served ticket for the URL

Static void updatelstrefreshticket (httpcontext CTX, int ticket)

{

Requesthistory [CTX. Request. Path] = ticket;

}

}

The check method is as follows: it compares the last service tag (if any) with the tag provided on the page. This page stores tag numbers in a hidden field that is read through the request object interface. The HTTP module maintains a hash. Each URL of the Service has a table entry. The value in the hash list stores the tag of the last service of the URL.

Pay attention to the item indexer attribute to set the tag of the final service, because the item overwrites the existing item. If the data item already exists, the add method returns only.

In addition to creating the HTTP module, we also need to arrange a page class to serve as the base class for detecting the page refreshed by the browser. The code for this page class is as follows:

// Assume to be in a custom namespace

Public class page: system. Web. UI. Page

{

Public bool isrefreshed {

Get {

Httpcontext CTX = httpcontext. Current;

Object o = CTX. items [refreshaction. pagerefreshentry];

If (O = NULL)

Return false;

Return (bool) O;

}

}

// Handle the prerendercomplete event

Protected override void onprerendercomplete (eventargs e ){

Base. onprerendercomplete (E );

Saverefreshstate ();

}

// Create the hidden field to store the current request ticket

Private void saverefreshstate (){

Httpcontext CTX = httpcontext. Current;

Int ticket = (INT) CTX. items [refreshaction. nextpageticketentry];

Clientscript. registerhiddenfield (

Refreshaction. currentrefreshticketentry,

Ticket. tostring ());

}

}

The example Page defines a new public Boolean attribute isrefreshed. We can use this attribute in the code using methods like ispostback or iscallback. This instance page overrides the onprerendercomplete method and adds hidden fields with page labels. As mentioned above, this page tag is obtained from the HTTP module through a special (and named at Will) item in the items set.

Figure 2.8 shows a running Sample Page.

Figure 2.8 If you refresh the browser view, the page does not repeat a sensitive action

The following is the source code of the page.

Public partial class testrefresh: proaspnetw.cs. components. Page

{

Protected void addcontactbutton_click (Object sender, eventargs E)

{

MSG. innertext = "added ";

If (! This. isrefreshed)

Addrecord (fname. Text, lname. Text );

Else

MSG. innertext = "Page refreshed ";

Binddata ();

}

M

}

The isrefreshed attribute allows us to decide what to do when a send-back action is requested. In the above Code, if the page is being refreshed, The addrecord method is not called. Needless to say, isrefreshed only applies to the custom page class described here. The custom page class does not just add this attribute, but also adds hidden fields, which is essential for this mechanism to work.
From: ASP. NET 2.0 advanced programming a programmer

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.