Brnshop Open Source Online store Second: ASP. NET MVC Framework

Source: Internet
Author: User

At the beginning of the team design Brnshop Web project, we encountered two problems, the first is the reuse and transfer of data, the second is the choice of a large MVC framework and a small MVC framework. Let me explain below in turn.

The first is the reuse and transfer of data: For each request of Brnshop, the program is divided into several stages of execution, such as validation, execution of action methods, etc., we may need to reuse the same information at various stages, and our vision is to hope that this information can only be obtained once, and then flow along the process pipeline, This can be used directly in the later stages, without re-acquisition, to improve the performance of the program. For example: In the authorization verification phase, we verify the user to obtain the user information, when the end of the verification, the user information is not discarded, but retained, so in the later action method we do not need to retrieve the user information, but directly using the authorization has been retained in the user information can be.

The implementation is like this: first we define a context class for the data that needs to be common, They are the Webworkcontext class and Adminworkcontext class in the BrnShop.Web.Framework project, where Webworkcontext is the context used by the foreground project, and Adminworkcontext is the context in which the background project is used. The code is simple enough to define some common fields, as follows:

View CodeView Code

With the context class, we need to find a place where the context can be guaranteed to flow. After flipping through the source code of ASP. NET MVC, we found a good place to be in the controller's base class controllers. Microsoft defines six methods in the controller, as follows:

    • protected override void Initialize (RequestContext requestcontext); Description: Data that might not be available after initializing the call constructor.
    • protected virtual void Onauthorization (AuthorizationContext filtercontext); Description: Called when authorization is made.
    • protected virtual void onactionexecuted (ActionExecutedContext filtercontext); Description: Called after the action method is invoked.
    • protected virtual void onactionexecuting (ActionExecutingContext filtercontext); Description: Called before the action method is invoked.
    • protected virtual void onresultexecuted (ResultExecutedContext filtercontext); Description: Called after the action result returned by the action method is executed.
    • protected virtual void onresultexecuting (ResultExecutingContext filtercontext); Description: Called before executing the result of the operation returned by the action method.

These are virtual methods, so we can define a new controller that inherits from controllers, and then override these methods. Because these methods are in the same class, they can share the same field (this is the context), and the other controllers inherit from the new controller class, so you can also access the shared field (the field of the parent class) in the action method. The new controller classes are Basewebcontroller classes and Baseadmincontroller classes in the BrnShop.Web.Framework project, where Basewebcontroller is the foreground controller class and Baseadmincontroller is the background Controller class, specifically implemented as follows:

View CodeView Code

This is not the end of the story, that is, the context is the field of the Controller, in the view if you want to access it requires coercion type conversion, the code is: ((Basewebcontroller) (this. Viewcontext.controller)). Workcontext; Imagine that we need such a long piece of code every time we access the context. What's the torment? But fortunately there is a solution, that is to rewrite the MVC webviewpage page (if you do not know Webviewpage and MVC compilation process Please read the great god "Artech" related articles, the address is as follows:/HTTP The specific code webviewpage classes and Adminviewpage classes in the BrnShop.Web.Framework project, where Webviewpage is the foreground view class and Adminviewpage is the background view class:

View CodeView Code

After defining the new view class, we need to inform the compiler to use this new class, which is shown in the Web. config of the view file, as detailed in the following:

By setting the value of "PageBaseType" to our new class name, we can use the context directly in the view file. Example: @WorkContext. Shopconfig.seokeyword

Having said the data reuse and delivery, let's talk about the big MVC framework and the small MVC framework. What is the big MVC framework First and what is the small MVC framework?

    • The big MVC framework refers to the complete set of ASP. NET MVC framework, including routing, Controller, model binding, model check, filter and so on.
    • The small MVC framework refers to the MVC part that only the project must use, and is not used or removed for parts that are not in use.

What do you think this is going to be difficult? But for an open source project this is really a very important issue, because the open source project for the product of the national and even the world's developers, everyone's technology is uneven, some high, there is a low. To ensure as many developers as possible, only the original MVC is more intimate and familiar to the developer, so the big MVC framework should be used. But a good product is not just for beginners, but also for advanced developers, who want to get the most control over the project, so the framework should try to use only the most core MVC part, so that the developer can be more space, so it seems that the small MVC framework should be used. Let me explain how we solve this problem from two aspects.

