主頁 > .NET開發 > ASP.NET Core管道詳解[3]: Pipeline = IServer + IHttpApplication

ASP.NET Core管道詳解[3]: Pipeline = IServer + IHttpApplication

2020-11-24 21:58:35 .NET開發

ASP.NET Core的請求處理管道由一個服務器和一組中間件構成,但對于面向傳輸層的服務器來說,它其實沒有中間件的概念,當服務器接收到請求之后,會將該請求分發給一個處理器進行處理,對服務器而言,這個處理器就是一個HTTP應用,此應用通過IHttpApplication<TContext>介面來表示,由于服務器是通過IServer介面表示的,所以可以將ASP.NET Core框架的核心視為由IServer和IHttpApplication<TContext>物件組成的管道,[本文節選自《ASP.NET Core 3框架揭秘》第13章, 更多關于ASP.NET Core的文章請點這里]

2

目錄
一、服務器(IServer)
二、承載應用( HostingApplication)
三、應用生命周期和請求日志
     ILogger日志
     DiagnosticSource診斷日志
     EventSource事件日志

一、服務器(IServer)

由于服務器是整個請求處理管道的“龍頭”,所以啟動和關閉應用的最終目的是啟動和關閉服務器,ASP.NET Core框架中的服務器通過IServer介面來表示,該介面具有如下所示的3個成員,其中由服務器提供的特性就保存在其Features屬性表示的IFeatureCollection集合中,IServer介面的StartAsync<TContext>方法與StopAsync方法分別用來啟動和關閉服務器,

public interface IServer : IDisposable
{
    IFeatureCollection Features { get; }

    Task StartAsync<TContext>(IHttpApplication<TContext> application, CancellationToken cancellationToken);
    Task StopAsync(CancellationToken cancellationToken);
}

服務器在開始監聽請求之前總是系結一個或者多個監聽地址,這個地址是應用程式從外部指定的,具體來說,應用程式指定的監聽地址會封裝成一個特性,并且在服務器啟動之前被添加到它的特性集合中,這個承載了監聽地址串列的特性通過如下所示的IServerAddressesFeature介面來表示,該介面除了有一個表示地址串列的Addresses屬性,還有一個布爾型別的PreferHostingUrls屬性,該屬性表示如果監聽地址同時設定到承載系統配置和服務器上,是否優先考慮使用前者,

public interface IServerAddressesFeature
{
    ICollection<string> Addresses { get; }
    bool PreferHostingUrls { get; set; }
}

正如前面所說,服務器將用來處理由它接收請求的處理器會被視為一個通過IHttpApplication<TContext>介面表示的應用,所以可以將ASP.NET Core的請求處理管道視為IServer物件和IHttpApplication<TContext>物件的組合,當呼叫IServer物件的StartAsync<TContext>方法啟動服務器時,我們需要提供這個用來處理請求的IHttpApplication<TContext>物件,IHttpApplication<TContext>采用基于背景關系的請求處理方式,泛型引數TContext代表的就是背景關系的型別,在IHttpApplication<TContext>處理請求之前,它需要先創建一個背景關系物件,該背景關系會在請求處理結束之后被釋放,背景關系的創建、釋放和自身對請求的處理實作在該介面3個對應的方法(CreateContext、DisposeContext和ProcessRequestAsync)中,

public interface IHttpApplication<TContext>
{
    TContext CreateContext(IFeatureCollection contextFeatures);
    void DisposeContext(TContext context, Exception exception); 
    Task ProcessRequestAsync(TContext context);
}

二、承載應用( HostingApplication)

ASP.NET Core框架利用如下所示的HostingApplication型別作為IHttpApplication<TContext>介面的默認實作,它使用一個內嵌的Context型別來表示處理請求的背景關系,一個Context物件是對一個HttpContext物件的封裝,同時承載了一些與診斷相關的資訊,

public class HostingApplication : IHttpApplication<HostingApplication.Context>
{
    ...
    public struct Context
    {
        public HttpContext HttpContext { get; set; }

        public IDisposable Scope { get; set; }
        public long StartTimestamp { get; set; }
        public bool EventLogEnabled { get; set; }
        public Activity Activity { get; set; }
    }
}

