Write your own ASP. NET MVC framework (bottom)

Source: Internet
Author: User
Tags httpcontext

Previous Blog "Write your own ASP. NET MVC Framework (above)" I'll show you how my MVC framework supports and implements Ajax. Today's blog will present my MVC framework support for the UI section.

Note: Since this blog is based on the previous blog, some of the things that have been said will be skipped directly and will not be prompted.
So, if you want to understand this blog, then reading the previous blog "Write your own ASP. NET MVC framework" is necessary.

Back to the top MYMVC features

In the process of developing MYMVC, I learned some of the experience of using ASP. NET WebForm, and also referenced ASP. NET MVC, and also accepted Martin Fowler's summary of the MVC idea. In the design process, I only implemented some of the necessary functions, and did not introduce other class libraries and components, so it is very simple and easy to use.

We can understand MYMVC: It's a simple, easy-to-use framework that fits the MVC mindset.

In the MYMVC framework, view still uses the page in the WebForm, after all, page has been used for ten years, can stand the test of time, it is still our reliable technology. On the other hand, page is also the default HTML output technology in ASP, which is convenient to use.

The difference between MYMVC and Microsoft's ASP. NET MVC is:
1. Do not rely on the URL routing component.
2. Do not provide any HtmlHelper
3. Controller is only a container for action, there is no base class requirement.
4. Action processing request does not distinguish between post, GET
5. The URL can correspond directly to an ASPX page (View) in a site directory.
6. The use of the view is specified using the path, regardless of the name of the controller,action.

Description: Although the URL can correspond with the page in the site, but this correspondence is not necessary, or can not correspond.
And in essence, the page execution process in Webfrom is not the same.
Reflects the execution process of a page request in MYMVC:

Back to top Introduction to sample Project

In order to make everyone interested in Mymvc, but also to test the design of MYMVC, I was developing the MYMVC process, but also dedicated to the development of a MYMVC-based ASP Web site sample project. The site offers three display styles (i.e. three view), with the Customer management page as an example to show the differences between the three view types:

Style 1

The code for the view corresponds to the following:

Style 2

The code for the view corresponds to the following:

Style 3

The code for the view corresponds to the following:

This is a three different style, and the code on the server side is completely different.

The second style, is the use of my previous blog summarized in the "pure Ajax site" style to develop, so in the service-side page development process, the simplest, it needs to output the least HTML, UI part of the client's JS to achieve.

For the first and third styles, their HTML structure is different, the function of the page can also be different, in addition, they should be more similar, are inherited from the following generic type:

Inherits= "Mypageview<customerspagemodel>"

The benefit of inheriting from generic types is that I can design a page with smart hints for access to model. Like what:

With the support of smart hints, you can improve your development efficiency and avoid some low-level spelling mistakes.

Although we can see from the picture above that the "three different pages" appear in the "Same URL address", the controller behind them is the same:

The above code can see that I used 4 http://www.mamicode.com/info-detail-1727250.html, which means that I can actually use 4 different URLs to access the three pages, And each URL will render the corresponding page according to the style chosen by the current user.

In fact, I can also specify more http://www.mamicode.com/info-detail-1727250.html for this action, so that it can handle more URLs. For the use and design purposes of http://www.mamicode.com/info-detail-1727250.html, please continue reading.

Back to top about URL routing

With the advent of. NET Framewrok 3.5, Microsoft has released an "ASP." Component, which presents a different option for the URL optimization method at the time, and it also provides some functionality that the URL rewriting component does not have: generating a URL.

With the advent of ap.net mvc, "ASP. NET Routing" becomes a direct component of this framework, and it's hard to have other choices, and we don't have to.

Interestingly, the Sang of the "ASP. NET Routing" is not very well compliant with some of the rules that ASP., most notably: It skips the "processor mapping" phase, causing ASP. NET MVC to be difficult to support the session. Until the last ASP. 4.0, Microsoft modified the partial implementation of the session so that ASP. NET MVC could finally take this opportunity to solve the full support problem for the session.

While ASP. NET routing can generate a URL, it introduces the concept of Routedata, which requires a lot of basic work at the framework level to support it.

