【轉載】ASP.Net請求處理機制初步探索之旅 - Part 3 管道

来源:https://www.cnblogs.com/yiting/archive/2019/06/22/11070445.html
-Advertisement-
Play Games

開篇:上一篇我們瞭解了一個ASP.Net頁面請求的核心處理入口,它經歷了三個重要的入口,分別是:ISAPIRuntime.ProcessRequest()、HttpRuntime.ProcessRequest()以及HttpApplication.Init()。其中,在HttpApplication ...


開篇:上一篇我們瞭解了一個ASP.Net頁面請求的核心處理入口,它經歷了三個重要的入口,分別是:ISAPIRuntime.ProcessRequest()、HttpRuntime.ProcessRequest()以及HttpApplication.Init()。其中,在HttpApplication的Init()方法中觸發了請求處理管道事件的執行,本篇我們就來看看所謂的請求處理管道。

(1)Part 1:前奏

(2)Part 2:核心

(3)Part 3:管道

(4)Part 4:WebForm頁面生命周期

(5)Part 5:MVC頁面聲命周期

一、所謂“請求處理管道”

  HttpApplication對象是ASP.NET中處理請求的重要對象,但是,這種類型的對象實例不是由程式員來創建的,而是由ASP.NET幫助我們創建的。為了便於擴展處理工作,HttpApplication採用處理管道的方法進行處理,將處理的過程分為多個步驟,每個步驟通過事件的形式暴露給程式員,這些事件按照固定的處理順序依次觸發,程式員通過編寫事件處理方法就可以自定義每一個請求的擴展處理過程。

①傳說中的19個事件

   對於HttpApplication來說,到ASP.NET 4.0版本,提供了19個重要的標準事件,如下圖所示:

  在整個請求處理管道中,HttpContext上下文被依次傳輸到各個處理事件中,由不同的處理單元(HttpModule、HttpHandler、Page等)進行處理。從這裡可以看出,ASP.NET請求處理管道就像是一個大型的AOP框架。

②HttpModule與HttpHandler

  在進一步深入瞭解之前,讓我們先來瞭解一下什麼是HttpModuleHttpHandlers。他們幫助我們在ASP.NET頁面處理過程的前後註入自定義的邏輯處理。他們之間主要的差別在於:

  • 如果你想要註入的邏輯是基於像'.aspx','.html'這樣的擴展文件,那麼你可以使用HttpHandler。換句話說,HttpHandler是一個基於處理器的擴展。
  • HttpHandler總結:在ASP.NET WebForm中,無論是一般處理程式還是WebPage都實現了IHttpHandler介面,而ASP.NET MVC中也有MvcHandler實現了IHttpHandler介面;
  • 如果你想要在ASP.NET管道事件中註入邏輯,那麼你可以使用HttpModule。也可以說,HttpModule是一個基於處理器的事件。
  • HttpModule總結:剛剛我們說到ASP.NET請求處理管道就像是一個大型的AOP框架,因此我們可以藉助HttpModule自定義地註冊或移除一些事件邏輯,以完成我們想要的效果。ASP.NET預設實現了針對WebForm和MVC的HttpModule,像ASP.NET MVC中預設使用的是UrlRoutingModule。具體實現方式是:通過改寫Global文件或自定義一個實現IHttpModule介面的類併在Web.config中進行註冊。
  • 複製代碼
    <?xml version="1.0"?>
    <configuration>
        <system.web>
            <httpModules>
                <add name="myHttpModule" type="FirstModule"/>
            </httpModules>
        </system.web>
    </configuration>
    複製代碼 複製代碼
        public class FirstModule : IHttpModule
        {
            public void Dispose()
            {
                throw new NotImplementedException();
            }
    
            public void Init(HttpApplication context)
            {
                context.BeginRequest += new EventHandler(context_BeginRequest);
            }
    
            void context_BeginRequest(object sender, EventArgs e)
            {
                HttpApplication application = sender as HttpApplication;
                application.Context.Response.Write("第三方過濾器:哇哈哈!");
            }
        }
    複製代碼