HostingApplication物件會在開始和完成請求處理,以及在請求程序中出現例外時發出一些診斷日志事件,具體來說,HostingApplication物件會采用3種不同的診斷日志形式,包括基于DiagnosticSource和EventSource的診斷日志以及基于 .NET Core日志系統的日志,Context除HttpContext外的其他屬性都與診斷日志有關,具體來說,Context的Scope是為ILogger創建的針對當前請求的日志范圍(第9章有對日志范圍的詳細介紹),此日志范圍會攜帶唯一標識每個請求的ID,如果注冊ILoggerProvider提供的ILogger支持日志范圍,它可以將這個請求ID記錄下來,那么我們就可以利用這個ID將針對同一請求的多條日志訊息組織起來做針對性分析,

HostingApplication物件會在請求結束之后記錄當前請求處理的耗時,所以它在開始處理請求時就會記錄當前的時間戳,Context的StartTimestamp屬性表示開始處理請求的時間戳,它的EventLogEnabled屬性表示針對EventSource的事件日志是否開啟,而Activity屬性則與針對DiagnosticSource的診斷日志有關,Activity代表基于當前請求處理的活動,

雖然ASP.NET Core應用的請求處理完全由HostingApplication物件負責,但是該型別的實作邏輯其實是很簡單的,因為它將具體的請求處理分發給一個RequestDelegate物件,該物件表示的正是所有注冊中間件組成的委托鏈,在創建HostingApplication物件時除了需要提供RequestDelegate物件,還需要提供用于創建HttpContext背景關系的IHttpContextFactory物件,以及與診斷日志有關的ILogger物件和DiagnosticListener物件,它們被用來創建上面提到過的HostingApplicationDiagnostics物件,

public class HostingApplication : IHttpApplication<HostingApplication.Context>
{
    private readonly RequestDelegate _application;
    private HostingApplicationDiagnostics _diagnostics;
    private readonly IHttpContextFactory _httpContextFactory;

    public HostingApplication(RequestDelegate application, ILogger logger,DiagnosticListener diagnosticSource, IHttpContextFactory httpContextFactory)
    {
        _application = application;
        _diagnostics = new HostingApplicationDiagnostics(logger, diagnosticSource);
        _httpContextFactory = httpContextFactory;
    }

    public Context CreateContext(IFeatureCollection contextFeatures)
    {
        var context = new Context();
        var httpContext = _httpContextFactory.Create(contextFeatures);
        _diagnostics.BeginRequest(httpContext, ref context);
        context.HttpContext = httpContext;
        return context;
    }

    public Task ProcessRequestAsync(Context context) => _application(context.HttpContext);

    public void DisposeContext(Context context, Exception exception)
    {
        var httpContext = context.HttpContext;
        _diagnostics.RequestEnd(httpContext, exception, context);
        _httpContextFactory.Dispose(httpContext);
        _diagnostics.ContextDisposed(context);
    }
}

如上面的代碼片段所示,當CreateContext方法被呼叫時,HostingApplication物件會利用IHttpContextFactory工廠創建出當前HttpContext背景關系,并進一步將它封裝成一個Context物件,在回傳這個Context物件之前,它會呼叫HostingApplicationDiagnostics物件的BeginRequest方法記錄相應的診斷日志,用來真正處理當前請求的ProcessRequestAsync方法比較簡單,只需要呼叫代表中間件委托鏈的RequestDelegate物件即可,

對于用來釋放背景關系的DisposeContext方法來說,它會利用IHttpContextFactory物件的Dispose方法來釋放創建的HttpContext物件,換句話說,HttpContext背景關系的生命周期是由HostingApplication物件控制的,完成針對HttpContext背景關系的釋放之后,HostingApplication物件會利用HostingApplicationDiagnostics物件記錄相應的診斷日志,Context的Scope屬性表示的日志范圍就是在呼叫HostingApplicationDiagnostics物件的ContextDisposed方法時釋放的,如果將HostingApplication物件引入ASP.NET Core的請求處理管道,那么完整的管道就體現為下圖所示的結構,

3

三、應用生命周期和請求日志

