ASP.NET初始化流程分析2

来源:http://www.cnblogs.com/ssxg/archive/2017/06/27/7085877.html
-Advertisement-
Play Games

上一篇講了從創建應用程式域到創建ISAPIRuntime實例的過程,本篇繼續講Asp.net處理第一次請求的必要的初始化過程。 ISAPIRuntime分析 ISAPIRuntime在System.Web.Hosting中實現,它的ProcessRequest是我們處理web請求的入口。 註意方法的 ...


上一篇講了從創建應用程式域到創建ISAPIRuntime實例的過程,本篇繼續講Asp.net處理第一次請求的必要的初始化過程。

ISAPIRuntime分析

ISAPIRuntime在System.Web.Hosting中實現,它的ProcessRequest是我們處理web請求的入口。

    public int ProcessRequest(IntPtr ecb, int iWRType) {
        IntPtr pHttpCompletion = IntPtr.Zero;
        if (iWRType == WORKER_REQUEST_TYPE_IN_PROC_VERSION_2) {
            pHttpCompletion = ecb;
            ecb = UnsafeNativeMethods.GetEcb(pHttpCompletion);
        }
        ISAPIWorkerRequest wr = null;
        try {
            bool useOOP = (iWRType == WORKER_REQUEST_TYPE_OOP);
            wr = ISAPIWorkerRequest.CreateWorkerRequest(ecb, useOOP);
            wr.Initialize();
            String wrPath = wr.GetAppPathTranslated();
            String adPath = HttpRuntime.AppDomainAppPathInternal;            
            if (adPath == null ||StringUtil.EqualsIgnoreCase(wrPath, adPath)) {
                HttpRuntime.ProcessRequestNoDemand(wr);
                return 0;
            }
            else {
                 HttpRuntime.ShutdownAppDomain(ApplicationShutdownReason.PhysicalApplicationPathChanged, SR.GetString(SR.Hosting_Phys_Path_Changed, adPath, wrPath));
                 return 1;
            }
        }
        catch(Exception e) {
            try {
                WebBaseEvent.RaiseRuntimeError(e, this);
            } catch {}
            if (wr != null && wr.Ecb == IntPtr.Zero) {
                if (pHttpCompletion != IntPtr.Zero) {
                    UnsafeNativeMethods.SetDoneWithSessionCalled(pHttpCompletion);
                }
                if (e is ThreadAbortException) {
                    Thread.ResetAbort();
                }                   
                return 0;
             }
            throw;
        }
    }

註意方法的IntPtr類型的參數ecb, 它是一個非托管的指針,用於傳遞一些必須的數據,以及最終將Response的內容返回給非托管環境ISAPI(非同步方式),然後呈現給Client用戶。方法中調用ISAPIWorkerRequest的靜態方法CreateWorkerRequest而創建ISAPIWorkerRequest對象實例,參數分別為ecb和代表WorkerRequest類型的int參數iWRType,通過判斷ecb和type類型的具體內容,來決定創建什麼類型的WorkerRequest(上述類型的ISPAIWorkerRequest都繼承於HttpWorkerRequest),上面的代碼可以看出對不同版本的IIS進行了不同的包裝,通過其Initialize方法來初始化一些基本的信息(比如:contentType, querystring的長度,filepath等相關信息)。然後調用HttpRuntime.ProcessRequestNoDemand(wr)轉入HttpRuntime處理請求,最終體現在調用ProcessRequestInternal方法上。

HttpRuntime分析

