In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-04-02 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/02 Report--
This article mainly introduces "how to write the service framework with ASP.NET". In the daily operation, I believe that many people have doubts about how to write the service framework with ASP.NET. The editor consulted all kinds of materials and sorted out simple and easy-to-use operation methods. I hope it will be helpful to answer the doubts about "how to write the service framework with ASP.NET". Next, please follow the editor to study!
First of all, I would like to talk about the ASP.NET request processing [pipeline]. I think this is the most important content in ASP.NET. All requests arriving at ASP.NET have to be processed through pipelines, whether it is WebForms, MVC, WebService, WCF (the hosting mode of ASP.NET), or other Microsoft frameworks that adopt the HTTP protocol. Why do these frameworks choose ASP.NET as their running platform?
Let's consider: if you were to design a service framework from scratch, what events would you have to deal with?
I think there are three most fundamental things to do: 1. Listening request port, 2. Assign a thread to each incoming connection request to perform a specific response operation, 3. Read out the requested data and be responsible for sending the processed response data to the caller.
This is actually a complex and boring process, but every server-side program needs these basic functions. Fortunately, IIS and ASP.NET can do these things for us, so those frameworks that choose the ASP.NET platform can save these complex tasks. Using the ASP.NET platform can not only simplify the design, but also has good scalability to meet more frameworks to continue to develop on this platform, and this good scalability is inseparable from its request processing pipeline.
ASP.NET is a fully functional platform framework, which not only provides some high-level frameworks for us to use, such as WebForms, MVC, WebService, but also provides some low-level mechanisms for us to use, so that we can develop new frameworks and new solutions with special requirements. This low-level mechanism is the request processing pipeline. There are two types of objects that use this pipeline: HttpHandler, HttpModule, and the object that controls the work of this pipeline is: HttpApplication. In general, we do not need to use HttpApplication objects directly, so the topic of this article will focus on the functions of HttpHandler and HttpModule objects and how to use them.
Understand the ASP.NET pipeline
The word Pipeline is also interesting, and it also vividly describes the processing of each ASP.NET request: the request is in a pipeline and goes through a series of process points, which are connected to form a line. The above is my understanding of the word, if there is any mistake, please correct it. These series of process points are actually a series of events triggered by HttpApplication, which can usually be subscribed by HttpModule or in Global.asax, and this series of events constitute the life cycle of a request.
The event mode, that is, the observer mode. According to the definition in the book [C # 3.0 Design pattern]: "the Observer pattern defines a relationship between objects so that when one object changes state, all other objects can be notified accordingly." The pipeline design of ASP.NET adopts this approach. In this design pattern, the observer is many HttpModule objects, and the object being observed is each "request". Its state is controlled by HttpApplication and is used to describe the processing phase of the current request. HttpApplication will modify the state according to a specific order and raise corresponding events after each state changes. ASP.NET assigns a HttpApplication object to each request to raise these events, so it allows a large number of observers to know the status of each request, and each observer can modify some of the requested data when they are interested. The data related to the request is HttpRequest, HttpResponse, which I mentioned in my last blog. It is precisely because of the introduction of the event mechanism, the ASP.NET framework also has a strong scalability. Taking a look at how the pipeline processes requests, I will directly refer to the snippet from the original article in MSDN [ASP.NET application lifecycle overview for IIS 5.0 and 6.0].
The following events will be executed by the HttpApplication class when the request is processed. Developers who want to extend the HttpApplication class need to pay particular attention to these events.
1. Validating the request examines the information sent by the browser and determines whether it contains potentially malicious tags. For more information, see ValidateRequest and script intrusion Overview.
two。 If any URL has been configured in the UrlMappingsSection section of the Web.config file, URL mapping is performed.
3. Raises the BeginRequest event.
4. Raises the AuthenticateRequest event.
5. Raises the PostAuthenticateRequest event.
6. Raises the AuthorizeRequest event.
7. Raises the PostAuthorizeRequest event.
8. Raises the ResolveRequestCache event.
9. Raises the PostResolveRequestCache event.
10. Based on the file extension of the requested resource (mapped in the application's configuration file), the class that implements IHttpHandler is selected to process the request. If the request is for an object (page) derived from the Page class and the page needs to be compiled, ASP.NET compiles the page before creating an instance of the page.
11. Raises the PostMapRequestHandler event.
twelve。 Raises the AcquireRequestState event.
13. Raises the PostAcquireRequestState event.
14. Raises the PreRequestHandlerExecute event.
15. Call the ProcessRequest method (or asynchronous version of IHttpAsyncHandler.BeginProcessRequest) of the appropriate IHttpHandler class for the request. For example, if the request is for a page, the current page instance processes the request.
16. Raises the PostRequestHandlerExecute event.
17. Raises the ReleaseRequestState event.
18. Raises the PostReleaseRequestState event.
19. If the Filter property is defined, response filtering is performed.
20. Raises the UpdateRequestCache event.
21. Raises the PostUpdateRequestCache event.
twenty-two。 Raises the EndRequest event.
23. Raises the PreSendRequestHeaders event.
24. Raises the PreSendRequestContent event.
If it is IIS7, the 10th event is the MapRequestHandler event, and two other events have been added before the EndRequest event: the LogRequest event and the PostLogRequest event.
MapRequestHandler, LogRequest, and PostLogRequest events are supported only if the application is running in IIS 7.0 integrated mode and in conjunction with .NET Framework 3.0 or later.
I would like to add here: not every event starting from BeginRequest will be triggered, because during the whole process, you can call Response.End () at any time or have an unhandled exception and end the process ahead of time. Of those "well-known" events, only the EndRequest event is certain to be triggered, and the BeginRequest (of some Module) may not be triggered.
With regard to these pipeline incidents, I just want to remind you of two very important points:
1. Each request will be mapped to a HttpHandler, which is usually the primary object for processing the request.
2. HttpModule can subscribe to these events at will, and can also participate in modifying the request in the event handler.
These two points also determine how HttpHandler and HttpModule work.
I found two [old pictures], hoping to more intuitively illustrate the processing process of the ASP.NET pipeline. Combined with what I mentioned earlier, let's taste the old pictures again.
HttpHandler
HttpHandler is usually the core object for processing requests. The vast majority of requests are mapped to a HttpHandler in step 10, and then executed in step 15, so such objects are often referred to as handlers or handlers. Page is known as a processor, and an ashx file is also a processor, but ashx is more primitive, so let's take a look at what ashx usually looks like:
Using System; using System.Web; public class Login: IHttpHandler {public void ProcessRequest (HttpContext context) {context.Response.ContentType = "text/plain"; string username = context.Request.Form ["name"]; string password = context.Request.Form ["password"]; if (password = = "aaaa") {System.Web.Security.FormsAuthentication.SetAuthCookie (username, false); context.Response.Write ("OK") } else {context.Response.Write ("incorrect username or password.") ;} public bool IsReusable {get {return false;}
You can see that it just implements an IHttpHandler interface, and the IHttpHandler interface is simple:
/ / defines the contract implemented by ASP.NET to synchronously process HTTP Web requests using custom HTTP handlers. Public interface IHttpHandler {/ / gets a value indicating whether other requests can use System.Web.IHttpHandler instances. / / return result: / / true; or false if the System.Web.IHttpHandler instance can be used again. Bool IsReusable {get;} / / enables the processing of HTTP Web requests by implementing a custom HttpHandler of the System.Web.IHttpHandler interface. Void ProcessRequest (HttpContext context);}
There are comments on the IsReusable attribute, so I won't say anything about it. The most important part of the interface is the method void ProcessRequest (HttpContext context); this method simply can not be simple, only one parameter, but the energy of this parameter is not small, with it you have almost everything, this is my evaluation of it. For more details on HttpContext, please refer to my blog [ASP.NET Core objects in my mind].
In Login.ashx, I did three simple things:
1. Read input data: from Request.Form.
two。 Execute specific business logic: a simple judgment.
3. Return the result to the client: call Response.Write ()
Yes, these are the three simple operations, but they are also the usual way to write most ashx files, and it can indeed complete the processing of a request.
Remember: in fact, any HttpHandler handles requests this way, but sometimes it changes with the help of some framework wrapper.
I think that the importance of HttpContext,HttpHandler is essential to the strength of HttpHandler because the pipeline maps each request to a HttpHandler.
Usually, we need a new HttpHandler, just create an ashx file. But you can also create your own HttpHandler, or if you want to hand over a class of [special paths / extensions] to a processor, you need to register that processor in web.config.
Note: if it is a [special extension], you may also need to register in IIS for a simple reason: IIS does not hand over the request to ASP.NET, and our code has no chance to run!
We can register a custom processor in web.config in the following ways:
Or: (I wrapped some of the code for typesetting)
FishWebLib, Version=3.0.0.0, Culture=neutral, PublicKeyToken=04db02423b9ebbb2 "/ > FishWebLib, Version=3.0.0.0, Culture=neutral, PublicKeyToken=04db02423b9ebbb2" / >
HttpModule
I mentioned earlier how HttpModule works: subscribe to pipeline events and perform the required actions in the event handler.
This description looks mundane, but the way it works gives it infinitely powerful processing power.
Its infinitely powerful processing power comes from the ability to subscribe to pipeline events, so it has the ability to modify requests at many stages that may eventually affect the processing of requests.
As I said earlier, "HttpHandler is the primary object for processing requests," but HttpModule is free to specify that a request is handed over to a processor for execution!
Even HttpModule can process requests directly, denying HttpHandler a job at all!
Let's take a look at how HttpModule is implemented:
/ / Module that supports bidirectional GZIP compression, which is automatically processed based on whether the client enables GZIP. / / for the service, don't worry about GZIP processing, the service only needs to deal with input and output. / internal class DuplexGzipModule: IHttpModule {public void Init (HttpApplication app) {app.BeginRequest + = new EventHandler (app_BeginRequest);} void app_BeginRequest (object sender, EventArgs e) {HttpApplication app = (HttpApplication) sender; / / Note: the header "Accept-Encoding" cannot be used here, the meaning of the two is completely different. If (app.Request.Headers ["Content-Encoding"] = = "gzip") {app.Request.Filter = new GZipStream (app.Request.Filter, CompressionMode.Decompress); app.Response.Filter = new GZipStream (app.Response.Filter, CompressionMode.Compress); app.Response.AppendHeader ("Content-Encoding", "gzip");}} public void Dispose () {}
Each HttpModule only needs to implement the IHttpModule interface. IHttpModule is also a simple interface:
Public interface IHttpModule {void Dispose (); void Init (HttpApplication app);}
Of these two methods, * methods can usually remain empty. The most important method is Init, which gives HttpModule the opportunity to subscribe to pipeline events, and then we can perform its specific operations in the corresponding event handling.
Remember when I gave an example on my blog [my idea of ASP.NET Core object]? In QueryOrderService.ashx, in order to support gzip, you need to call the GZipStream class directly, which may not be a problem for one or two ashx. If there are more such processors and each processor writes like that, can you stand it? I can't stand it anyway, so today I changed it to use Module to implement it, and the code is much simpler. It can be downloaded at the end of this article.
For the ASP.NET project: when you find that many operations dealing with input and output are very similar, that is the stage where HttpModule can play, so leave these repetitive operations to it.
To make HttpModule work, you also need to register in web.config:
Typically, I implement some HttpModule in a class library and register it in the web.config of the project I need to use.
This also shows its high reusability: once written, many projects can be used directly.
How HttpModule is loaded: as I said earlier, "ASP.NET allocates a HttpApplication object for each request." in the initialization of each HttpApplication object, it loads all HttpModule registered in web.config. Because ASP.NET does not create only one HttpApplication object, but multiple HttpApplication objects, it is possible for each HttpModule's Init event to be called multiple times. Many people like to do all kinds of initialization here, so please pay attention to the thread safety problems when modifying static variable members here. In particular, if you want to perform program initialization, you should put the code in the Application_Start of Global.asax, and the Init event of HttpModule is not appropriate.
Choose to subscribe to the appropriate pipeline event for HttpModule: this is very important, and subscribing to different events will produce different results. The reason is also simple: in the ASP.NET environment, there is not only one HttpModule, and the judgment of one HttpModule may be based on the output of other HttpModule, and in some (late) pipeline events, the output data can no longer be modified. In the later example, DirectProcessRequestMoudle subscribes to the PostAuthorizeRequest event, which might get better performance if you subscribe to the BeginRequest event, but in the BeginRequest event, the authentication module is not working yet, so each request belongs to the "not logged in" state during this event phase.
I've talked about a lot of HttpModule, and in fact, in this case, the protagonist is another object: Filter. I mentioned it in my last blog, * * in order to demonstrate it, put it in a HttpHandler. I can't help it. The theme of the previous post is not pipelines. I have to play with HttpModule today. I think Filter should still be used with HttpModule to give full play to its unique value. The features of Filter are really not suitable for use in HttpHandler, and if you use Filter in HttpHandler, I think it is necessary to consider whether you are using it incorrectly.
Borrow HttpModule's place. Let's talk about Filter. Filter is very low-key, how low-key: perhaps few people have paid attention to it, so few people have used it. As a matter of fact, you don't need it in general, but when you use it, you will find it very powerful. As I often mentioned earlier, the requested data is almost all in the stream except the request header. If you want to process this data in a streaming manner, especially for all requests, or some kind of request, then using Filter is very appropriate. The previous example is a very reasonable use, savor it, and you may find that Filter can do more.
HttpHandler or HttpModule?
HttpHandler is the primary processing object for each request, while HttpModule can choose which HttpHandler to process the request to, and it can even choose to process the request itself.
Let me give you an example code to show that HttpModule can also request directly:
/ this Module demonstrates that using Module directly can also handle client requests. / / suggestion: this method is not recommended unless there is a good reason. / / internal class DirectProcessRequestMoudle: IHttpModule {public void Init (HttpApplication app) {app.PostAuthorizeRequest + = new EventHandler (app_PostAuthorizeRequest);} void app_PostAuthorizeRequest (object sender, EventArgs e) {HttpApplication app = (HttpApplication) sender; ServiceInfo info = GetServiceInfo (app.Context); if (info = = null) return; ServiceExecutor.ProcessRequest (app.Context, info); app.Response.End ();}
In order to better answer this question in this section, I will give the equivalent code for the segment, but the request is processed by HttpHandler
Internal class MyServiceHandler: IHttpHandler {internal ServiceInfo ServiceInfo {get;set;} public void ProcessRequest (HttpContext context) {ServiceInfo info = this.ServiceInfo? GetServiceInfo (context); ServiceExecutor.ProcessRequest (context, info);}
Both HttpHandler and HttpModule can handle requests, which one should I choose?
In such cases, my answer is: depending on the situation, as I described in the comments, it is not recommended to use HttpModule to handle requests unless there is a good reason. Using HttpModule may be faster at some point, but the key point is to call Response.End () when the processing is complete; this shorts all subsequent events and leaves no chance for other HttpModule to execute. If your framework or project design relies heavily on event handling in the pipeline, calling Response.End () will no doubt break this rule and lead to incorrect results. Choose HttpHandler and this won't happen.
However, there is nothing absolute: you can call Response.End () anywhere during request processing; the result is the same.
Fortunately, short circuits do not occur very often, so choosing HttpHandler will make the entire ASP.NET pipeline work, so I recommend that HttpHandler be preferred.
Especially on the premise that HttpHandler can do a good job, you should choose HttpHandler, because choosing HttpModule will bring unnecessary performance loss to other requests. Please continue to read the details.
In fact, we can also look at this issue from another perspective.
First of all, read the previous sample code carefully. Do you find that they are very similar in the way they are implemented?
The answer should be found by now: separate the specific processing operations outside the HttpHandler,HttpModule. Then, this problem is not a problem at this time, and you can also provide a variety of solutions for users to choose from. For example, I have provided five ways for [my Service Framework] to allow users to easily expose a C# method as a service method. It is up to the user to decide how to choose this question, which will not embarrass me.
My point of view: without too much technical difficulty, it should be right to provide multiple solutions, and you will avoid a lot of hassle.
Invisible performance problems
Earlier I introduced the important advantage of HttpModule: high reusability. As long as you write a HttpModule, you can use it in any ASP.NET project, which is very convenient.
But no matter how good it is, you can't abuse it. HttpModule can also have a negative impact on performance. The reason is also simple: for each ASP.NET request, each HttpModule performs some action logic in the events they subscribe to. This operational logic may be meaningless to some requests, but it will still be executed. Therefore, the computer will waste some resources to execute some meaningless code.
Knowing the reason, the solution is clear:
1. Get rid of unwanted HttpModule
two。 In each HttpModule's event handler, the first step is to determine if it is the request you need to process. For some irrelevant requests, you should withdraw immediately.
When we created an ASP.NET project, Microsoft already loaded a lot of HttpModule for us without making any changes. Look at the following code:
Protected void Page_Load (object sender, EventArgs e) {HttpApplication app = HttpContext.Current.ApplicationInstance; StringBuilder sb = new StringBuilder (); foreach (string module in app.Modules.AllKeys) sb.AppendFormat (module) .append (""); Response.Write (sb.ToString ());}
The output is as follows:
There are 14 in all.
Alas, most of them are definitely not used by me, but they are loaded, so in the events they subscribe to, their code will check all requests, and the meaningless code will have a chance to execute. If you don't want to turn a blind eye, make a similar change in web.config to remove unwanted Module.
The second thing to note about HttpModule is that HttpModule is valid for all requests. If HttpModule cannot handle all requests, please first determine whether the current request needs to be processed. For requests that do not need to be processed, you should exit immediately. Look at the following sample code:
/ [demo] Let the request of Aspx page support gzip compressed output / public class FishGzipModule: IHttpModule {public void Init (HttpApplication app) {app.BeginRequest + = new EventHandler (app_BeginRequest);} void app_BeginRequest (object sender, EventArgs e) {HttpApplication app = (HttpApplication) sender; / / A simple demonstration here, dealing with only the output compression of aspx pages. / / of course, IIS also provides compression, which is for demonstration purposes only and may be suitable for some special occasions. If (app.Request.AppRelativeCurrentExecutionFilePath.EndsWith ("aspx", StringComparison.OrdinalIgnoreCase) = = false) / / Note: first determine whether the request is to be processed, and if not, exit directly. / / instead of: execute the latter judgment first, and then exit when it is found that it is not aspx. Return; string flag = app.Request.Headers ["Accept-Encoding"]; if (string.IsNullOrEmpty (flag) = = false & & flag.ToLower (). IndexOf ("gzip") > = 0) {app.Response.Filter = new GZipStream (app.Response.Filter, CompressionMode.Compress); app.Response.AppendHeader ("Content-Encoding", "gzip");}}
More practical introduction
From here, this article will no longer describe some theoretical words too much, but will demonstrate the powerful pipeline functions of ASP.NET in the form of actual combat, which show some classic application scenarios, most of which will be used as the key part of my Service Framework. So be careful to understand the code.
The actual combat code makes extensive use of most of the objects introduced in the previous blog [ASP.NET Core objects in my mind], which is another demonstration of the importance of those core objects, so be sure to understand those core objects first.
The previous blog only showed the functions of those powerful objects, and it is not realistic to use them alone. Today, I will demonstrate the various tasks they can accomplish working side by side with HttpHandler and HttpModule.
Before the end of the story, the legend continues. More highlights are coming!
Field demonstration-simulate more HttpMethod
In recent years, a concept called RESTful Web service has entered the vision of developers, which advocates the use of GET, POST, PUT and DELETE methods provided by the HTTP protocol to manipulate network resources. However, current browsers only support GET and POST, so some people think of using HTTP headers, form values, or query strings to simulate HTTP methods that these browsers do not support. Each framework that supports RESTful Web services has its own implementation, and today I'll use HttpModule to simulate this operation. The end result is that you can access HttpRequest.HttpMethod directly to get the names of the methods for these operations.
How it works: subscribe to the BeginRequest event in the pipeline, check whether the current request needs to modify the HttpMethod, and if so, modify the HttpMethod property.
So the BeginRequest event is chosen because it is early so that the new results can be read in subsequent stages of the request.
/ [demo] implements Module / internal class XHttpMethodModule to simulate more HttpMethod: IHttpModule {private FieldInfo _ field; public void Init (HttpApplication context) {/ / subscribe to this earlier event, so that new results can be read at subsequent stages of the request. Context.BeginRequest + = new EventHandler (context_BeginRequest); _ field = typeof (HttpRequest) .GetField ("_ httpMethod", BindingFlags.Instance | BindingFlags.NonPublic);} void context_BeginRequest (object sender, EventArgs e) {HttpApplication app = (HttpApplication) sender; / / only check whether it is a POST operation. If you need to use GET to simulate in your application, please modify it here. If (string.Equals (app.Request.HttpMethod, "POST", StringComparison.OrdinalIgnoreCase)) {/ / for simplicity here, I only check the request header, and if you also need to check the form value or query string, please modify it here. String headerOverrideValue = app.Request.Headers ["X-HTTP-Method-Override"]; if (string.IsNullOrEmpty (headerOverrideValue) = = false) {if (string.Equals (headerOverrideValue, "GET", StringComparison.OrdinalIgnoreCase) = = false & & string.Equals (headerOverrideValue, "POST", StringComparison.OrdinalIgnoreCase) = = false) {/ / HttpRequest.HttpMethod attribute is actually accessing the private field _ httpMethod, which I will modify directly. / / after this modification, the original HTTP method is lost, which is usually acceptable. _ field.SetValue (app.Request, headerOverrideValue.ToUpper ();}
I think it's a good choice to use HttpModule to deal with this problem. It has at least two benefits:
1. This HttpModule can continue to be used by other website projects, thus improving the reusability of the code.
two。 I can decide at any time whether or not to support simulation, and when I don't need it, I don't have to load it from web.config, so switching is flexible and there is no need to modify existing code.
Let's take a look at the page and the call result.
Protected void Page_Load (object sender, EventArgs e) {Response.Write (Request.HttpMethod);}
The call result is as follows:
Practical demonstration-URL rewriting
Use HttpModule to implement URL rewriting. This function should be a very classic application of HttpModule.
Typically, the common way for this application is to rewrite a URL: / product/12 to / product.aspx?id=12, where product.aspx should be an existing page. Obviously, the rewritten address is more friendly. The purpose of URL rewriting is to make URL more friendly.
How it works: subscribe to the PostAuthorizeRequest event of the pipeline, check to see if URL is the pattern you want to modify, and if so, call Context.RewritePath () to complete the rewrite of URL. In subsequent processing of the pipeline, the new URL will eventually be used to map to an appropriate HttpHandler. Note: the selected event only needs to be before the 10th event, because rewriting the URL before the 10th event is guaranteed to map the request to the appropriate processor to execute. It's as simple as that. Please refer to the following code:
Public class MyServiceUrlRewriteModule: IHttpModule {/ / write down the dead address directly for simple demonstration. / / Note: MyService.axd must be registered in web.config to ensure that it can be mapped successfully. Public static string RewriteUrlPattern = "/ MyService.axd?sc= {1} & op= {1}"; public void Init (HttpApplication app) {app.PostAuthorizeRequest + = new EventHandler (app_PostAuthorizeRequest);} void app_PostAuthorizeRequest (object sender, EventArgs e) {HttpApplication app = (HttpApplication) sender; / / here we will check whether URL is a pattern that needs to be rewritten, such as / / http://localhost:11647/service/OrderService/QueryOrder NamesPair pair = FrameworkRules.ParseNamesPair (app.Request); if (pair = = null) return / / starting rewriting URL,*** will map to MyServiceHandler int p = app.Request.Path.IndexOf ('?'); if (p > 0) app.Context.RewritePath (string.Format (RewriteUrlPattern, pair.ServiceName, pair.MethodName) + "&" + app.Request.Path.Substring (p + 1); else app.Context.RewritePath (RewriteUrlPattern, pair.ServiceName, pair.MethodName);}
What happened to rewriting?
For an incoming request: http://localhost:11647/service/FormDemoService/ShowUrlInfo
It will be rewritten as: http://localhost:11647/MyService.axd?sc=FormDemoService&op=ShowUrlInfo
Because MyService.axd is registered in web.config, ASP.NET will forward the request to the registered processor to process it.
Note: URL overrides can affect the values of some variables. Please refer to the following code, I will write a service method to detect this phenomenon:
[MyServiceMethod]
Public string ShowUrlInfo (int a)
{
System.Web.HttpRequest request = System.Web.HttpContext.Current.Request
System.Text.StringBuilder sb = new System.Text.StringBuilder ()
Sb.AppendFormat ("Path: {0}", request.Path)
Sb.AppendFormat ("RawUrl: {0}", request.RawUrl)
Sb.AppendFormat ("Url.PathAndQuery: {0}", request.Url.PathAndQuery)
Return sb.ToString ()
}
Output result:
Practical demonstration-URL routin
Use HttpModule to implement URL routing. This feature has become increasingly popular with the advent of the ASP.NET MVC framework.
The goal of URL routing is also to be more URL-friendly, similar to URL rewriting.
Implementation principle: subscribe to the PostResolveRequestCache event of the pipeline, check whether URL is the desired route pattern, if so, find a suitable processor based on the information contained in the request, and temporarily save the processor, rewrite URL to an address where ASP.NET can map the processor. In the PostMapRequestHandler of the pipeline, check to see if there is a temporary processor, and if so, re-assign the Context.Handler and rewrite the URL to the original address. In the subsequent processing of the pipeline, Context.Handler 's HttpHandler will eventually be used. It's as simple as that. Please refer to the following code:
Public class MyServiceUrlRoutingModule: IHttpModule
{
Private static readonly object s_dataKey = new object ()
Public void Init (HttpApplication app)
{
App.PostResolveRequestCache + = new EventHandler (app_PostResolveRequestCache)
App.PostMapRequestHandler + = new EventHandler (app_PostMapRequestHandler)
}
Private void app_PostResolveRequestCache (object sender, EventArgs e)
{
HttpApplication app = (HttpApplication) sender
/ / get the appropriate processor, note that this is the fundamental difference from URL rewriting.
/ that is: [take the initiative] to find a processor according to the current request instead of using RewritePath to ask ASP.NET to find it for us.
MyServiceHandler handler = GetHandler (app.Context)
If (handler = = null)
Return
/ / temporarily save the previously obtained processor, and this value will be taken out in the PostMapRequestHandler event.
App.Context.items [s _ dataKey] = handler
/ / enter a normal MapRequestHandler event and map it to a processor.
App.Context.RewritePath ("~ / MyServiceUrlRoutingModule.axd")
}
Private void app_PostMapRequestHandler (object sender, EventArgs e)
{
HttpApplication app = (HttpApplication) sender
/ / remove the processor obtained in the PostResolveRequestCache event
MyServiceHandler handler = (MyServiceHandler) app. Context. Items [s _ dataKey]
If (handler! = null) {
/ / restore the URL request address. Notice the difference between this and URL rewriting.
App.Context.RewritePath (app.Request.RawUrl)
/ / restore the processor obtained according to the GetHandler (app.Context) call.
/ / because at this time, app.Context.Handler is obtained by "~ / MyServiceUrlRoutingModule.axd" mapping.
App.Context.Handler = handler
}
}
Note: in MyServiceUrlRoutingModule, I will request [route] to an instance of MyServiceHandler instead of having ASP.NET select it for me based on URL.
In the demonstration of URL rewriting, some URL-related properties have changed, so let's take a look at what URL routing looks like:
Implement your own service framework
This blog said at the beginning: we will improve the last service implementation in this blog to make it a really useful service framework.
Earlier, when talking about the ASP.NET pipeline, I gave you a lot of sample code, which can be downloaded at the end of the blog. This code comes from part of the source code in my Service Framework. I will focus on my Service Framework below.
Expose a class as a service using my Service Framework
In [my Service Framework], if a class wants to be exposed as a service class, it does not need to inherit a class or implement any interface, it just needs to add a feature to the class and a feature to the method. The example code is as follows:
[MyService]
Public class OrderService
{
[MyServiceMethod]
Public static string Hello (string name)
{
Return "Hello" + name
}
[MyServiceMethod]
Public ListQueryOrder (QueryOrderCondition query)
{
/ / simulate the query process, and a list is returned directly here.
Listlist = new List ()
For (int I = 0 * * I 0
Select new TypeAndAttrInfo {
ServiceType = t, Attr = a [0], AuthorizeAttr = t.GetClassAuthorizeAttribute ()}
ToList () .ForEach (b = > s_typeList.Add (b))
}
Catch {}
}
}
Private static AuthorizeAttribute GetClassAuthorizeAttribute (this Type t)
{
AuthorizeAttribute [] attrs = (AuthorizeAttribute]) t.GetCustomAttributes (typeof (AuthorizeAttribute), false)
Return (attrs.Length > 0? Attrs [0]: null)
}
/ / /
/ / get the corresponding service type according to a name (get the type from the cache)
/ / /
/ / /
/ / /
Private static TypeAndAttrInfo GetServiceType (string typeName)
{
If (string.IsNullOrEmpty (typeName))
Throw new ArgumentNullException ("typeName")
/ / how to find the type: if there is a dot, look for it by full name (including namespace), otherwise only look at the name.
/ / this framework will return * matches for multiple matching criteria.
If (typeName.IndexOf ('.') > 0)
Return s_typeList.FirstOrDefault (t = > string.Compare (t.ServiceType.FullName, typeName, true) = = 0)
Else
Return s_typeList.FirstOrDefault (t = > string.Compare (t.ServiceType.Name, typeName, true) = = 0)
}
Private static Hashtable s_methodTable = Hashtable.Synchronized (
New Hashtable (4096, StringComparer.OrdinalIgnoreCase))
/ / /
/ / obtain the corresponding method information according to the specified type and method name
/ / /
/ / /
/ / /
/ / /
Private static MethodAndAttrInfo GetServiceMethod (Type type, string methodName)
{
If (type = = null)
Throw new ArgumentNullException ("type")
If (string.IsNullOrEmpty (methodName))
Throw new ArgumentNullException ("methodName")
/ / first try to read from the cache
String key = methodName + "@" + type.FullName
MethodAndAttrInfo mi = (MethodAndAttrInfo) s _ methodTable [key]
If (mi = = null) {
/ / Note: method overloading is not considered here.
MethodInfo method = type.GetMethod (methodName
BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase)
If (method = = null)
Return null
MyServiceMethodAttribute [] attrs = (MyServiceMethodAttribute [])
Method.GetCustomAttributes (typeof (MyServiceMethodAttribute), false)
If (attrs.Length! = 1)
Return null
/ / because the parameters of the service method are derived from deserialization, only one parameter can be included at this time.
ParameterInfo [] paraInfos = method.GetParameters ()
If (paraInfos.Length! = 1)
Throw new ArgumentNullException ("the specified method was found, but the number of parameters for this method is not 1")
AuthorizeAttribute [] auths = (AuthorizeAttribute]) method.GetCustomAttributes (typeof (AuthorizeAttribute), false)
Mi = new MethodAndAttrInfo {
MethodInfo = method
ParamType = paraInfos [0] .ParameterType
Attr = attrs [0]
AuthorizeAttr = (auths.Length > 0? Auths [0]: null)
}
S _ methodTable [key] = mi
}
Return mi
}
/ / /
/ / return the relevant information to be called based on the type name and method name
/ / /
/ / an object that contains the name of the type and method
/ / /
Public static InvokeInfo GetInvokeInfo (NamesPair pair)
{
If (pair = = null)
Throw new ArgumentNullException ("pair")
InvokeInfo vkInfo = new InvokeInfo ()
VkInfo.ServiceTypeInfo = GetServiceType (pair.ServiceName)
If (vkInfo.ServiceTypeInfo = = null)
Return null
VkInfo.MethodAttrInfo = GetServiceMethod (vkInfo.ServiceTypeInfo.ServiceType, pair.MethodName)
If (vkInfo.MethodAttrInfo = = null)
Return null
If (vkInfo.MethodAttrInfo.MethodInfo.IsStatic = = false)
VkInfo.ServiceInstance = Activator.CreateInstance (vkInfo.ServiceTypeInfo.ServiceType)
Return vkInfo
}
}
ServiceExecutor is used to invoke service methods, and all of the five service publishing methods mentioned above will eventually go through here.
/ / /
/ / the utility class that finally invokes the service method.
/ / /
Public static class ServiceExecutor
{
Internal static void ProcessRequest (HttpContext context, ServiceInfo info)
{
If (context = = null)
Throw new ArgumentNullException ("context")
If (info = = null | | info.InvokeInfo = = null)
Throw new ArgumentNullException ("info")
/ / if (context.Request.InputStream.Length = = 0)
/ / throw new InvalidDataException ("there is no call data, please pass in the call data as the request body.")
If (info.InvokeInfo.AuthenticateRequest (context) = = false)
ExceptionHelper.Throw403Exception (context)
/ / get the data serialization format of the client.
/ / default implementation method: request.Headers ["Serializer-Format"]
/ / Note: this is my custom request header name, or it may not be specified. The default is: form (form)
String serializerFormat = FrameworkRules.GetSerializerFormat (context.Request)
ISerializerProvider serializerProvider =
SerializerProviderFactory.GetSerializerProvider (serializerFormat)
/ / get the parameter type to call the method
Type destType = info.InvokeInfo.MethodAttrInfo.ParamType
/ / get the parameters to be called
Context.Request.InputStream.Position = 0 Module hand / prevent other GPS from reading, but no homing.
Object param = serializerProvider.Deserialize (destType, context.Request)
/ / invoke the service method
Object result = info.InvokeInfo.MethodAttrInfo.MethodInfo.Invoke (
Info.InvokeInfo.ServiceInstance, new object [] {param})
/ / write the output result
If (result! = null)
SerializerProvider.Serializer (result, context.Response)
}
/ / /
/ / [external interface] is used to execute a request based on the class name and method name of the service
/ / /
/ / /
/ / /
Public static void ProcessRequest (HttpContext context, NamesPair pair)
{
If (pair = = null)
Throw new ArgumentNullException ("pair")
If (string.IsNullOrEmpty (pair.ServiceName) | | string.IsNullOrEmpty (pair.MethodName))
ExceptionHelper.Throw404Exception (context)
InvokeInfo vkInfo = ReflectionHelper.GetInvokeInfo (pair)
If (vkInfo = = null)
ExceptionHelper.Throw404Exception (context)
ServiceInfo info = new ServiceInfo (pair, vkInfo)
ProcessRequest (context, info)
}
}
At this point, the study on "how to write a service framework in ASP.NET" is over. I hope to be able to solve your doubts. The collocation of theory and practice can better help you learn, go and try it! If you want to continue to learn more related knowledge, please continue to follow the website, the editor will continue to work hard to bring you more practical articles!
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.