③19個事件中我們可以做些什麼?

  一個十分有價值的問題就是在什麼事件中我們又可以做些什麼?下表就展示了這個問題的答案:

Section Event Description
HttpModule BeginRequest 此事件標志著一個新的請求,它保證在每個請求中都會被觸發。
HttpModule AuthenticateRequest 此事件標誌ASP.NET運行時準備驗證用戶。任何身份驗證代碼都可以在此註入。
HttpModule AuthorizeRequest 此事件標誌ASP.NET運行時準備授權用戶。任何授權代碼都可以在此註入。
HttpModule ResolveRequest 在ASP.NET中我們通常使用OutputCache指令做緩存。在這個事件中,ASP.NET運行時確定是否能夠從緩存中載入頁面,而不是從頭開始生成。任何緩存的具體活動可以被註入這裡。
HttpModule AcquireRequestState 此事件標志著ASP.NET運行時準備獲得Session會話變數。可以對Session變數做任何你想要做的處理。
HttpModule PreRequestHandlerExecute 恰好在ASP.NET 開始執行事件處理程式前發生。可以預處理你想做的事。
HttpHandler ProcessRequest HttpHandler邏輯被執行。在這個部分我們將為每個頁面擴展寫需要的邏輯。
Page Init 此事件發生在ASP.NET頁面且可以用來: 
1、動態地創建控制項,如果你一定要在運行時創建控制項; 
2、任何初始化設置 
3、母版頁及其設置 
在這部分中我們沒有獲得viewstate、postedvalues及已經初始化的控制項。
Page Load 在這部分ASP.NET控制項完全被載入且在這裡你可以寫UI操作邏輯或任何其他邏輯。NOTE:這個事件也是我們最常見且最常用的一個事件。
Page Validate 如果在頁面上你有驗證器,你同樣想在這裡做一下檢查。
Page Render 是時候將輸出發送到瀏覽器。如果你想對最終的HTML做些修改,你可以在這裡輸入你的HTML邏輯。
Page Unload 頁面對象從記憶體中卸載。
HttpModule PostRequestHandlerExecute 可以註入任何你想要的邏輯,在處理程式執行之後。
HttpModule ReleaseRequestState 如果你想要保存對某些狀態變數的更改,例如:Session變數的值。
HttpModule UpdateRequestCache 在結束之前,你是否想要更新你的緩存。
HttpModule EndRequest 這是將輸出發送到客戶端瀏覽器之前的最後一個階段。

④自定義處理邏輯

  我們可以通過一個示常式序代碼來展示以上介紹的那些事件是怎樣被最終觸發的。在這個示例中,我們已經創建了一個HttpModule和HttpHandler,並且也在所有的事件中通過添加自定義邏輯代碼展示了一個簡單的響應。

  下麵是HttpModule類,它跟蹤了所有的事件並將其添加到了一個全局的集合中。

public class clsHttpModule : IHttpModule
{
    ...... 
    void OnUpdateRequestCache(object sender, EventArgs a)
    {
        objArrayList.Add("httpModule:OnUpdateRequestCache");
    }
    void OnReleaseRequestState(object sender, EventArgs a)
    {
        objArrayList.Add("httpModule:OnReleaseRequestState");
    }
    void OnPostRequestHandlerExecute(object sender, EventArgs a)
    {     objArrayList.Add("httpModule:OnPostRequestHandlerExecute");
    }
    void OnPreRequestHandlerExecute(object sender, EventArgs a)
    {  objArrayList.Add("httpModule:OnPreRequestHandlerExecute");
    }
    void OnAcquireRequestState(object sender, EventArgs a)
    {
        objArrayList.Add("httpModule:OnAcquireRequestState");
    }
    void OnResolveRequestCache(object sender, EventArgs a)
    {
        objArrayList.Add("httpModule:OnResolveRequestCache");
    }
    void OnAuthorization(object sender, EventArgs a)
    {
        objArrayList.Add("httpModule:OnAuthorization");
    }
    void OnAuthentication(object sender, EventArgs a)
    {
        objArrayList.Add("httpModule:AuthenticateRequest");
    }
    void OnBeginrequest(object sender, EventArgs a)
    {
        objArrayList.Add("httpModule:BeginRequest");
    }
    void OnEndRequest(object sender, EventArgs a)
    {
        objArrayList.Add("httpModule:EndRequest");
        objArrayList.Add("<hr>");
        foreach (string str in objArrayList)
        {
            httpApp.Context.Response.Write(str + "<br>") ;
        }
    } 
}            
View Code

  下麵是HttpHandler類的一個代碼片段,它跟蹤了ProcessRequest事件。