Httpruntime在System.Web下實現,我們來看其處理請求的ProcessRequestInternal方法。

    private void ProcessRequestInternal(HttpWorkerRequest wr) {
        Interlocked.Increment(ref _activeRequestCount);
        if (_disposingHttpRuntime) {
            try {
                wr.SendStatus(503, "Server Too Busy");
                wr.SendKnownResponseHeader(HttpWorkerRequest.HeaderContentType, "text/html; charset=utf-8");
                byte[] body = Encoding.ASCII.GetBytes("<html><body>Server Too Busy</body></html>");
                wr.SendResponseFromMemory(body, body.Length);
                wr.FlushResponse(true);
                wr.EndOfRequest();
            } finally {
                Interlocked.Decrement(ref _activeRequestCount);
            }
            return;
        }
        HttpContext context;
        try {
            context = new HttpContext(wr, false);
        }
        catch {
            try {
                wr.SendStatus(400, "Bad Request");
                wr.SendKnownResponseHeader(HttpWorkerRequest.HeaderContentType, "text/html; charset=utf-8");
                byte[] body = Encoding.ASCII.GetBytes("<html><body>Bad Request</body></html>");
                wr.SendResponseFromMemory(body, body.Length);
                wr.FlushResponse(true);
                wr.EndOfRequest();
                return;
            } finally {
                Interlocked.Decrement(ref _activeRequestCount);
            }
        }
        wr.SetEndOfSendNotification(_asyncEndOfSendCallback, context);
        HostingEnvironment.IncrementBusyCount();
        try {
            try {
                EnsureFirstRequestInit(context);
            }
            catch {
                if (!context.Request.IsDebuggingRequest) {
                    throw;
                }
            }
            context.Response.InitResponseWriter();
            IHttpHandler app = HttpApplicationFactory.GetApplicationInstance(context);
            if (app == null)
                throw new HttpException(SR.GetString(SR.Unable_create_app_object));
            if (EtwTrace.IsTraceEnabled(EtwTraceLevel.Verbose, EtwTraceFlags.Infrastructure)) EtwTrace.Trace(EtwTraceType.ETW_TYPE_START_HANDLER, context.WorkerRequest, app.GetType().FullName, "Start");
            if (app is IHttpAsyncHandler) {
                IHttpAsyncHandler asyncHandler = (IHttpAsyncHandler)app;
                context.AsyncAppHandler = asyncHandler;
                asyncHandler.BeginProcessRequest(context, _handlerCompletionCallback, context);
            }
            else {
                app.ProcessRequest(context);
                FinishRequest(context.WorkerRequest, context, null);
            }
        }
        catch (Exception e) {
            context.Response.InitResponseWriter();
            FinishRequest(wr, context, e);
        }
    }

該方法中創建了熟悉的HttpContext並同時創建了HttpRequest與HttpResponse

internal HttpContext(HttpWorkerRequest wr, bool initResponseWriter) {

        _wr = wr;

        Init(new HttpRequest(wr, this), new HttpResponse(wr, this));

if (initResponseWriter)

            _response.InitResponseWriter();

        PerfCounters.IncrementCounter(AppPerfCounter.REQUESTS_EXECUTING);

    }

然後通過HttpApplicationFactory的GetApplicationInstance靜態方法,獲取我們熟悉的HttpApplication對象實例(註:HttpApplication對象是繼承IHttpAsyncHandler,而IHttpAsyncHandler又繼承於IHttpHandler),然後執行調用BeginProcessRequest方法。至此正式進入了HttpApplication對象的創建以及大家熟知的HttpApplication以後的生命周期了。

HttpApplicationFactory分析

HttpApplicationFactory在System.Web下實現。

查看HttpApplicationFactory用來創建Httpapplication的GetApplicationInstance方法。

    internal static IHttpHandler GetApplicationInstance(HttpContext context) {
        if (_customApplication != null)
            return _customApplication;
        if (context.Request.IsDebuggingRequest)
            return new HttpDebugHandler();
        _theApplicationFactory.EnsureInited();
        _theApplicationFactory.EnsureAppStartCalled(context);
        return _theApplicationFactory.GetNormalApplicationInstance(context);
    }

該方法有三個步驟:首先是EnsureInited,會檢查是否已經初始化,如果沒有會調用Init方法先獲取global.asax文件的完整路徑,然後調用CompileApplication()對global.asax進行編譯,Init方法如下。

    private void Init() {
        if (_customApplication != null)
            return;
        try {
            try {
                _appFilename = GetApplicationFile();
                CompileApplication();
             }
            finally {
                SetupChangesMonitor();
            }
        }
        catch {
            throw;
        }
}