很多人可能對ASP.NET Core框架自身記錄的診斷日志并不關心,其實很多時候這些日志對糾錯排錯和性能監控提供了很有用的資訊,例如,假設需要創建一個APM(Application Performance Management)來監控ASP.NET Core處理請求的性能及出現的例外,那么我們完全可以將HostingApplication物件記錄的日志作為收集的原始資料,實際上,目前很多APM(如Elastic APM和SkyWalking APM等)針對ASP.NET Core應用的客戶端都是利用這種方式收集請求呼叫鏈資訊的,

ILogger日志

為了確定什么樣的資訊會被作為診斷日志記錄下來,下面介紹一個簡單的實體,將HostingApplication物件寫入的診斷日志輸出到控制臺上,前面提及,HostingApplication物件會將相同的診斷資訊以3種不同的方式進行記錄,其中包含日志系統,所以我們可以通過注冊對應ILoggerProvider物件的方式將日志內容寫入對應的輸出渠道,

整個演示實體如下面的代碼片段所示:首先通過呼叫IWebHostBuilder介面的ConfigureLogging方法注冊一個ConsoleLoggerProvider物件,并開啟針對日志范圍的支持,我們呼叫IApplicationBuilder介面的Run擴展方法注冊了一個中間件,該中間件在處理請求時會利用表示當前請求背景關系的HttpContext物件得到與之系結的IServiceProvider物件,并進一步從中提取出用于發送日志事件的ILogger<Program>物件,我們利用它寫入一條Information等級的日志,如果請求路徑為“/error”,那么該中間件會拋出一個InvalidOperationException型別的例外,

public class Program
{
    public static void Main()
    {
        Host.CreateDefaultBuilder()
            .ConfigureLogging(builder => builder.AddConsole(options => options.IncludeScopes = true))
            .ConfigureWebHostDefaults(builder => builder
                .Configure(app => app.Run(context =>
                {
                    var logger = context.RequestServices.GetRequiredService<ILogger<Program>>();
                    logger.LogInformation($"Log for event Foobar");
                    if (context.Request.Path == new PathString("/error"))
                    {
                        throw new InvalidOperationException("Manually throw exception.");
                    }
                    return Task.CompletedTask;
                })))
            .Build()
            .Run();
    }
}

在啟動程式之后,我們利用瀏覽器采用不同的路徑(“/foobar”和“/error”)向應用發送了兩次請求,演示程式的控制臺上呈現的輸出結果如下圖所示,由于我們開啟了日志范圍的支持,所以被ConsoleLogger記錄下來的日志都會攜帶日志范圍的資訊,日志范圍的唯一標識被稱為請求ID(Request ID),它由當前的連接ID和一個序列號組成,從圖13-4可以看出,兩次請求的ID分別是“0HLO4ON65ALGG:00000001”和“0HLO4ON65ALGG:00000002”,由于采用的是長連接,并且兩次請求共享同一個連接,所以它們具有相同的連接ID(“0HLO4ON65ALGG”),同一連接的多次請求將一個自增的序列號(“00000001”和“00000002”)作為唯一標識,

4

除了用于唯一表示每個請求的請求ID,日志范圍承載的資訊還包括請求指向的路徑,這也可以從圖13-4所示的輸出介面看出來,另外,上述請求ID實際上對應HttpContext型別的TraceIdentifier屬性,如果需要進行跨應用的呼叫鏈跟蹤,所有相關日志就可以通過共享TraceIdentifier屬性構建整個呼叫鏈,

public abstract class HttpContext
{ 
    public abstract string  TraceIdentifier { get; set; } 
    ...
}

對于兩次采用不同路徑的請求,控制臺共捕獲了7條日志,其中類別為App.Program的日志是應用程式自行寫入的,HostingApplication寫入日志的類別為“Microsoft.AspNetCore.Hosting.Diagnostics”,對于第一次請求的3條日志訊息,第一條是在HostingApplication開始處理請求時寫入的,我們利用這條日志獲知請求的HTTP版本(HTTP/1.1)、HTTP方法(GET)和請求URL,對于包含主體內容的請求,請求主體內容的媒體型別(Content-Type)和大小(Content-Length)也會一并記錄下來,當HostingApplication物件處理完請求后會寫入第三條日志,日志承載的資訊包括請求處理耗時(67.877 6毫秒)和回應狀態碼(200),如果回應具有主體內容,對應的媒體型別同樣會被記錄下來,