And, I think:
1. Not every site requires this technology, and for sites that do not require URL optimization, the use of URL routing is a waste of performance.
2. On the other hand, even if you need URL optimization, we have a number of URL rewriting components to choose from, so that you don't have to change the current architecture.

Therefore,Although URL routing is not supported by MYMVC, it does not imply that URL optimizations cannot be implemented.

In the MVC mindset, the controller should be the place to process the request and the first part to run. In the traditional WebForm programming model, however, the ASPX page is responsible for processing the request. Therefore, you must take a way to move the first processing of the request from the ASPX page and to execute it in advance.

Also, there are two additional benefits to moving code out of the page:
1. The removed code is definitely not part of the UI, so it is easier to test.
2. The sharing of the code with the UI also means that the results can be selectively presented to different view depending on the operating conditions.

Considering that the action can choose to give the result to a different view, and the session needs support, I finally decided to use a dedicated HttpHandler within the framework to execute the user's action, Depending on the session support mode required by action, Httphandlerfactory creates different HttpHandler to support. Because Httphandlerfactory is required, it must be registered in Web. config.

Back to top Configure MYMVC frame

Mymvc in use, you need a simple configuration in Web. config:

If you are using IIS7, refer to the following configuration:

We can think of Mvcpagehandlerfactory as MYMVC at the entry of the ASP.

Attention:
1. In the configuration code above, it is not necessary to select the ASPX extension, or you can choose the extension you prefer.
2. If you do not like the extension mappings, you can use the methods provided in HTTPMODULE,MYMVC to replace the process.

Back to top map processor (ingress)

After registering mvcpagehandlerfactory in Web. config, all eligible requests will be entered into mvcpagehandlerfactory.
Let's take a look at the implementation code for Mvcpagehandlerfactory:

As you can see from the code, MYMVC first finds out whether there is an action to handle it based on the current request address, and if not, it is handled in the default way of ASP. Therefore, it is not a problem to give the "*.aspx" to mvcpagehandlerfactory.

Description: An empty shell type Aspnetpagehandlerfactory was created because the constructor of PageHandlerFactory could not be called directly.

Go back to the top of the internal initialization

MYMVC when the request is processed for the first time, an initialization process is initiated by a call in the Mvcpagehandlerfactory:

Try to get actioninvokeinfo Vkinfo = Reflectionhelper.getpageactioninvokeinfo (virtualpath) based on the request path;

Reflectionhelper has a static constructor, although the last time I posted its code, but that's part of the code, here's the full initialization code:

From the above code can be seen, at the time of initialization, MYMVC loaded all the pageaction, and Ajaxaction did not use this way to achieve, why? Please read on.

Go back to the top of the mapping process from URL to action

Before we see the MYMVC initialization process, it is actually done in the Reflectionhelper constructor. After this initialization, Mvcpagehandlerfactory calls Reflectionhelper.getpageactioninvokeinfo (virtualpath) to get a specific description of the action to invoke. I call this process: from URL to action mapping .

The implementation code for the Getpageactioninvokeinfo method is as follows:

Before we introduce this mapping process, let's review the Declaration code for Action:

With the initialization process done in the Reflectionhelper constructor, the description of each action generates multiple dictionary entries based on the number of http://www.mamicode.com/info-detail-1727250.html, so , in the Getpageactioninvokeinfo implementation process, but also simply to find the dictionary, you can get the required call information, from the surface to complete the mapping process. The whole process can be expressed in form:

In the example above, I used the url "/mvc/customers", which obviously does not match the URL pattern requirements i specified for mvcpagehandlerfactory registration in Web. config. So, what should we do with it?

Although this URL does not have an extension, but I can still configure the way to solve the HttpHandler, the following configuration is what we need:

When introducing Mvcpagehandlerfactory, MYMVC provides another method Trygethandler for external use. Therefore, in the example Web site, I can also call this method in Global.asax to solve the previous problem:

For the operation of switching HttpHandler, I have the following suggestions:
1. Try to put it in the httpmodule to achieve it. Because you can modify the configuration to switch rules (enabled or disabled), it is more flexible.
2. If can be achieved through HttpHandler mapping, try to prefer the HttpHandler mapping method. Reason: Faster and more standard.