然後是EnsureAppStartCalled方法如果未開始啟動會調用FireApplicationOnStart。

   private void EnsureAppStartCalled(HttpContext context) {
        if (!_appOnStartCalled) {
            lock (this) {
                if (!_appOnStartCalled) {
                    using (new DisposableHttpContextWrapper(context)) {
                        WebBaseEvent.RaiseSystemEvent(this, WebEventCodes.ApplicationStart);
                        FireApplicationOnStart(context);
                        }
                    _appOnStartCalled = true;
                }
            }
        }
    }

    private void FireApplicationOnStart(HttpContext context) {
        if (_onStartMethod != null) {
            HttpApplication app = GetSpecialApplicationInstance();
            app.ProcessSpecialRequest(context, _onStartMethod, _onStartParamCount, this, EventArgs.Empty, null);
            RecycleSpecialApplicationInstance(app);
        }
    }

這裡創建特定的HttpApplication實例,觸發ApplicationOnStart事件(會執行global.asax中的Application_Start方法)。然後在處理完事件以後就立即被回收掉,因為系統初始化只需要一次。

最後是GetNormalApplicationInstance,如果在有空閑的HttpApplication實例,就直接用,如果沒有就新創建,然後調用InitInternal方法進行初始化相關的內容,最後返回該HttpApplication實例。

    private HttpApplication GetNormalApplicationInstance(HttpContext context) {
        HttpApplication app = null;
        lock (_freeList) {
            if (_numFreeAppInstances > 0) {
                app = (HttpApplication)_freeList.Pop();
                _numFreeAppInstances--;
                if (_numFreeAppInstances < _minFreeAppInstances) {
                    _minFreeAppInstances = _numFreeAppInstances;
                }
            }
        }
        if (app == null) {
            app = (HttpApplication)HttpRuntime.CreateNonPublicInstance(_theApplicationType);
            using (new ApplicationImpersonationContext()) {
                app.InitInternal(context, _state, _eventHandlerMethods);
            }
        }
        ……
        return app;
    }

HttpApplication分析

HttpApplication在System.Web下實現,首先查看HttpApplication的InitInternal方法,該方法用於初始化。

    internal void InitInternal(HttpContext context, HttpApplicationState state, MethodInfo[] handlers) {
        Debug.Assert(context != null, "context != null");
        _state = state;
        PerfCounters.IncrementCounter(AppPerfCounter.PIPELINES);
        try {
            try {
                _initContext = context;
                _initContext.ApplicationInstance = this;
                context.ConfigurationPath = context.Request.ApplicationPathObject;
                using (new DisposableHttpContextWrapper(context)) {
                    if (HttpRuntime.UseIntegratedPipeline) {
                        Debug.Assert(_moduleConfigInfo != null, "_moduleConfigInfo != null");
                        Debug.Assert(_moduleConfigInfo.Count >= 0, "_moduleConfigInfo.Count >= 0");
                        try {
                            context.HideRequestResponse = true;
                            _hideRequestResponse = true;
                            InitIntegratedModules();
                        }
                        finally {
                            context.HideRequestResponse = false;
                            _hideRequestResponse = false;
                        }
                    }
                    else {
                        InitModules();
                        Debug.Assert(null == _moduleContainers, "null == _moduleContainers");
                    }
                    if (handlers != null)
                        HookupEventHandlersForApplicationAndModules(handlers);
                    _context = context;
                    if (HttpRuntime.UseIntegratedPipeline && _context != null) {
                        _context.HideRequestResponse = true;
                    }
                    _hideRequestResponse = true;
                    try {
                        Init();
                    }
                    catch (Exception e) {
                        RecordError(e);
                    }
                }
                if (HttpRuntime.UseIntegratedPipeline && _context != null) {
                    _context.HideRequestResponse = false;
                }
                _hideRequestResponse = false;
                _context = null;
                _resumeStepsWaitCallback= new WaitCallback(this.ResumeStepsWaitCallback);
                if (HttpRuntime.UseIntegratedPipeline) {
                    _stepManager = new PipelineStepManager(this);
                }
                else {
                    _stepManager = new ApplicationStepManager(this);
                }
                _stepManager.BuildSteps(_resumeStepsWaitCallback);
            }
            finally {
                _initInternalCompleted = true;
                context.ConfigurationPath = null;
                _initContext.ApplicationInstance = null;
                _initContext = null;
            }
        }
        catch {
            throw;
        }
    }