對于第二次請求,由于我們人為拋出了一個例外,所以例外的資訊被寫入日志,但是如果足夠仔細,就會發現這條等級為Error的日志并不是由HostingApplication物件寫入的,而是作為服務器的KestrelServer寫入的,因為該日志采用的類別為“Microsoft.AspNetCore.Server.Kestrel”,換句話說,HostingApplication物件利用ILogger記錄的日志中并不包含應用的例外資訊,

DiagnosticSource診斷日志

HostingApplication采用的3種日志形式還包括基于DiagnosticSource物件的診斷日志,所以我們可以通過注冊診斷監聽器來收集診斷資訊,如果通過這種方式獲取診斷資訊,就需要預先知道診斷日志事件的名稱和內容荷載的資料結構,通過查看HostingApplication型別的源代碼,我們會發現它針對“開始請求”、“結束請求”和“未處理例外”這3類診斷日志事件對應的名稱,具體如下,

  • 開始請求:Microsoft.AspNetCore.Hosting.BeginRequest,
  • 結束請求:Microsoft.AspNetCore.Hosting.EndRequest,
  • 未處理例外:Microsoft.AspNetCore.Hosting.UnhandledException,

至于針對診斷日志訊息的內容荷載(Payload)的結構,上述3類診斷事件具有兩個相同的成員,分別是表示當前請求背景關系的HttpContext和通過一個Int64整數表示的當前時間戳,對應的資料成員的名稱分別為httpContext和timestamp,對于未處理例外診斷事件,它承載的內容荷載還包括一個額外的成員,那就是表示拋出例外的Exception物件,對應的成員名稱為exception,

既然我們已經知道事件的名稱和診斷承載資料的成員,所以可以定義如下所示的DiagnosticCollector型別作為診斷監聽器(需要針對NuGet包“Microsoft.Extensions.DiagnosticAdapter”的參考),針對上述3類診斷事件,我們在DiagnosticCollector型別中定義了3個對應的方法,各個方法通過標注的DiagnosticNameAttribute特性設定了對應的診斷事件,我們根據診斷資料承載的結構定義了匹配的引數,所以DiagnosticSource物件寫入診斷日志提供的診斷資料將自動系結到對應的引數上,

public class DiagnosticCollector
{
    [DiagnosticName("Microsoft.AspNetCore.Hosting.BeginRequest")]
    public void OnRequestStart(HttpContext httpContext, long timestamp)
    {
        var request = httpContext.Request;
        Console.WriteLine($"\nRequest starting {request.Protocol} {request.Method} { request.Scheme}://{request.Host}{request.PathBase}{request.Path}");
        httpContext.Items["StartTimestamp"] = timestamp;
    }

    [DiagnosticName("Microsoft.AspNetCore.Hosting.EndRequest")]
    public void OnRequestEnd(HttpContext httpContext, long timestamp)
    {
        var startTimestamp = long.Parse(httpContext.Items["StartTimestamp"].ToString());
        var timestampToTicks = TimeSpan.TicksPerSecond / (double)Stopwatch.Frequency;
        var elapsed = new TimeSpan((long)(timestampToTicks * (timestamp - startTimestamp)));
        Console.WriteLine($"Request finished in {elapsed.TotalMilliseconds}ms { httpContext.Response.StatusCode}");
    }
    [DiagnosticName("Microsoft.AspNetCore.Hosting.UnhandledException")]
    public void OnException(HttpContext httpContext, long timestamp, Exception exception)
    {
        OnRequestEnd(httpContext, timestamp);
        Console.WriteLine($"{exception.Message}\nType:{exception.GetType()}\nStacktrace: { exception.StackTrace}");
    }
}

可以在針對“開始請求”診斷事件的OnRequestStart方法中輸出當前請求的HTTP版本、HTTP方法和URL,為了能夠計算整個請求處理的耗時,我們將當前時間戳保存在HttpContext背景關系的Items集合中,在針對“結束請求”診斷事件的OnRequestEnd方法中,我們將這個時間戳從HttpContext背景關系中提取出來,結合當前時間戳計算出請求處理耗時,該耗時和回應的狀態碼最侄訓被寫入控制臺,針對“未處理例外”診斷事件的OnException方法則在呼叫OnRequestEnd方法之后將例外的訊息、型別和跟蹤堆疊輸出到控制臺上,