The first is the MVC filter: The friends who have seen our source have found that none of the filter classes have been defined in our project. So where are our filters? The answer is in the context flow above and we implement all the filters in the filter method overridden above. If you want to filter on a separate controller A, you can add your own code again by overriding the filter method in a. If you want to filter on only one method, you only need to filter on the method individually. This allows us to isolate the third-party filter by using a filter method built into the controller, and also reduces the number of times the reflection gets the filter.

The second is model binding and validation: We first remove all model bindings by manually acquiring the request collection, using the login code as an example:

        <summary>///Login///</summary> public ActionResult Login ()//Note that there are no parameters in this regard            {String returnUrl = webhelper.getquerystring ("ReturnUrl");            if (Returnurl.length = = 0) ReturnUrl = "/";            if (WorkContext.ShopConfig.LoginType = = "") return Promptview (RETURNURL, "The mall has now closed the login function!");            if (Workcontext.uid > 0) return Promptview (RETURNURL, "you are logged in without having to log in repeatedly!"); if (WorkContext.ShopConfig.LoginFailTimes! = 0 && Loginfaillogs.getloginfailtimesbyip (workcontext.ip) >= WorkContext.ShopConfig.LoginFailTimes) return Promptview (RETURNURL, "You have entered the error" + WorkContext.ShopConfig.Logi            Nfailtimes + "Times password, please log in 15 minutes later!"); Get Request if (Webhelper.isget ()) {Viewdata.add ("oauthpluginlist", Plugins.getoauthplug                Inlist ());            Return View (New Loginmodel ()); }//post Request LOginmodel model = new Loginmodel (); Model bindings are manually bound to models. AccountName = webhelper.getformstring (WorkContext.ShopConfig.ShadowName).            Trim (); Model.            Password = webhelper.getformstring ("Password"); Model.            Isremember = Webhelper.getformint ("Isremember"); Model.            Verifycode = webhelper.getformstring ("Verifycode");            Model validation Partuserinfo Partuserinfo = VerifyLogin (models); if (!                Modelstate.isvalid)//validation fails {viewdata.add ("Oauthpluginlist", Plugins.getoauthpluginlist ());            return View (model);                    } else//When validation is successful {//when the user level is a disabled access level if (Partuserinfo.userrid = = 1)                Return Promptview ("Your account is currently locked and cannot be accessed");                Delete Login failure log loginfaillogs.deleteloginfaillogbyip (WORKCONTEXT.IP); Update user last access int regionid = workcontext.region! = null?  WorkContext.Region.RegionId:-1;              Users.updateuserlastvisit (Partuserinfo.uid, Workcontext.ip, RegionID, DateTime.Now);                Update the shopping cart User ID orders.updateshopcartuidbysid (partuserinfo.uid, Workcontext.sid); Write user information to Cookie Shoputils.setusercookie (Partuserinfo, (WorkContext.ShopConfig.IsRemember = = 1 && m Odel. Isremember = = 1)?                30:-1);            Return Redirect (RETURNURL); }        }