該代碼主要有2個功能,一個是初始化大家熟悉的HttpModules,一個是通過BuildSteps執行多個生命周期事件的處理函數。通過上面的代碼我們可以看出,每個功能都有一個特殊判斷,判斷IIS是否是IIS7的集成模式,如果是就有特殊的步驟,如果不是就走一般的步驟(兩者直接的差異分別是:經典模式初始化HttpModules的時候會從網站配置的Modules里讀取,集成模式會預載入CLR和大量Modules,比如載入伺服器上設置的HttpModules;另外在BuildSteps的時候, IIS7集成模式走的是自己特殊的流程)。

總結一下,InitInternal方法的主要功能如下:

InitModules():根據Web.Config的設置,載入相應的HttpModules。

InitIntegratedModules():會載入IIS7集成模式下在伺服器上設定的HttpModuels和Web.config里system.webserver下的HttpModuels。

HookupEventHandlersForAppplicationAndModules:綁定HttpApplication實例中相應的事件處理函數(在Global.asax中定義的事件處理函數)。

創建很多實現IExecutionStep介面的類的實例並添加到當前HttpApplication實例的_execSteps中(包括HttpModules中定義的周期事件處理函數和查找匹配的HttpHandler、執行HttpHandler的方法以及過濾輸出等特殊事件),等待回調時執行。從這裡我們可以看到HttpApplication是以非同步的方式處理請求, 對請求的很多處理工作都放入了_execStep等待回調時執行。

在 HttpApplication的事件如下形式定義:

    public event EventHandler BeginRequest {
        add { AddSyncEventHookup(EventBeginRequest, value, RequestNotification.BeginRequest); }
        remove { RemoveSyncEventHookup(EventBeginRequest, value, RequestNotification.BeginRequest); }
    }

所有的事件都是調用AddSyncEventHookup方法添加進去的,其中第一個參數是以Event+事件名稱的值。

    internal void AddSyncEventHookup(object key, Delegate handler, RequestNotification notification) {
        AddSyncEventHookup(key, handler, notification, false);
    }
private void AddSyncEventHookup(object key, Delegate handler, RequestNotification notification, bool isPostNotification) { ThrowIfEventBindingDisallowed(); Events.AddHandler(key, handler); if (IsContainerInitalizationAllowed) { PipelineModuleStepContainer container = GetModuleContainer(CurrentModuleCollectionKey); if (container != null) { SyncEventExecutionStep step = new SyncEventExecutionStep(this, (EventHandler)handler); container.AddEvent(notification, isPostNotification, step); } } }

經典模式下在初始化HttpModlue的時候通過調用Events.AddHandler方法,將事件添加到Events集合里,同時這個key就是Event+事件名稱,而集成模式下這些事件是添加到另外一個地方的(通過將事件hanlder包裝成SyncEventExecutionStep類型,然後調用container.AddEvent方法將事件添加到另外一個地方),也就是說if上面的Events集合是給經典模式用的,下麵的Container里的數據是給集成模式用的,這些事件是存放在HttpApplication的ModuleContainers屬性里,這個屬性的類型是PipelineModuleStepContainer[],個數就是HttpModules的個數,也就是說每個HttpModule在HttpApplication上添加的事件都放在各自的PipelineModuleStepContainer容器里。

    private PipelineModuleStepContainer[] ModuleContainers {
        get {
            if (_moduleContainers == null) {
                Debug.Assert(_moduleIndexMap != null && _moduleIndexMap.Count > 0, "_moduleIndexMap != null && _moduleIndexMap.Count > 0");
                _moduleContainers = new PipelineModuleStepContainer[_moduleIndexMap.Count];
                for (int i = 0; i < _moduleContainers.Length; i++) {
                    _moduleContainers[i] = new PipelineModuleStepContainer();
                }
            }
            return _moduleContainers;
        }
}

