When developing enterprise Web applications, we often need to record user operation logs so that they can be traced after an emergency. For example, we need to record every page accessed by users. The common practice may be to compile a method for logging (such as AddAccessLog), and call this AddAccessLog method in the Page_Load event of each page to record page access logs. This method is feasible when there are few pages, but when the project is getting bigger and more pages need to be logged, we need to call this method in every page, this makes the system difficult to maintain. Is there a simple way? Why not use the IHttpModule interface to implement a custom LogHttpModule?
The IHttpModule interface defines two methods: Init and Dispose. The Init method initializes a module and prepares it for request processing. At this time, we agree to receive notifications of events of interest. The Dispose method handles resources used by this module. The Init method accepts a reference to the HttpApplication object that serves the request. You can use this reference to connect to system events.
Class PageLoggerModule: IHttpModule // implements the IHttpModule Interface
{
Public void Dispose ()
{
}
Public void Init (HttpApplication context)
{
Context. BeginRequest + = new EventHandler (context_BeginRequest );
}
Void context_BeginRequest (object sender, EventArgs e)
{
// Implement the specific code here
HttpApplication app = (HttpApplication) sender;
HttpContext ctx = app. Context;
// Acquire session state
String userId = "Not Registered ";
If (ctx. Session! = Null & ctx. Session ["userId"]! = Null)
{
UserId = ctx. Session ["userId"]. ToString ();
}
// Acquire request string
String funcId = "Init funcId ";
If (ctx. Request. QueryString ["funcId"]! = Null)
{
FuncId = ctx. Request. QueryString ["funcId"]. ToString ();
}
LogEntry log = new LogEntry (); // This is a custom class. The attribute list is consistent with the fields in the database.
Log. DateTime = System. DateTime. Now;
Log. IpAddress = ctx. Request. UserHostAddress;
Log. MachineName = ctx. Request. UserHostName;
Log. UserId = userId;
Log. FunctionId = funcId;
String logString = log. DateTime. ToString () + "<br/>"
+ Log. UserId + "<br/>"
+ Log. IpAddress + "<br/>"
+ Log. MachineName + "<br/>"
+ Log. FunctionId + "<br/> ";
// You can add other codes here
Ctx. Response. AppendHeader ("Author", "Changyu Du ");
Ctx. Response. Write (logString );
}
}
In Web. config, add an HttpModule in the System. Web Section:
<HttpModules>
<Add name = "PageLoggerHttpModule" type = "PageLoggerHttpModule. PageLoggerModule, PageLoggerHttpModule"/>
</HttpModules>
Create a common aspx page and save the user information to the Session when loading the page to simulate the project application:
Protected void Page_Load (object sender, EventArgs e)
{
// Add userName into SESSION
If (Session ["userId"] = null)
{
Session ["userId"] = "1101 ";
}
Else
{
Response. Write ("SESSION: userId =" + Session ["userId"]. ToString ());
}
}
To ensure that the user ID information is added to the session, a button is added to the test page, and button_click does nothing, to ensure that the userId is saved to the Session, send the page back. Run F5. Well, it's good. All the LogEntry information has been output, and the access time, IP address, and so on have been obtained. It's basically successful! You can directly Save the LogEntry information to the database.
However, don't be busy. Why can't we get the user information in the Session? The information such as the access log user ID must be recorded!
Later, I found out that I am not familiar with the event processing process of aspnet, And I still did not load the Session status at Begin_Request. Naturally, I cannot get it.
The event trigger sequence provided on MSDN is as follows:
When processing this requestHttpApplicationClass to execute the following events. Want to expandHttpApplicationClass developers need to pay special attention to these events.
When a request is verified, the browser checks the information sent and determines whether it contains a potentially malicious flag. For more information, see ValidateRequest and script intrusion overview.
If you have configured any URL in the UrlMappingsSection of the Web. config file, perform URL ing.
The BeginRequest event is triggered.
The AuthenticateRequest event is triggered.
This triggers the PostAuthenticateRequest event.
The AuthorizeRequest event is triggered.
This triggers the PostAuthorizeRequest event.
This triggers a ResolveRequestCache event.
The PostResolveRequestCache event is triggered.
Based on the file extension of the requested resource (ing in the application configuration file), select an IHttpHandler class to process the request. If the request is for an object (Page) derived from the Page class and needs to compile the Page, ASP. NET will compile the Page before creating an instance.
The PostMapRequestHandler event is triggered.
The AcquireRequestState event is triggered.
This triggers the PostAcquireRequestState event.
Raises the PreRequestHandlerExecute event.
Call the appropriateIHttpHandlerClass ProcessRequest method (or asynchronous BeginProcessRequest ). For example, if the request is for a page, the current page instance processes the request.
The PostRequestHandlerExecute event is triggered.
This triggers the ReleaseRequestState event.
The PostReleaseRequestState event is triggered.
If the Filter attribute is defined, the response is filtered.
The UpdateRequestCache event is triggered.
This triggers the PostUpdateRequestCache event.
An EndRequest event is triggered.
|
AcquireRequestState event occurs when the processing program of the actual service request obtains the status information associated with the request. The userId information in the Session can be obtained only when this event occurs. The BeginRequest event occurs before the AcquireRequestState. If we put the Session Status Code in BeginRequest, it cannot be obtained.
After finding the problem, place the log record code in the AcquireRequestState, and change it to the following:
Public void Init (HttpApplication context)
{
Context. AcquireRequestState + = new EventHandler (context_AcquireRequestState );
}
Void context_AcquireRequestState (object sender, EventArgs e)
{
// The code in the original context_BeginRequest does not repeat the position occupied by the post :)
// Save the information in LogEntry to the database
}
Now we inherit the IHttpModule interface and implement a custom LogMudule. In this way, every page of the user will automatically record the user's information, such as in the access log database, you no longer need to write in Page_Load on each page, which is much easier to maintain! Yeah ~
For convenience, I can paste the source code: it protects the code of my previous blog "Writing BuildProvider to implement ORM and BuildProvider debugging:
CustomBuilderProvider.rar 299KB 2008/3/7 22:07:36
Download