public class clsHttpHandler : IHttpHandler
{
    public void ProcessRequest(HttpContext context)
    {             clsHttpModule.objArrayList.Add("HttpHandler:ProcessRequest");
        context.Response.Redirect("Default.aspx");
    }
}
View Code

   同上,我們也可以跟蹤來自ASP.NET Page頁面的所有事件。

public partial class _Default : System.Web.UI.Page 
{
    protected void Page_init(object sender, EventArgs e)
    {
        clsHttpModule.objArrayList.Add("Page:Init");
    }

    protected void Page_Load(object sender, EventArgs e)
    {
        clsHttpModule.objArrayList.Add("Page:Load");
    }

    public override void Validate() 
    {
        clsHttpModule.objArrayList.Add("Page:Validate");
    }

    protected void Button1_Click(object sender, EventArgs e)
    {
        clsHttpModule.objArrayList.Add("Page:Event");
    }

    protected override void Render(HtmlTextWriter output) 
    {
        clsHttpModule.objArrayList.Add("Page:Render");
        base.Render(output);
    }

    protected void Page_Unload(object sender, EventArgs e)
    {
        clsHttpModule.objArrayList.Add("Page:UnLoad");
    }
}    
View Code

  下圖則顯示了上面我們所討論的所有事件的執行順序:

二、WebForm經歷的管道事件概覽

  在ASP.NET WebForm應用中,其在請求處理管道中主要經歷了三個重要階段:

①在第八個事件中創建Page類對象並轉換為IHttpHandler介面

  從上面的介紹中可以看到,第八個事件是:PostMapRequestHandler。在這個事件中,對於訪問不同的資源類型,ASP.NET具有不同的HttpHandler對其進程處理。對於每個請求,ASP.NET會通過擴展名選擇匹配相應的HttpHandler類型,成功匹配後,該實現被觸發。因此,如果請求的擴展名是.aspx,便會生成Page類對象,而Page類對象是實現了IHttpHandler介面的。

②在第九個到第十事件之間根據SessionId獲取Session

sessionId

  從上面的介紹中可以看到,第九到第十個事件是:AcquireRequestStatePostAcquireRequestState。這期間首先會接收到瀏覽器發過來的SessionId,然後先會將IHttpHandler介面嘗試轉換為IRequiresSessionState介面,如果轉換成功,ASP.NET會根據這個SessionId到伺服器的Session池中去查找所對應的Session對象,並將這個Session對象賦值到HttpContext對象的Session屬性。如果嘗試轉換為IRequiresSessionState介面不成功,則不載入Session。

sessionstate

③在第十一個事件與第十二個事件之間執行頁面生命周期

  從上面的介紹中可以看到,第十一和第十二個事件是:PreRequestHandlerExecutePostRequestHandlerExecute。在這兩個事件之間,ASP.NET最終通過請求資源類型相對應的HttpHandler實現對請求的處理,其實現方式是調用在第八個事件創建的頁面對象的ProcessRequest方法。

  在FrameworkInitialize()這個方法內部就開始打造WebForm的頁面控制項樹,在其中調用了ProcessRequestMain方法,在這個方法裡面就執行了整個ASP.NET WebFom頁面生命周期。至於WebForm頁面生命周期的細節,我們在本系列後續的Part 4再來細細研究。