StepManager分析

集成模式和經典模式(或IIS6)使用的是不同的StepManager,這個類的BuildSteps方法就是為了創建有序的ExecutionStep,其中包括各種事件的事情以及其它在各時間周期之間穿插的操作,最主要的操作,大家以前就應該知道的,比如哪個周期可以判定使用哪個HttpHandler,以及在哪個周期內執行這個HttpHandler的BeginProcessRequest方法。StepManager的具體實現類(ApplicationStepManager、PipelineStepManager)和HttpApplication類在同一個文件中定義。

ApplicationStepManager的BuildSteps方法(用於經典模式)

    internal override void BuildSteps(WaitCallback stepCallback ) {
        ArrayList steps = new ArrayList();
        HttpApplication app = _application;
        bool urlMappingsEnabled = false;
        UrlMappingsSection urlMappings = RuntimeConfig.GetConfig().UrlMappings;
        urlMappingsEnabled = urlMappings.IsEnabled && ( urlMappings.UrlMappings.Count > 0 );
        steps.Add(new ValidateRequestExecutionStep(app));
        steps.Add(new ValidatePathExecutionStep(app));
        if (urlMappingsEnabled)
            steps.Add(new UrlMappingsExecutionStep(app)); // url mappings
        app.CreateEventExecutionSteps(HttpApplication.EventBeginRequest, steps);
        app.CreateEventExecutionSteps(HttpApplication.EventAuthenticateRequest, steps);
        app.CreateEventExecutionSteps(HttpApplication.EventDefaultAuthentication, steps);
        app.CreateEventExecutionSteps(HttpApplication.EventPostAuthenticateRequest, steps);
        app.CreateEventExecutionSteps(HttpApplication.EventAuthorizeRequest, steps);
        app.CreateEventExecutionSteps(HttpApplication.EventPostAuthorizeRequest, steps);
        app.CreateEventExecutionSteps(HttpApplication.EventResolveRequestCache, steps);
        app.CreateEventExecutionSteps(HttpApplication.EventPostResolveRequestCache, steps);
        steps.Add(new MapHandlerExecutionStep(app));     // map handler
        app.CreateEventExecutionSteps(HttpApplication.EventPostMapRequestHandler, steps);
        app.CreateEventExecutionSteps(HttpApplication.EventAcquireRequestState, steps);
        app.CreateEventExecutionSteps(HttpApplication.EventPostAcquireRequestState, steps);
        app.CreateEventExecutionSteps(HttpApplication.EventPreRequestHandlerExecute, steps);
        steps.Add(app.CreateImplicitAsyncPreloadExecutionStep());        
        steps.Add(new CallHandlerExecutionStep(app)); // execute handler       
        app.CreateEventExecutionSteps(HttpApplication.EventPostRequestHandlerExecute, steps);
        app.CreateEventExecutionSteps(HttpApplication.EventReleaseRequestState, steps);
        app.CreateEventExecutionSteps(HttpApplication.EventPostReleaseRequestState, steps);
        steps.Add(new CallFilterExecutionStep(app)); // filtering
        app.CreateEventExecutionSteps(HttpApplication.EventUpdateRequestCache, steps);
        app.CreateEventExecutionSteps(HttpApplication.EventPostUpdateRequestCache, steps);
        _endRequestStepIndex = steps.Count;
        app.CreateEventExecutionSteps(HttpApplication.EventEndRequest, steps);
        steps.Add(new NoopExecutionStep()); // the last is always there
        _execSteps = new IExecutionStep[steps.Count];
        steps.CopyTo(_execSteps);
        _resumeStepsWaitCallback = stepCallback;
    }

    private void CreateEventExecutionSteps(Object eventIndex, ArrayList steps) {
        AsyncAppEventHandler asyncHandler = AsyncEvents[eventIndex];
        if (asyncHandler != null) {
            asyncHandler.CreateExecutionSteps(this, steps);
        }
        EventHandler handler = (EventHandler)Events[eventIndex];
        if (handler != null) {
            Delegate[] handlers = handler.GetInvocationList();
            for (int i = 0; i < handlers.Length; i++)  {
                steps.Add(new SyncEventExecutionStep(this, (EventHandler)handlers[i]));
            }
         }
    }