Back to the top Pageurl design idea

In the previous example code, I added multiple http://www.mamicode.com/info-detail-1727250.html for an action to mark the action to handle multiple URLs, so An action can handle which URLs are implemented by specifying http://www.mamicode.com/info-detail-1727250.html.

Why do you call it "Pageurl"?
I think some people may have this question.
I'll answer this question and let you know why I designed pageurl:
1. We request a URL usually to get a page display, so you can think of a URL can ultimately be represented as a page.
2. I also want to use the name [url], but it feels too short, and the AJAX request also has a Url, then it must be explicitly differentiated.
So, I finally decided to use the name http://www.mamicode.com/info-detail-1727250.html.

In the Ajax section, I think it's usually just a matter of getting the data and the ability to process the submitted data. Therefore, in most cases it is not required to view, and a function corresponds to a URL, which can also simplify the problem. So, in the Ajax section, I advocate pointing out directly in the URL which action in which controller to invoke.

In the page section, there is actually a need for an action, which can still be used, but I did not do it for the following reasons:
1. We create a view is also created page, using the path of page is not better? and WebForm fans may be more likely to like it.
2. Multi-url matching function. Detailed instructions will be available later.

For all these reasons, I have designed http://www.mamicode.com/info-detail-1727250.html to be independent of [Action] and http://www.mamicode.com/ The info-detail-1727250.html can be specified more than once.

Attention:
1. The string specified in the URL parameter can correspond to an ASPX page. you can also not correspond to an ASPX page.
2. In the URL parameter, do not include QueryString, otherwise it will not match at all.
3. If you use a URL rewrite component, this should be the rewritten path.

Since the virtualpath I passed into the mvcpagehandlerfactory using the ASP. NET Framework does not include query parameters, it is also appropriate to interpret it as a page path.

Back to top multi-url matching feature

Perhaps some people think that multi-URL matching action is meaningless, such as the following action is more common sense:

Yes, it is common for an action to handle a URL.
But there are still two situations that require this feature. First look at the following example:

The 4 pages involved in the code are rendered with no need for data, but in order to be able to implement multi-style support, they can share an action, so this is just a way to switch the view.

Understanding the above sentence, you may also need to know the implementation code of Stylehelper:

The directory structure of the sample Web site is as follows:

In the example Web site, because of the three different styles, especially in the functional and HTML structure is completely separate, so it is not possible to solve through CSS or skin, so I created three directories for three styles, respectively, the corresponding paging file. Ultimately, depending on the user's Choice (Cookie), the page under which directory is used to render.

User-set Style JS code is as follows,

The C # code for the server is as follows:

Description: Cookiehelper is designed to support unit testing, so do not suspect that the code here does not conform to MVC and will talk about it later.

So, in this case, it makes sense to map multiple URLs to an action. This is the first use of the "multi-url matching feature".

Back to top resolve old URL compatibility issues

In the process of growing a website, there is usually a process of refactoring. During the refactoring process, some of the previous pages may be deleted, or the URL format may be adjusted. However, users may also collect links to this site, but because of the refactoring of the page, old links may be invalidated, resulting in 404 errors. The compatibility of URLs should be resolved at this point.

In ASP. NET, we can configure the urlmappings node in Web. config to do such a mapping transformation. Another way is to create a httpmodule specifically to determine whether to request some old URLs, and if so, to redirect to a new page. In summary, regardless of which method you use, you need to check for each incoming request whether the URL is an old-format URL, and this process is checked by one list, but unfortunately: most requests may be new URL formats, and those compatibility scenarios will undoubtedly waste a lot of CPU resources.

In Mymvc, this problem can be handled simply as in the following example:

This "Customer management" page may have been reconstructed several times, it is OK, as long as the various versions of the address is identified with http://www.mamicode.com/info-detail-1727250.html, completely without the previously mentioned compatibility scheme, therefore, There is no burden on the compatible handling of URLs, and it does not affect performance.