當我們直接使用*.ashx頁面的時候,它的ProcessRequest()方法就直接調用了一個FrameworkInitialize(),並最終生成響應報文,發送回客戶端。

當我們在使用*.aspx頁面的時候,它繼承自Page類,而Page類實現了IHttpHandler介面,然後了調用Page類的ProcessRequest()方法,其中會構建頁面控制項樹,然後一個一個地去呈現。

三、ASP.NET MVC經歷的管道事件概覽

  在ASP.NET MVC中,最核心的當屬“路由系統”,而路由系統的核心則源於一個強大的System.Web.Routing.dll組件。

  在這個System.Web.Routing.dll中,有一個最重要的類叫做UrlRoutingModule,它是一個實現了IHttpModule介面的類,在請求處理管道中專門針對ASP.NET MVC請求進行處理。首先,我們要瞭解一下UrlRoutingModule是如何起作用的。

  (1)IIS網站的配置可以分為兩個塊:全局 Web.config 和本站 Web.config。Asp.Net Routing屬於全局性的,所以它配置在全局Web.Config 中,我們可以在如下路徑中找到:“$\Windows\Microsoft.NET\Framework\版本號\Config\Web.config“

複製代碼
 <?xml version="1.0" encoding="utf-8"?>
 <!-- the root web configuration file -->
 <configuration>
     <system.web>
         <httpModules>
             <add name="UrlRoutingModule-4.0" type="System.Web.Routing.UrlRoutingModule" />
         </httpModules>
    </system.web>
 </configuration>
複製代碼

  (2)通過在全局Web.Config中註冊 System.Web.Routing.UrlRoutingModule,IIS請求處理管道接到請求後,就會載入 UrlRoutingModule類型的Init()方法。其源碼入下:

public class UrlRoutingModule : IHttpModule
{
    // Fields
    private static readonly object _contextKey = new object();
    private static readonly object _requestDataKey = new object();
    private RouteCollection _routeCollection;
 
    // Methods
    protected virtual void Dispose()
    {
    }
 
    protected virtual void Init(HttpApplication application)
    {
        if (application.Context.Items[_contextKey] == null)
        {
            application.Context.Items[_contextKey] = _contextKey;
            // 這裡為UrlRoutingModule 註冊了一個PostResolveRequestCache 事件處理方法:OnApplicationPostResolveRequestCache().
            application.PostResolveRequestCache += new EventHandler(this.OnApplicationPostResolveRequestCache);
        }
    }
 
    private void OnApplicationPostResolveRequestCache(object sender, EventArgs e)
    {
        HttpContextBase context = new HttpContextWrapper(((HttpApplication) sender).Context);
        this.PostResolveRequestCache(context);
    }
 
    [Obsolete("This method is obsolete. Override the Init method to use the PostMapRequestHandler event.")]
    public virtual void PostMapRequestHandler(HttpContextBase context)
    {
    }
 
    public virtual void PostResolveRequestCache(HttpContextBase context)
    {
        RouteData routeData = this.RouteCollection.GetRouteData(context);
        if (routeData != null)
        {
            IRouteHandler routeHandler = routeData.RouteHandler;
            if (routeHandler == null)
            {
                throw new InvalidOperationException(string.Format(CultureInfo.CurrentUICulture, SR.GetString("UrlRoutingModule_NoRouteHandler"), new object[0]));
            }
            if (!(routeHandler is StopRoutingHandler))
            {
                RequestContext requestContext = new RequestContext(context, routeData);
                context.Request.RequestContext = requestContext;
                IHttpHandler httpHandler = routeHandler.GetHttpHandler(requestContext);
                if (httpHandler == null)
                {
                    throw new InvalidOperationException(string.Format(CultureInfo.CurrentUICulture, SR.GetString("UrlRoutingModule_NoHttpHandler"), new object[] { routeHandler.GetType() }));
                }
                if (httpHandler is UrlAuthFailureHandler)
                {
                    if (!FormsAuthenticationModule.FormsAuthRequired)
                    {
                        throw new HttpException(0x191, SR.GetString("Assess_Denied_Description3"));
                    }
                    UrlAuthorizationModule.ReportUrlAuthorizationFailure(HttpContext.Current, this);
                }
                else
                {
                    context.RemapHandler(httpHandler);
                }
            }
        }
    }
 