這個方法的完整功能歸納總結有以下幾點:

對請求的Request進行驗證,ValidateRequestExecutionStep。

對請求的路徑進行安全檢查,禁止非法路徑訪問(ValidatePathExecutionStep)。 

如果設置了UrlMappings, 進行RewritePath(UrlMappingsExecutionStep)。

執行事件處理函數,比如將BeginRequest、AuthenticateRequest轉化成可執行ExecutionStep在正式調用時候執行。

在這多個個事件操作處理期間,根據不同的時機加了4個特殊的ExecutionStep。

MapHandlerExecutionStep:查找匹配的HttpHandler

CallHandlerExecutionStep:執行HttpHandler的BeginProcessRequest

CallFilterExecutionStep:調用Response.FilterOutput方法過濾輸出

NoopExecutionStep:空操作,留著以後擴展用

所有的ExecuteionStep都保存在ApplicationStepManager實例下的私有欄位_execSteps里,而HttpApplication的BeginProcessRequest方法最終會通過該實例的ResumeSteps方法來執行這些操作。

PipelineStepManager的BuildSteps(用於集成模式)

    internal override void BuildSteps(WaitCallback stepCallback) {
        Debug.Trace("PipelineRuntime", "BuildSteps");
        HttpApplication app = _application;
        IExecutionStep materializeStep = new MaterializeHandlerExecutionStep(app);
        app.AddEventMapping(ttpApplication.IMPLICIT_HANDLER,
                    RequestNotification.MapRequestHandler,
                    false, materializeStep);
        app.AddEventMapping(HttpApplication.IMPLICIT_HANDLER,
                    RequestNotification.ExecuteRequestHandler,
                    false, app.CreateImplicitAsyncPreloadExecutionStep());
        IExecutionStep handlerStep = new CallHandlerExecutionStep(app);
        app.AddEventMapping(HttpApplication.IMPLICIT_HANDLER,
                    RequestNotification.ExecuteRequestHandler,
                    false, handlerStep);
        IExecutionStep webSocketsStep = new TransitionToWebSocketsExecutionStep(app);
        app.AddEventMapping(HttpApplication.IMPLICIT_HANDLER,
                    RequestNotification.EndRequest,
                    true, webSocketsStep);
        IExecutionStep filterStep = new CallFilterExecutionStep(app);
        app.AddEventMapping(HttpApplication.IMPLICIT_FILTER_MODULE,
                    RequestNotification.UpdateRequestCache,
                    false, filterStep);
        app.AddEventMapping(HttpApplication.IMPLICIT_FILTER_MODULE,
                    RequestNotification.LogRequest,
                    false, filterStep);
        _resumeStepsWaitCallback = stepCallback;
    }

    private void AddEventMapping(string moduleName,RequestNotification requestNotification, bool isPostNotification,IExecutionStep step) {
        ......
        PipelineModuleStepContainer container = GetModuleContainer(moduleName);
        container.AddEvent(requestNotification, isPostNotification, step);
    }

以上代碼有2個地方和經典模式不相同:

集成模式沒有使用MapHandlerExecutionStep來裝載ExecutionStep(也就是查找對應的HttpHandler),而是通過MaterializeHandlerExecutionStep類來獲得HttpHandler,方式不一樣。