The second is the model check, and the verification is divided into two parts. The first part is the verification, we also use Manual Check, the same as the landing as an example:

        <summary>///Login verification///</summary> private Partuserinfo VerifyLogin (Loginmodel            Model) {Partuserinfo partuserinfo = null; Verify the Account name if (string. Isnullorwhitespace (model.            AccountName) {modelstate.addmodelerror (WorkContext.ShopConfig.ShadowName, "account name cannot be empty"); } else if (model. Accountname.length < 4 | | Model. Accountname.length >) {modelstate.addmodelerror (WorkContext.ShopConfig.ShadowName, "account name must be            Greater than 3 and not greater than 50 characters "); } else if ((! Securehelper.issafesqlstring (model.            AccountName)) {Modelstate.addmodelerror (WorkContext.ShopConfig.ShadowName, "account name does not exist"); }//Verify the password if (string. Isnullorwhitespace (model.            Password) {modelstate.addmodelerror ("Password", "Password cannot be null"); } else if (model. Password.length < 4 | | Model. PassWord.            Length > +) {modelstate.addmodelerror ("password", "Password must be greater than 3 and not greater than 32 characters");            }//Verify the Verification code if (Commonhelper.isinarray (Workcontext.pagekey, WorkContext.ShopConfig.VerifyPages)) {if (string. Isnullorwhitespace (model.                Verifycode) {modelstate.addmodelerror ("Verifycode", "Verification code cannot be empty"); } else if (model. Verifycode.tolower ()! = sessions.getvaluestring (Workcontext.sid, "Verifycode")) {ModelS Tate.                Addmodelerror ("Verifycode", "Incorrect verification Code"); }}//When all of the above validations Pass, if (modelstate.isvalid) {if (Bspconfig.shopco Nfig. Logintype.contains ("2") && Validatehelper.isemail (model. AccountName)//Mailbox Login {Partuserinfo = Users.getpartuserbyemail (model.                    AccountName);                      if (Partuserinfo = = null)  Modelstate.addmodelerror (WorkContext.ShopConfig.ShadowName, "Mailbox does not exist"); } else if (BSPConfig.ShopConfig.LoginType.Contains ("3") && Validatehelper.ismobile (model. AccountName)//Mobile Login {partuserinfo = Users.getpartuserbymobile (model.                    AccountName);                if (Partuserinfo = = null) modelstate.addmodelerror (WorkContext.ShopConfig.ShadowName, "mobile phone does not exist");                    } else if (BSPConfig.ShopConfig.LoginType.Contains ("1"))//user name Login { Partuserinfo = Users.getpartuserbyname (model.                    AccountName);                if (Partuserinfo = = null) modelstate.addmodelerror (WorkContext.ShopConfig.ShadowName, "User name does not exist"); }//Determine if the password is correct if (partuserinfo! = null && Users.createuserpassword (mode L.password, partuserinfo.salt)! = Partuserinfo.password) {LoGinfaillogs.addloginfailtimes (Workcontext.ip, DateTime.Now);//Increase the number of login failures Modelstate.addmodelerror ("Passwo                Rd "," Password is incorrect ");        }} return partuserinfo; }

With the code above you can see that all the validations are done manually.

The second part of the checksum is the verification information display, In MVC, we often use methods such as html.validationmessagefor to display authentication information, so in order to ensure that the above method can be used normally, we need to add all the authentication information to Modelstate (because html.validationmessagefor The essence of a method implementation is to determine whether to display and display content by modelstate the contents of the specified key value. Here we have the checksum data, and the rest is shown in the view. About showing that we can still use html.validationmessagefor or something like that, if you want more flexibility, you can use the "getverifyerrorlist" method of the View page, which in our newly defined visual EOG class, Its function is to build officer information into a JSON object. The code is as follows:

        <summary>///        Get validation Error List///</summary>//        <returns></returns>        Public Mvchtmlstring getverifyerrorlist ()        {            modelstatedictionary modelstate = (Controller) (this. Viewcontext.controller)). Modelstate;            if (modelstate = = NULL | | modelstate.count = = 0)                return new mvchtmlstring ("null");            StringBuilder errorlist = new StringBuilder ("[");            foreach (keyvaluepair<string, modelstate> item in modelstate)            {                Errorlist.appendformat ("{0} ' key ': ' { 1} ', ' msg ': ' {2} ' {3}, ', ' {', item. Key, item. Value.errors[0]. ErrorMessage, "}");            }            Errorlist.remove (errorlist.length-1, 1);            Errorlist.append ("]");            return new Mvchtmlstring (errorlist.tostring ());        }

Here is an example of using the code to log in to the view:

   Script code    <script type= "Text/javascript" >        var verifyerrorlist= @GetVerifyErrorList ();         $ (function () {             if (verifyerrorlist! = null) {for                 (var i = 0; i < verifyerrorlist.length; i++) {                    $ ("#" +verify errorlist[i].key+ "Error"). html (VERIFYERRORLIST[I].MSG)}}         )    </script>    //html Code   <tr>      <td> password:</td>      <td>              <input type= "password" name= "password" id= "Password" value= "@Model. Password"/>      </td>      <td><span style= "color:red;" id= " Passworderror "></span></td>    </tr>

By implementing the above, we ensure that the framework is compatible with all MVC functions and provides ample space for advanced developers. PS: There is a colleague in the team who once removed the code for model binding and model validation in ASP., and the perfect running instance, the performance and cost of a lot less, interested friends can go try!

Brnshop Open Source Online store Second: ASP. NET MVC Framework

Related Article

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: 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.