    void IHttpModule.Dispose()
    {
        this.Dispose();
    }
 
    void IHttpModule.Init(HttpApplication application)
    {
        this.Init(application);
    }
 
    // Properties
    public RouteCollection RouteCollection
    {
        get
        {
            if (this._routeCollection == null)
            {
                this._routeCollection = RouteTable.Routes;
            }
            return this._routeCollection;
        }
        set
        {
            this._routeCollection = value;
        }
    }
}
View Code

  從源碼中可以看出,在UrlRoutingModule中為請求處理管道中的第七個事件PostResolveRequestCache註冊了一個事件處理方法:OnApplicationPostResolveRequestCache。從這裡可以看出:ASP.NET MVC的入口在UrlRoutingModule,即訂閱了HttpApplication的第7個管道事件PostResolveRequestCahce。換句話說,是在HtttpApplication的第7個管道事件處對請求進行了攔截。

  現在我們將ASP.NET MVC的請求處理分為兩個重要階段來看看:

①在第七個事件中創建實現了IHttpHandler介面的MvcHandler

  當請求到達UrlRoutingModule的時候,UrlRoutingModule取出請求中的Controller、Action等RouteData信息,與路由表中的所有規則進行匹配,若匹配,把請求交給IRouteHandler,即MVCRouteHandler。我們可以看下UrlRoutingModule的源碼來看看,以下是幾句核心的代碼:

複製代碼
public virtual void PostResolveRequestCache(HttpContextBase context)
{
    // 通過RouteCollection的靜態方法GetRouteData獲取到封裝路由信息的RouteData實例
    RouteData routeData = this.RouteCollection.GetRouteData(context);
    if (routeData != null)
    {
        // 再從RouteData中獲取MVCRouteHandler
        IRouteHandler routeHandler = routeData.RouteHandler;
        ......
        if (!(routeHandler is StopRoutingHandler))
        {
            ......
            // 調用 IRouteHandler.GetHttpHandler(),獲取的IHttpHandler 類型實例,它是由 IRouteHandler.GetHttpHandler獲取的,這個得去MVC的源碼里看
            IHttpHandler httpHandler = routeHandler.GetHttpHandler(requestContext);
            ......
            // 合適條件下,把之前將獲取的IHttpHandler 類型實例 映射到IIS HTTP處理管道中
            context.RemapHandler(httpHandler);
        }
    }
}
複製代碼

  MVCRouteHandler的作用是用來生成實現IHttpHandler介面的MvcHandler

複製代碼
namespace System.Web.Routing
{  
    public interface IRouteHandler
    {       
        IHttpHandler GetHttpHandler(RequestContext requestContext);
    }
}
複製代碼

  那麼,MvcRouteHandler從何而來呢?眾所周知,ASP.NET MVC項目啟動是從Global中的Application_Start()方法開始的,那就去看看它:

複製代碼
    public class MvcApplication : System.Web.HttpApplication
    {
        protected void Application_Start()
        {
            ......
            //這裡要註冊路由了
            RouteConfig.RegisterRoutes(RouteTable.Routes);
            BundleConfig.RegisterBundles(BundleTable.Bundles);
        }
    }

    public class RouteConfig
    {
        public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
            // 玄機就在這了,這個MapRoute位於System.Web.Mvc.RouteCollectionExtensions
            routes.MapRoute(
                name: "Default",
                url: "{controller}/{action}/{id}",
                defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
            );
        }
    }    
複製代碼

   於是,我們再去看看Route類的這個MapRoute()方法的源碼:

複製代碼
        public static Route MapRoute(this RouteCollection routes, string name, string url, object defaults, object constraints, string[] namespaces) 
        {
            ......
       // 終於等到你,還好我沒放棄。 Route route
= new Route(url, new MvcRouteHandler()) { Defaults = new RouteValueDictionary(defaults), Constraints = new RouteValueDictionary(constraints), DataTokens = new RouteValueDictionary() }; ...... return route; }
複製代碼

  從上面的源碼可以得知為什麼可以從RouteData中拿到MvcRouteHadnler?因為當我們在HttpApplication的第一個管道事件,使用MapRoute()方法註冊路由的時候,已經通過Route類的構造函數把MvcRouteHandler註入到路由中了。

  剛剛我們知道MvcRouteHandler是用來生成實現IHttpHandler介面的MvcHandler,那麼我們繼續從UrlRoutingModule的源碼可以看到,通過HttpHandler的GetHttpHandler()方法獲取到了實現了IHttpHandler介面的MvcHandler

            // 調用 IRouteHandler.GetHttpHandler(),獲取的IHttpHandler 類型實例,它是由 IRouteHandler.GetHttpHandler獲取的,這個得去MVC的源碼里看
            IHttpHandler httpHandler = routeHandler.GetHttpHandler(requestContext);
            ......
            // 合適條件下,把之前將獲取的IHttpHandler 類型實例 映射到IIS HTTP處理管道中
            context.RemapHandler(httpHandler);

  於是,我們進入ASP.NET MVC的源碼看看MvcHandlerd的實現,這裡我看的是MVC 4.0的源碼:

        protected virtual IHttpHandler GetHttpHandler(RequestContext requestContext)
        {
          requestContext.HttpContext.SetSessionStateBehavior(GetSessionStateBehavior(requestContext));
            return new MvcHandler(requestContext);
        }

  可以看出,在這裡創建了MvcHandler實例。換句話說,MvcRouteHandler把請求交給了MvcHandler去做請求處理管道中後續事件的處理操作了。

②在第十一個事件與第十二個事件之間調用MvcHandler的ProcessRequest()方法

  (1)在WebForm中,此階段會調用Page類對象的ProcessRequest()方法。在ASP.NET MVC中,會調用MvcHandler的ProcessRequest()方法,此方法會激活具體請求的Controller類對象,觸發Action方法,返回ActionResult實例。

  (2)如果ActionResult是非ViewResult,比如JsonResult, ContentResult,這些內容將直接被輸送到Response響應流中,顯示給客戶端;如果是ViewResult,就會進入下一個渲染視圖環節。

  (3)在渲染視圖環節,ViewEngine找到需要被渲染的視圖,View被載入成WebViewPage<TModel>類型,並渲染生成Html,最終返回Html。

TIP:有關此ProcessRequest()處理環節的詳細內容,請等待本系列Part 5中的介紹。

參考資料

致謝:本文參閱了大量園友的文章,也直接使用了大量園友製作的圖,在此對以下各位園友表示感謝。

(1)Darren Ji,《ASP.NET MVC請求處理管道聲明周期的19個關鍵環節》:http://www.cnblogs.com/darrenji/p/3795661.html

(2)木宛城主,《ASP.NET那點不為人知的事兒》:http://www.cnblogs.com/OceanEyes/archive/2012/08/13/aspnetEssential-1.html

(3)Tony He,《ASP.NET請求處理機制》:http://www.cnblogs.com/cilence/archive/2012/05/28/2520712.html

(4)兩會的博客,《IIS是怎樣處理ASP.NET請求的》:http://www.cnblogs.com/hkncd/archive/2012/03/23/2413917.html

(5)wjn2000,《ASP.NET請求處理過程(IIS6)》:http://www.cnblogs.com/wjn2010/archive/2011/04/21/2024341.html

(6)農村出來的大學生,《ASP.NET網頁請求處理全過程(反編譯)》:http://www.cnblogs.com/poorpan/archive/2011/09/25/2190308.html