Note: The order of http://www.mamicode.com/info-detail-1727250.html is unimportant and can be adjusted arbitrarily.

Back to top support for identity authentication

MYMVC also supports some basic authentication, which can be indicated by adding the [authorize] modifier property to the action method.
The implementation code for Authorizeattribute is as follows:

The authentication check occurs before the action is called and the code is as follows:

The following sample code demonstrates its use:

Attention:
1. If an action does not use [authorize], it means that any user access is allowed (including users who are not logged in).
2. [Authorize] is still valid for ajaxaction.

Back to the top view design method

In Mymvc, view uses an ASP. NET Page, but I don't recommend using the CodeFile file. Do not use CodeFile files, I think this is a lot of people like WebForm unacceptable. They prefer to get data in the CodeFile file, bind the data, respond to events, and process the user's submission data. It is for this reason that other people think that WebForm is a programming model with very poor unit testing.

Here I would like to express my point of view: whether the code can support unit testing, the most important reason is the developers themselves, the framework of choice is only to promote or partially limited role. even if some people use ASP. NET MVC, the code they write does not necessarily support unit testing, some people are too dependent on httpcontext.current, and even write this code in ASP.

Well, back to the page design topic. The approach advocated by MYMVC is similar to ASP. NET MVC, which is to display data inline in the page instead of binding data in CodeFile. Many people see the inline syntax of ASP, and feel back to the ASP era, that is in reverse, in fact, this is only a superficial phenomenon. Behind the surface: The code is away from the UI. , it can also be understood as: logic away from the UI. This is exactly what ASP. NET MVC has always advocated: separating the concerns. in the new development concept, the original page is decomposed into view and controller, when implementing them, only focus on their own part of it, so, if you look at the page, there may be the previous said that the feeling. On the other hand, because the code is away from the UI, there may be more opportunities to refactor them, making them more reusable.

Here's a look at the code in page MYMVC:

At this point, the data required for rendering can be obtained directly from the model object, but it is required to indicate the type of model in the page directive, which can also have the advantage of smart hints. If the page needs to display data, be sure to inherit from Mypageview<>, which implements the following code:

It's actually a simple type that contains the model property. As for Mybasepage's implementation code, we can ignore it, which is inherited directly from System.Web.UI.Page.

Another piece of code for the user control:

Basically, the way the page was developed, only the base class was replaced with Myusercontrolview<>.

What I think to add here is this:
Unlike ASP. NET MVC, MYMVC does not provide any htmlhelper.
I think HtmlHelper has nothing to do with the idea of MVC, so we don't offer these methods.
On the other hand, many people want to have more control over HTML code, so there is no need to provide these methods.
If you think you need some of the necessary htmlhelper methods, you can implement your favorite HtmlHelper class library.

Finally, I would like to say: page inheritance generic class, but also need some additional processing. For example, the following code:

Inherits= "Mypageview<customerspagemodel>"

To enable this setting to be compiled, the following configuration is required in Web. config:

<pages  pageparserfiltertype= "Mymvc.viewtypeparserfilter, Mymvc"  >

Viewtypeparserfilter implementation code is long, I do not post here, can be downloaded from the end of this article.

Back to the top Controller,action design method

In Mymvc, the action is divided into two types: Ajaxaction and pageaction.
There is no difference between pageaction and ajaxactioin in the definition of a method, as long as it is a public method.
However, the difference between pageaction and ajaxaction is that:
1. Controller's container name is different, pageaction requires that the controller's name must end with a controller.
2. You must have a valid http://www.mamicode.com/info-detail-1727250.html modifier attribute that indicates the URL that can be processed
3. The name of the action is independent of the URL and can be named arbitrarily.

In Mymvc, there is another feature of the 2 action: no distinction between get,post.
The reason is: I like to use jquery, with its implementation of the client Ajax, GET, POST, just a parameter difference. On the other hand, for an HTML form, get, post is only a parameter difference, most forms can also be submitted by get, as long as you want. So, I think, since the client can switch so flexibly, there is no need for the server to do that limit. Maybe some people think it's safer to differentiate between the two, but I don't think they have any effect on security. Instead, if the server ignores them, it will only make the client easier to invoke.