如下面的代碼片段所示,在注冊的Startup型別中,我們在Configure方法注入DiagnosticListener服務,并呼叫它的SubscribeWithAdapter擴展方法將上述DiagnosticCollector物件注冊為診斷日志的訂閱者,與此同時,我們呼叫IApplicationBuilder介面的Run擴展方法注冊了一個中間件,該中間件會在請求路徑為“/error”的情況下拋出一個例外,

public class Program
{
    public static void Main()
    {
        Host.CreateDefaultBuilder()
            .ConfigureLogging(builder => builder.ClearProviders())
            .ConfigureWebHostDefaults(builder => builder.UseStartup<Startup>())
            .Build()
            .Run();
    }
}

public class Startup
{
    public void Configure(IApplicationBuilder app, DiagnosticListener listener)
    {
        listener.SubscribeWithAdapter(new DiagnosticCollector());
        app.Run(context =>
        {
            if (context.Request.Path == new PathString("/error"))
            {
                throw new InvalidOperationException("Manually throw exception.");
            }
            return Task.CompletedTask;
        });
    }
}

待演示實體正常啟動后,可以采用不同的路徑(“/foobar”和“/error”)對應用程式發送兩個請求,服務端控制臺會以圖13-5所示的形式輸出DiagnosticCollector物件收集的診斷資訊,如果我們試圖創建一個針對ASP.NET Core的APM框架來監控請求處理的性能和出現的例外,可以采用這樣的方案來收集原始的診斷資訊,

5

EventSource事件日志

除了上述兩種日志形式,HostingApplication物件針對每個請求的處理程序中還會利用EventSource物件發出相應的日志事件,除此之外,在啟動和關閉應用程式(實際上就是啟動和關閉IWebHost物件)時,同一個EventSource物件還會被使用,這個EventSource型別采用的名稱為Microsoft.AspNetCore.Hosting,上述5個日志事件對應的名稱如下,

  • 啟動應用程式:HostStart,
  • 開始處理請求:RequestStart,
  • 請求處理結束:RequestStop,
  • 未處理例外:UnhandledException,
  • 關閉應用程式:HostStop,

我們可以通過如下所示的實體來演示如何利用創建的EventListener物件來監聽上述5個日志事件,如下面的代碼片段所示,我們定義了派生于抽象類EventListener的DiagnosticCollector,在啟動應用前,我們創建了這個DiagnosticCollector物件,并通過注冊其EventSourceCreated事件開啟了針對目標名稱為Microsoft.AspNetCore.Hosting的EventSource的監聽,在注冊的EventWritten事件中,我們將監聽到的事件名稱的負載內容輸出到控制臺上,

public class Program
{
    private sealed class DiagnosticCollector : EventListener {}
    static void Main()
    {
        var listener = new DiagnosticCollector();
        listener.EventSourceCreated +=(sender, args) =>
        {
            if (args.EventSource.Name == "Microsoft.AspNetCore.Hosting")
            {
                listener.EnableEvents(args.EventSource, EventLevel.LogAlways);
            }
        };  
        listener.EventWritten += (sender, args) =>
        {
            Console.WriteLine(args.EventName);
            for (int index = 0; index < args.PayloadNames.Count; index++)
            {
                Console.WriteLine($"\t{args.PayloadNames[index]} = {args.Payload[index]}");
            }
        };

        Host.CreateDefaultBuilder()
            .ConfigureLogging(builder => builder.ClearProviders())
            .ConfigureWebHostDefaults(builder => builder
                .Configure(app => app.Run(context =>
                {
                    if (context.Request.Path == new PathString("/error"))
                    {
                        throw new InvalidOperationException("Manually throw exception.");
                    }
                    return Task.CompletedTask;
                })))
            .Build()
            .Run();
    }
}