(7)碧血軒,《ASP.NET頁面生命周期》,http://www.cnblogs.com/xhwy/archive/2012/05/20/2510178.html

(8)吳秦,《ASP.NET 應用程式與頁面生命周期(意譯)》,http://www.cnblogs.com/skynet/archive/2010/04/29/1724020.html

(9)我自己,《【翻譯】ASP.NET應用程式和頁面聲明周期》:http://www.cnblogs.com/edisonchou/p/3958305.html

(10)Shivprasad koirala,《ASP.NET Application and Page Life Cycle》:http://www.codeproject.com/Articles/73728/ASP-NET-Application-and-Page-Life-Cycle

(11)學而不思則罔,《ASP.NET Routing與MVC之一:請求如何到達MVC》:http://www.cnblogs.com/acejason/p/3869731.html

(12)初心不可忘,《綜述:ASP.NET MVC請求處理管道》:http://www.cnblogs.com/luguobin/archive/2013/03/15/2962458.html

 

作者:周旭龍

出處:http://edisonchou.cnblogs.com/


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 大家都知道cookie是存在客戶端,session存在伺服器端。那麼客戶端具體是怎樣獲取cookie信息的呢? 原文: http://www.hellomao.top/2019/06/22/two_web_cookie/   作者: 小friend 實驗環境 實驗環境:xampp ...
  • 1、JLabel 標簽 構造函數: JLabel() JLabel(String text) JLabel(String text,int align) //第二個參數設置文本的對齊方式,常量,比如SwingConstants.LEFT/RIGHT。 JLabel(Icon image) //顯示圖 ...
  • 先做個自我介紹,我13年考上一所很爛專科民辦的學校,學的是生物專業,具體的學校名稱我就不說出來獻醜了。13年我就輟學了,我在那樣的學校,一年學費要1萬多,但是根本沒有人學習,我實在看不到希望,我就退學了。 退學後我也迷茫,大專都沒有畢業,我真的不知道我能幹什麼,我在糾結著我能做什麼。所以輟學後我一段 ...
  • 一、插入排序的介紹 插入排序的工作方式非常像人們排序一手撲克牌一樣。開始時,我們的左手為空並且桌子上的牌面朝下。然後,我們每次從桌子上拿走一張牌並將它插入左手中正確的位置。為了找到一張牌的正確位置,我們從右到左將它與已在手中的每張牌進行比較,如下圖所示: 那插曲排序是如何藉助上面提到的思想來實現排序 ...
  • 看了劉江老師教程這麼多天,卧槽,我才發現他也曾躋身於行伍之間,interesting 劉老師這波講解很到位,告訴你如何編寫單例視圖的時候忽然告訴你,其實不用這麼麻煩,我們有通用視圖,那些總是要做相似的行為的視圖,咱們就寫一個好了,解放生產力不就是進步嗎? 好的廢話不說進入正題,先修改一波detail ...
  • 在C#的List集合中,如果要查找List集合是否包含某一個值或者對象,如果不使用List集合類的擴展方法的話一般會使用for迴圈或者foreach遍歷來查找,其實List集合類中的擴展方法Contain方法即可實現此功能,Contain方法的簽名為bool Contains(T item),ite ...
  • 在C#中的List集合操作過程中,有時候需要清空List集合中的元素對象,將之重置為一個初始化的List集合對象,此時就可以使用到List集合的擴展方法Clear()方法,此方法將清空List集合中所有的元素對象,清空後List集合中的元素個數為0。 例如有個List<int>的集合list1,內部 ...
  • 前言: 項目實戰中不論是業務編碼還是通用編碼,總會歸納出一些通用的工具類。放入項目中一勞永逸,讓兄弟姐妹們避免編寫重覆代碼。所以利用了工作之餘的時間,將這些散落在多個項目中精緻優雅的工具類,歸納起來形成工程,方便後續工作的使用和便捷開發。 根據實際需求,編寫了此工具。目前只支持SQLServer數據 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...