There is also a case where you might want to differentiate between two: a request is the same address as a commit.
This should be a way of developing the "server-centric site" that I summarized in my last article.
In fact, in a project that uses MYMVC,,<form> labels should be handwritten, and may be submitted to another address more often.
Because, I recommend using AJAX to submit data.
So, finally I decided:Mymvc's action does not distinguish between get, POST.

When designing the MYMVC, I never forget the separation of the view and controller, and there are fewer constraints on the controller, only the constraint on the name and the action, so when we implement the action, we can completely separate them into the class library project,
Just like the example project:

The advantage of doing this is that it's easier to test actioin.
At this point the site may be just a bunch of aspx,js, CSS files. I've been looking forward to giving the aspx to the artist for maintenance, so the design would be more likely.

Back to the top of the way to output HTML

MYMVC provides two ways to return HTML in an action, either returning Pageresult or Ucresult, indicating that a page or a user control needs to be rendered. When the action returns these two results, the part of the action is executed. The rest of the processing is done in the MYMVC framework, and the MYMVC framework calls the Ouput method output to the client on these two results in a Iactionresult interface.
The implementation codes for Pageresult and Ucresult are as follows:

The two types are used the same way, you need to provide two parameters, the first parameter represents the location of the page or user control, and the second parameter represents the display data required by the page or user control. For example, the following:

To design these two types of results, my intention is:
1. Ucresult is used for AJAX requests because it is possible to require the server to output a piece of HTML
2. Pageresult is used for full page response.

In Mymvc, the execution of a page or user control requires the path of the page or user control, rather than the convention relationship.
I think the convention will result in a name coupling, and the contract will also affect the flexibility of the restriction, so it must be explicitly specified (allow null).

Pageresult is used for pageaction, and Pageaction has http://www.mamicode.com/ Info-detail-1727250.html to indicate which URLs can be processed, although one pageaction can handle more than one URL, typically it is the case that a URL is handled in the majority of a pageaction. At this point, MYMVC allows the first parameter to be set to NULL when returning Pageresult, indicating that the current request address is used. If there is an ASPX page corresponding to the current request address at this time, it will be much more convenient. You can refer to the following example:

In the MYMVC framework, Pageresult eventually calls Pageexecutor.render () to get the generated code for the page, as follows:

Ucresult calls Ucexecutor.render () to generate the output code for the user control, as follows:

Back to top HTML tile output

Note that the visibility of the 2 render methods described earlier is public, so the idea is to have the framework provide the ability to generate HTML externally, and perhaps some users have this requirement. On the other hand, there may be some users who intend to output the larger HTML page blocks to the client during action execution. Bigpipe used the idea that the entire request does not wait until all the data gets successful, but splits the page into business logic, and immediately outputs a portion of the fragment to the client after acquiring the corresponding data.

In fact, HTML block output in ASP is not a new technology, but in the presence of ASP, it already exists, that is, in the process of output calls Response.Flush () constantly;

Because MYMVC generates HTML as a basic function, in Mymvc, you can easily implement chunked output as long as you call Response.Flush (). However, to make the invocation simpler, I have provided two helper methods to simplify the process.

The Responsewrite method in the Pageexecutor type:

The Responsewrite method in the Ucexecutor type:

Note: because these two methods use httpcontext.current internally, calling them in action causes the action not to support unit tests.

Back to top support for unit tests

When it comes to MVC, I think I have to talk about unit tests.
Because the main idea of MVC is to separate the three items to facilitate development and testing. In this, I think the separation of the view and controller is the most important, because the UI is more difficult to test, in turn, if there is no UI things are easier to test.

In ASP. NET, however, it is not just UI elements that affect unit testing, but also core objects such as Httpcontxt, HttpRequest, HttpResponse. For example, even if we implement the controller in a class library project, but still access querystring,form in action, and even initiate a redirect request, you say how the code is tested.

I think there's an easy way to determine whether a method can support unit testing: Write a console program to call it and see if it works.