集成模式是通過HttpApplication的AddEventMapping方法來添加事件的,從而將事件加入到前面所說的ModuleContainers容器。

總結一下,在經典模式下,是用 Event+事件名稱做key將所有事件的保存在HttpApplication的Events屬性對象里,然後在BuildSteps里統一按照順序組裝,中間載入4個特殊的ExecutionStep,最後在統一執行;在集成模式下,是通過HttpModule名稱+RequestNotification枚舉值作為key將所有的事件保存在HttpApplication的ModuleContainers屬性對象里,然後也在BuildSteps里通過偽造HttpModule名稱載入那4個特殊的ExecutionStep,最後按照枚舉類型的順序,遍歷所有的HttpModule按順序來執行這些事件,可以自行編寫一個自定義的HttpModuel來執行這些事件看看效果如何。

下麵是總結一下處理第一次請求的大體處理流程。


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

-Advertisement-
Play Games
更多相關文章
  • 一、簡介 1、認識 加密網頁(https): tcp:443 明文網頁(http): tcp:80 survey.netcraft.net --這個網站上可以查到最新的網站伺服器的使用率 超文本傳輸協議(HTTP,HyperText Transfer Protocol)是互聯網上應用最為廣泛的一種網 ...
  • select系統調用的的用途是:在一段指定的時間內,監聽用戶感興趣的文件描述符上可讀、可寫和異常等事件。 select 機制的優勢 為什麼會出現select模型? 先看一下下麵的這句代碼: 這是用來接收數據的,在預設的阻塞模式下的套接字里,recv會阻塞在那裡,直到套接字連接上有數據可讀,把數據讀到 ...
  • 在ASP.NET MVC中來實現主題的切換一般有兩種方式,一種是通過切換皮膚的css和js引用,一種就是通過重寫視圖引擎。通過重寫視圖引擎的方式更加靈活,因為我不僅可以在不同主題下麵佈局和樣式不一樣,還可以讓不同的主題下麵顯示的數據條目不一致,就是說可以在某些主題下麵添加一下個性化的東西。 本篇我將 ...
  • 本文版權歸博客園和作者吳雙本人共同所有 轉載和爬蟲請註明原文地址 www.cnblogs.com/tdws 一.寫在前面 適配器模式(Adapter) 可用來在現有介面和不相容的類之間進行適配。有助於避免大規模改寫現有客戶代碼,其工作機制是對現有類的介面進行包裝,這樣客戶程式就能使用這個並非為其量身 ...
  • 寫在前面整個項目都托管在了 Github 上:https://github.com/ikesnowy/Algorithms-4th-Edition-in-Csharp這一節內容可能會用到的庫文件有 Geometry 和 Commercial,同樣在 Github 上可以找到。善用 Ctrl + F ... ...
  • 引言 Postman 是一個用來測試Web API的Chrome 外掛軟體,可由google store 免費取得並安裝於Chrome里,對於有在開發Web API的開發者相當有用,省掉不少寫測試頁面呼叫的工作,通常我們看到的使用情境多數是直接呼叫Web API而未隨著Request發送相關所需參數 ...
  • 一、Chart(Winform) 使用圖表控制項(chart)首先要理解圖表區域(ChartArea)、XY軸(AxisX、AxisY)、數據點(Series)、標題(Title)、圖例(Legend)這幾個之間的層次關係。 圖1:柱形圖 從圖1可以中內容,可以對Chart控制項的組成有了一個簡單的瞭解 ...
  • 古人雲:溫故而知新。這是極好的,近來,作為一個小白,利用點空閑時間把之前幾個月自己寫過的一個作為練手的一個OA系統又重新拿來溫習一番,希望在鞏固基礎之上能得到新的啟示。現在回想起來,之前一個人,寫寫停停,不覺感嘆,平時工作中團隊的重要性以及個人力量的渺小。因為是練手的項目,整個系統從資料庫到前端都是 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...