以命令列的形式啟動這個演示程式后,從下圖所示的輸出結果可以看到名為HostStart的事件被發出,然后采用目標地址“http://localhost:5000/foobar”和“http:// http://localhost:5000/error”對應用程式發送兩個請求,從輸出結果可以看出,應用程式針對前者的處理程序會發出RequestStart事件和RequestStop事件,針對后者的處理則會因為拋出的例外發出額外的事件UnhandledException,輸入“Ctrl+C”關閉應用后,名稱為HostStop的事件被發出,對于通過EventSource發出的5個事件,只有RequestStart事件會將請求的HTTP方法(GET)和路徑(“/foobar”和“/error”)作為負載內容,其他事件都不會攜帶任何負載內容,

6

請求處理管道[1]: 模擬管道實作
請求處理管道[2]: HttpContext本質論
請求處理管道[3]: Pipeline = IServer +  IHttpApplication<TContext
請求處理管道[4]: 中間件委托鏈
請求處理管道[5]: 應用承載[上篇
請求處理管道[6]: 應用承載[下篇]

轉載請註明出處,本文鏈接:https://www.uj5u.com/net/227002.html

標籤:.NET技术

上一篇:C# 程式禁止重復啟動

下一篇:被 C# 的 ThreadStatic 標記的靜態變數,都存放在哪里了?

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • WebAPI簡介

    Web體系結構: 有三個核心:資源(resource),URL(統一資源識別符號)和表示 他們的關系是這樣的:一個資源由一個URL進行標識,HTTP客戶端使用URL定位資源,表示是從資源回傳資料,媒體型別是資源回傳的資料格式。 接下來我們說下HTTP. HTTP協議的系統是一種無狀態的方式,使用請求/ ......

    uj5u.com 2020-09-09 22:07:47 more
  • asp.net core 3.1 入口:Program.cs中的Main函式

    本文分析Program.cs 中Main()函式中代碼的運行順序分析asp.net core程式的啟動,重點不是剖析原始碼,而是理清程式開始時執行的順序。到呼叫了哪些實體,哪些法方。asp.net core 3.1 的程式入口在專案Program.cs檔案里,如下。ususing System; us ......

    uj5u.com 2020-09-09 22:07:49 more
  • asp.net網站作為websocket服務端的應用該如何寫

    最近被websocket的一個問題困擾了很久,有一個需求是在web網站中搭建websocket服務。客戶端通過網頁與服務器建立連接,然后服務器根據ip給客戶端網頁發送資訊。 其實,這個需求并不難,只是剛開始對websocket的內容不太了解。上網搜索了一下,有通過asp.net core 實作的、有 ......

    uj5u.com 2020-09-09 22:08:02 more
  • ASP.NET 開源匯入匯出庫Magicodes.IE Docker中使用

    Magicodes.IE在Docker中使用 更新歷史 2019.02.13 【Nuget】版本更新到2.0.2 【匯入】修復單列匯入的Bug,單元測驗“OneColumnImporter_Test”。問題見(https://github.com/dotnetcore/Magicodes.IE/is ......

    uj5u.com 2020-09-09 22:08:05 more
  • 在webform中使用ajax

    如果你用過Asp.net webform, 說明你也算是.NET 開發的老兵了。WEBform應該是2011 2013左右,當時還用visual studio 2005、 visual studio 2008。后來基本都用的是MVC。 如果是新開發的專案,估計沒人會用webform技術。但是有些舊版 ......

    uj5u.com 2020-09-09 22:08:50 more
  • iis添加asp.net網站,訪問提示:由于擴展配置問題而無法提供您請求的

    今天在iis服務器配置asp.net網站,遇到一個問題,記錄一下: 問題:由于擴展配置問題而無法提供您請求的頁面。如果該頁面是腳本,請添加處理程式。如果應下載檔案,請添加 MIME 映射。 WindowServer2012服務器,添加角色安裝完.netframework和iis之后,運行aspx頁面 ......

    uj5u.com 2020-09-09 22:10:00 more
  • WebAPI-處理架構

    帶著問題去思考,大家好! 問題1:HTTP請求和回傳相應的HTTP回應資訊之間發生了什么? 1:首先是最底層,托管層,位于WebAPI和底層HTTP堆疊之間 2:其次是 訊息處理程式管道層,這里比如日志和快取。OWIN的參考是將訊息處理程式管道的一些功能下移到堆疊下端的OWIN中間件了。 3:控制器處理 ......

    uj5u.com 2020-09-09 22:11:13 more
  • 微信門戶開發框架-使用指導說明書

    微信門戶應用管理系統,采用基于 MVC + Bootstrap + Ajax + Enterprise Library的技術路線,界面層采用Boostrap + Metronic組合的前端框架,資料訪問層支持Oracle、SQLServer、MySQL、PostgreSQL等資料庫。框架以MVC5,... ......

    uj5u.com 2020-09-09 22:15:18 more
  • WebAPI-HTTP編程模型

    帶著問題去思考,大家好!它是什么?它包含什么?它能干什么? 訊息 HTTP編程模型的核心就是訊息抽象,表示為:HttPRequestMessage,HttpResponseMessage.用于客戶端和服務端之間交換請求和回應訊息。 HttpMethod類包含了一組靜態屬性: private stat ......

    uj5u.com 2020-09-09 22:15:23 more
  • 部署WebApi隨筆

    一、跨域 NuGet參考Microsoft.AspNet.WebApi.Cors WebApiConfig.cs中配置: // Web API 配置和服務 config.EnableCors(new EnableCorsAttribute("*", "*", "*")); 二、清除默認回傳XML格式 ......

    uj5u.com 2020-09-09 22:15:48 more
最新发布
  • C#多執行緒學習(二) 如何操縱一個執行緒

    <a href="https://www.cnblogs.com/x-zhi/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/2943582/20220801082530.png" alt="" /></...

    uj5u.com 2023-04-19 09:17:20 more
  • C#多執行緒學習(二) 如何操縱一個執行緒

    C#多執行緒學習(二) 如何操縱一個執行緒 執行緒學習第一篇:C#多執行緒學習(一) 多執行緒的相關概念 下面我們就動手來創建一個執行緒,使用Thread類創建執行緒時,只需提供執行緒入口即可。(執行緒入口使程式知道該讓這個執行緒干什么事) 在C#中,執行緒入口是通過ThreadStart代理(delegate)來提供的 ......

    uj5u.com 2023-04-19 09:16:49 more
  • 記一次 .NET某醫療器械清洗系統 卡死分析

    <a href="https://www.cnblogs.com/huangxincheng/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/214741/20200614104537.png" alt="" /&g...

    uj5u.com 2023-04-18 08:39:04 more
  • 記一次 .NET某醫療器械清洗系統 卡死分析

    一:背景 1. 講故事 前段時間協助訓練營里的一位朋友分析了一個程式卡死的問題,回過頭來看這個案例比較經典,這篇稍微整理一下供后來者少踩坑吧。 二:WinDbg 分析 1. 為什么會卡死 因為是表單程式,理所當然就是看主執行緒此時正在做什么? 可以用 ~0s ; k 看一下便知。 0:000> k # ......

    uj5u.com 2023-04-18 08:33:10 more
  • SignalR, No Connection with that ID,IIS

    <a href="https://www.cnblogs.com/smartstar/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/u36196.jpg" alt="" /></a>...

    uj5u.com 2023-03-30 17:21:52 more
  • 一次對pool的誤用導致的.net頻繁gc的診斷分析

    <a href="https://www.cnblogs.com/dotnet-diagnostic/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/3115652/20230225090434.png" alt=""...

    uj5u.com 2023-03-28 10:15:33 more
  • 一次對pool的誤用導致的.net頻繁gc的診斷分析

    <a href="https://www.cnblogs.com/dotnet-diagnostic/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/3115652/20230225090434.png" alt=""...

    uj5u.com 2023-03-28 10:13:31 more
  • C#遍歷指定檔案夾中所有檔案的3種方法

    <a href="https://www.cnblogs.com/xbhp/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/957602/20230310105611.png" alt="" /></a&...

    uj5u.com 2023-03-27 14:46:55 more
  • C#/VB.NET:如何將PDF轉為PDF/A

    <a href="https://www.cnblogs.com/Carina-baby/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/2859233/20220427162558.png" alt="" />...

    uj5u.com 2023-03-27 14:46:35 more
  • 武裝你的WEBAPI-OData聚合查詢

    <a href="https://www.cnblogs.com/podolski/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/616093/20140323000327.png" alt="" /><...

    uj5u.com 2023-03-27 14:46:16 more