Typically, the user's input data is mainly three sources: QueryString, Form, Cookie. And most of the former, cookies are used to save user preferences. Therefore, in MYMVC, you can let the action no longer direct access to QueryString, form, instead of the way: the name to be read as the C # method parameter name is clearly indicated. In this way, the code in the Actioin is far away from the querystring, Form. As for cookie access, MYMVC provides an auxiliary class to support access:

Redirection is also a common requirement. MYMVC is supported by the provision of Redirectresult:

Description: The Ouput method is called by the framework and does not affect unit testing of the action.
Example code:

There is one more common requirement in the ASP. NET project development process: Access to some of the current environment variables.
MYMVC is handled by the following two types.

Note Httpcontexthelper This class, I enclose some of the properties that I normally visit that are related to requests or to the ASP. NET runtime environment. If not enough, you can continue adding. With this code, I can simply invoke them in a console program (you can also move to a unit test project):

People who have used ASP. NET MVC may ask me:
Why not use those types defined by System.Web.Abstractions, so it's easier to support unit tests?
Yes, I also know the benefits of that approach. But that would be a lot more work.
According to the current Mymvc design method, if you want to introduce HttpContextBase, Httprequestbase, httpresponsebase such objects, you need to design the controller base class, And the process of creating a controller is more complex than it is today, or the process of creating a controller from ASP. NET MVC is still incomplete. However, I would like to design another simple way to solve this problem as much as possible. the above method is my design, although not complete, but it is simple and effective, and the test code will be much simpler. On the other hand, I do not provide a controller base class, but can design access methods such as Httpcontexthelper.current, the final result is still can support unit testing, Moreover httpcontexthelper.current this kind of usage also does not make the person difficult to adapt. There is another benefit of not providing: it allows you to design your own base class.

One more thing to add is that there is no use of System.Web.Abstractions inside the MYMVC framework, yes, I know.
This can only be said: The framework's code cannot be unit tested. unit tests that do not affect the user's action code.
In addition, the code in the framework is hard to do unit testing, after all, it's too dependent on ASP. And I don't have that much free time and driving power.

MYMVC There is an unsupported file upload and download.
Here I would say how to achieve this feature:
1. You can access the httpcontext.current directly and ignore the unit testing capabilities of the code.
2. Implement the httpcontexthelper.current that I didn't realize before.
Yes, I did not finish this function, and left it to the user, sorry.

Back to top about frame code with sample code

At the end of this article, I provided the code for the MYMVC framework, along with all the sample code.

I previously provided a demo example of my old version of the framework, which I think I have considered quite thoughtful:
1. No IIS, no vs, can run my demo, because I put the fishaspnetloader in, the call to the bat file is also ready.
2. SQL Server is easy to deploy if it supports the user instance mode.
3. In terms of data, I have provided not only MDF files, but also SQL scripts.
4. A number of documentation was also prepared.

However, the fact is not as good as I thought, there are a lot of people send me e-mail, ask me why the example can not run.
The inability to run the environment is also something that I have not thought of at all:
1. Someone deployed it to IIS6, and the extension mapping encountered a problem.
2. Someone deployed it to IIS7, but I didn't provide the configuration for IIS7!
3. Someone does not have SQL SERVER installed. This can only be no way!
4. Someone cannot complete the SQL Server configuration required by the sample program.
5. Someone opened the project with VS2010 and upgraded the. NET version, encountering some unclear issues.

After absorbing the previous lesson, my example takes an XML file as a data source and adds a IIS7 configuration.
However, one thing I can't set for you is the Write permission for the XML file.
If the data cannot be saved, check the Write permission for the directory, at which point the program does not have any prompts.

Add two more points:
1. If you use VS2010 to open the sample project, do not choose to upgrade the. NET version, do not blindly click OK.
2. If you encounter problems deploying the sample Web site in IIS, we recommend using VS to run the sample Web site.

If you have a problem configuring an ASP. NET application, follow my next blog.
Next blog I'm going to talk about some of the settings that you must know in IIS6/7 and SQL Server when you deploy an ASP.

Transferred from: http://www.cnblogs.com/fish-li/archive/2012/02/21/2361982.html

Write your own ASP. NET MVC framework (bottom)

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.