ASP.NET Core 運行原理解剖[4]:進入HttpContext的世界

来源:http://www.cnblogs.com/RainingNight/archive/2017/09/08/httpcontext-in-asp-net-core.html
-Advertisement-
Play Games

HttpContext是ASP.NET中的核心對象,每一個請求都會創建一個對應的HttpContext對象,我們的應用程式便是通過HttpContext對象來獲取請求信息,最終生成響應,寫回到HttpContext中,完成一次請求處理。在前面幾章中也都有提到HttpContext,本章就來一起探索一 ...


HttpContext是ASP.NET中的核心對象,每一個請求都會創建一個對應的HttpContext對象,我們的應用程式便是通過HttpContext對象來獲取請求信息,最終生成響應,寫回到HttpContext中,完成一次請求處理。在前面幾章中也都有提到HttpContext,本章就來一起探索一下HttpContext的世界,揭開它的神秘面紗。

目錄

本系列文章從源碼分析的角度來探索 ASP.NET Core 的運行原理,分為以下幾個章節:

ASP.NET Core 運行原理解剖[1]:Hosting

ASP.NET Core 運行原理解剖[2]:Hosting補充之配置介紹

ASP.NET Core 運行原理解剖[3]:Middleware-請求管道的構成

ASP.NET Core 運行原理解剖[4]:進入HttpContext的世界(Current)

  1. IHttpContextFactory
  2. IFeatureCollection
  3. HttpContext
  4. IHttpContextAccessor

ASP.NET Core 運行原理解剖[5]:Authentication(待續)

IHttpContextFactory

第一章中,我們介紹到,WebHost 在啟動 IServer 時,會傳入一個 IHttpApplication<TContext> 類型的對象,Server 負責對請求的監聽,在接收到請求時,會調用該對象的 ProcessRequestAsync 方法將請求轉交給我們的應用程式。IHttpApplication<TContext> 的預設實現為 HostingApplication ,有如下定義:

public class HostingApplication : IHttpApplication<HostingApplication.Context>
{
    private readonly RequestDelegate _application;
    private readonly IHttpContextFactory _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)
    {
        return _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);
    }
}

首先使用 IHttpContextFactory 來創建 HttpContext 實例,然後在 ProcessRequestAsync 方法中調用上一章介紹的 RequestDelegate,由此進入到我們的應用程式當中。

IHttpContextFactory 負責對 HttpContext 的創建和釋放,分別對應著CreateDispose方法,它的預設實現類為HttpContextFactory,定義如下:

public class HttpContextFactory : IHttpContextFactory
{
    private readonly IHttpContextAccessor _httpContextAccessor;
    private readonly FormOptions _formOptions;

    public HttpContext Create(IFeatureCollection featureCollection)
    {
        var httpContext = new DefaultHttpContext(featureCollection);
        if (_httpContextAccessor != null)
        {
            _httpContextAccessor.HttpContext = httpContext;
        }

        var formFeature = new FormFeature(httpContext.Request, _formOptions);
        featureCollection.Set<IFormFeature>(formFeature);

        return httpContext;
    }

    public void Dispose(HttpContext httpContext)
    {
        if (_httpContextAccessor != null)
        {
            _httpContextAccessor.HttpContext = null;
        }
    }
}

如上,HttpContextFactory 只是簡單的使用 new DefaultHttpContext(featureCollection) 來創建 HttpContext 的實例,而這裡涉及到一個 IFeatureCollection 對象,它是由 Server 根據原始請求創建而來的,下麵就先介紹一下該對象。

IFeatureCollection

不過,在介紹 IFeatureCollection 之前,我們先需先回顧一下OWIN:

OWIN是 “Open Web Server Interface for .NET” 的首字母縮寫,它定義了一套Web Server和Web Application之間的標準介面,主要用於解除 ASP.NET 與 IIS 的緊密耦合。為此,OWIN 定義了四個核心組件:Host, Server, Middleware, Application,併為Server和Middleware的之間的交互提供了一個 Func<IDictionary<string,object>,Task> 類型的標準介面。

每一個OWIN中間件,都會接收到一個 IDictionary<string,object> 類型的變數,用來表示當前請求的相關信息,也稱為環境字典。每一個支持OWIN標準的 Web Server 都會根據請求的原始上下文信息,封裝成這個環境字典,然後在OWIN中間件之間傳遞,進而完成整個請求的處理。環境字典定義了一系列預先約定好的Key,比如:用 "owin.RequestBody" 來表示請求體,"owin.RequestHeaders" 來表示請求頭,"owin.RequestMethod" 來表示請求方法等。

OWIN是隨著ASP.NET MVC5進行到我們的視線中,在當時,ASP.NET WebAPI 2.0 也基於OWIN實現了自寄宿模式。再後來,提出了 ASP.NET 5 與 MVC6,完全是基於OWIN的模式來開發的,再到今天的 ASP.NET Core,OWIN的概念已被模糊化了,但是還是隨處可以見到OWIN的影子,並且也提供了對 OWIN 的擴展支持。

在 ASP.NET Core 中,提出了 IFeatureCollection 的概念,它本質上也是一個 IDictionary<string,object> 鍵值對,但是它具有面向對象的特點,相對於 IDictionary<string,object> 更加清晰,容易理解,並且Server構建成這樣一個對象也很容易,它有如下定義:

public interface IFeatureCollection : IEnumerable<KeyValuePair<Type, object>>
{
    bool IsReadOnly { get; }

    int Revision { get; }

    object this[Type key] { get; set; }

    TFeature Get<TFeature>();

    void Set<TFeature>(TFeature instance);
}

它的定義非常簡單,由一系列以鍵值對來表示的標準特性對象(TFeature)組成,可以通過一個索引以及 GetSet 方法來獲取或設置這些特性對象。

下麵,我們看一下在 ASP.NET Core 中的對它的一個模擬實現:

public class FeatureCollection : IFeatureCollection
{
    private IDictionary<Type, object> _features;
    private readonly IFeatureCollection _defaults;
    private volatile int _containerRevision;

    public virtual int Revision
    {
        get { return _containerRevision + (_defaults?.Revision ?? 0); }
    }

    public object this[Type key]
    {
        get
        {
            object result;
            return _features != null && _features.TryGetValue(key, out result) ? result : _defaults?[key];
        }
        set
        {
            if (value == null)
            {
                if (_features != null && _features.Remove(key))
                {
                    _containerRevision++;
                }
                return;
            }

            if (_features == null)
            {
                _features = new Dictionary<Type, object>();
            }
            _features[key] = value;
            _containerRevision++;
        }
    }

    public TFeature Get<TFeature>()
    {
        return (TFeature)this[typeof(TFeature)];
    }

    public void Set<TFeature>(TFeature instance)
    {
        this[typeof(TFeature)] = instance;
    }    
}

如上,它的內部屬性 _features 便是OWIN中的標準環境字典,並且提供了更加方便的泛型 Get, Set 方法,以及一個索引器來訪問該環境字典。不過,如果只是這樣,那使用起來依然不夠方便,更為重要的是 ASP.NET Core 還提供了一系列的特性對象,並以這些特性對象的類型做為環境字典中的Key。

通過上面代碼,還可以發現,每次對該環境字典的修改,都會使 Revision 屬性遞增1。

這裡為什麼說FeatureCollection是一個模擬的實現呢?具我觀察,FeatureCollection對象只在ASP.NET Core的測試代碼中用到,而每個Server都有它自己的方式來構建IFeatureCollection,並不會使用FeatureCollection,關於Server中是如何創建IFeatureCollection實例的,可以參考KestrelHttpServer中的實現,這裡就不再深究。

那特性對象又是什麼呢?我們先看一下請求特性的定義:

public interface IHttpRequestFeature
{
    string Protocol { get; set; }
    string Scheme { get; set; }
    string Method { get; set; }
    string PathBase { get; set; }
    string Path { get; set; }
    string QueryString { get; set; }
    string RawTarget { get; set; }
    IHeaderDictionary Headers { get; set; }
    Stream Body { get; set; }
}

再看一下表單特性的定義:

public interface IFormFeature
{
    bool HasFormContentType { get; }
    IFormCollection Form { get; set; }
    IFormCollection ReadForm();
    Task<IFormCollection> ReadFormAsync(CancellationToken cancellationToken);
}

可以看到,這些特性對象與我們熟悉的 HttpContext 中的屬性非常相似,這也就大大簡化了在 IHttpRequestFeatureHttpContext 之間的轉換。我們可以通過這些特性介面定義的屬性來獲取到原始上下文中描述的信息,並通過特性對象提供的方法來操作原始上下文,它就像Web Server與我們的應用程式之間的橋梁,完成抽象和具體之間的轉換。

ASP.NET Core 提供了一系列豐富的特性對象,如 Session, Cookies, Query, Form, WebSocket, Request, Response 等等, 更詳細的列表可以查看 Microsoft.AspNetCore.Http.Features

HttpContext

HttpContext 對象我們應該都很熟悉了,它用來表示一個抽象的HTTP上下文,而HttpContext對象的核心又體現在用於描述請求的Request和描述響應的Response屬性上。除此之外,它還包含一些與當前請求相關的其他上下文信息,如描述當前HTTP連接的ConnectionInfo對象,控制WebSocket的WebSocketManager,代表當前用戶的ClaimsPrincipal對象的Session,等等:

public abstract class HttpContext
{
    public abstract IFeatureCollection Features { get; }
    public abstract HttpRequest Request { get; }
    public abstract HttpResponse Response { get; }
    public abstract ConnectionInfo Connection { get; }
    public abstract WebSocketManager WebSockets { get; }
    public abstract ClaimsPrincipal User { get; set; }
    public abstract IDictionary<object, object> Items { get; set; }
    public abstract IServiceProvider RequestServices { get; set; }
    public abstract CancellationToken RequestAborted { get; set; }
    public abstract string TraceIdentifier { get; set; }
    public abstract ISession Session { get; set; }
    public abstract void Abort();
}

在我們處理請求時,如果希望終止該請求,可以通過 RequestAborted 屬性給請求管道發送一個終止信息。當需要對整個管道共用一些與當前上下文相關的數據,可以將它保存在 Items 字典中。而在 ASP.NET Coer 1.x 中還包含一個管理認證的AuthenticationManager對象,但是在 2.0 中,將它移到了 AuthenticationHttpContextExtensions 中,因為用戶認證本來就一個相對複雜且獨立的模塊,把它獨立出去會更加符合 ASP.NET Core 的簡潔模塊化特性。

在上文中,我們瞭解到 HttpContext 的預設實現使用的是 DefaultHttpContext 類型 ,而 DefaultHttpContext 便是對上面介紹的 IFeatureCollection 對象的封裝:

public class DefaultHttpContext : HttpContext
{
    private FeatureReferences<FeatureInterfaces> _features;

    private HttpRequest _request;
    private HttpResponse _response;

    public DefaultHttpContext(IFeatureCollection features)
    {
        Initialize(features);
    }

    public virtual void Initialize(IFeatureCollection features)
    {
        _features = new FeatureReferences<FeatureInterfaces>(features);
        _request = InitializeHttpRequest();
        _response = InitializeHttpResponse();
    }

    protected virtual HttpRequest InitializeHttpRequest() => new DefaultHttpRequest(this);
}

如上,DefaultHttpContext通過 Initialize 來完成從 IFeatureCollection 到 HttpContext 的轉換,而各個屬性的轉換又交給了它們自己。

HttpRequest

HttpRequest 可以用來獲取到描述當前請求的各種相關信息,比如請求的協議(HTTP或者HTTPS)、HTTP方法、地址,以及該請求的請求頭,請求體等:

public abstract class HttpRequest
{
    public abstract HttpContext HttpContext { get; }
    public abstract string Method { get; set; }
    public abstract string Scheme { get; set; }
    public abstract bool IsHttps { get; set; }
    public abstract HostString Host { get; set; }
    public abstract PathString PathBase { get; set; }
    public abstract PathString Path { get; set; }
    public abstract QueryString QueryString { get; set; }
    public abstract IQueryCollection Query { get; set; }
    public abstract string Protocol { get; set; }
    public abstract IHeaderDictionary Headers { get; }
    public abstract IRequestCookieCollection Cookies { get; set; }
    public abstract long? ContentLength { get; set; }
    public abstract string ContentType { get; set; }
    public abstract Stream Body { get; set; }
    public abstract bool HasFormContentType { get; }
    public abstract IFormCollection Form { get; set; }
    public abstract Task<IFormCollection> ReadFormAsync(CancellationToken cancellationToken = new CancellationToken());
}

HttpRequest是一個抽象類,它的預設實現是DefaultHttpRequest:

public class DefaultHttpRequest : HttpRequest
{
    private readonly static Func<IFeatureCollection, IHttpRequestFeature> _nullRequestFeature = f => null;
    private FeatureReferences<FeatureInterfaces> _features;
    
    public DefaultHttpRequest(HttpContext context)
    {
        Initialize(context);
    }

    public virtual void Initialize(HttpContext context)
    {
        _context = context;
        _features = new FeatureReferences<FeatureInterfaces>(context.Features);
    }

    private IHttpRequestFeature HttpRequestFeature => _features.Fetch(ref _features.Cache.Request, _nullRequestFeature);

    public override string Method
    {
        get { return HttpRequestFeature.Method; }
        set { HttpRequestFeature.Method = value; }
    }

    public override Task<IFormCollection> ReadFormAsync(CancellationToken cancellationToken)
    {
        return FormFeature.ReadFormAsync(cancellationToken);
    }
}

在 DefaultHttpRequest 中,並沒有額外的功能,它只是簡單的與 IHttpRequestFeature 中的同名屬性和方法做了一個映射,而 IHttpRequestFeature 對象的獲取又涉及到一個 FeatureReferences<FeatureInterfaces> 類型, 從字面意思來說,就是對Feature對象的一個引用,用來保存對應的Feature實例,併在上文介紹的 Revision 屬性發生變化時,清空Feature實例的緩存:

public struct FeatureReferences<TCache>
{
    public IFeatureCollection Collection { get; private set; }
    public int Revision { get; private set; }

    public TCache Cache;

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public TFeature Fetch<TFeature, TState>(ref TFeature cached, TState state, Func<TState, TFeature> factory) where TFeature : class
    {
        var flush = false;
        var revision = Collection.Revision;
        if (Revision != revision)
        {
            cached = null;
            flush = true;
        }
        return cached ?? UpdateCached(ref cached, state, factory, revision, flush);
    }

    private TFeature UpdateCached<TFeature, TState>(ref TFeature cached, TState state, Func<TState, TFeature> factory, int revision, bool flush) where TFeature : class
    {
        if (flush)
        {
            Cache = default(TCache);
        }
        cached = Collection.Get<TFeature>();
        if (cached == null)
        {
            cached = factory(state);
            Collection.Set(cached);
            Revision = Collection.Revision;
        }
        else if (flush)
        {
            Revision = revision;
        }
        return cached;
    }

    public TFeature Fetch<TFeature>(ref TFeature cached, Func<IFeatureCollection, TFeature> factory)
        where TFeature : class => Fetch(ref cached, Collection, factory);
}

如上,當 Revision 生成變化時,會將 Cache 設置為 null , 然後重新從 IFeatureCollection 中獲取,最後更新 Revision 為最新版本,相當於一個緩存工廠。

Fetch方法使用了[MethodImpl(MethodImplOptions.AggressiveInlining)]特性,表示該方法會儘可能的使用內聯方式來執行。而內聯是一種很重要的優化方式, 它允許編譯器在方法調用開銷比方法本身更大的情況下消除對方法調用的開銷,即直接將該方法體嵌入到調用者中。

HttpResponse

在瞭解了表示請求的抽象類 HttpRequest 之後,我們再來認識一下與它對應的,用來描述響應的 HttpResponse 類型:

public abstract class HttpResponse
{
    private static readonly Func<object, Task> _callbackDelegate = callback => ((Func<Task>)callback)();
    private static readonly Func<object, Task> _disposeDelegate = disposable =>
    {
        ((IDisposable)disposable).Dispose();
        return Task.CompletedTask;
    };

    public abstract HttpContext HttpContext { get; }
    public abstract int StatusCode { get; set; }
    public abstract IHeaderDictionary Headers { get; }
    public abstract Stream Body { get; set; }
    public abstract long? ContentLength { get; set; }
    public abstract string ContentType { get; set; }
    public abstract IResponseCookies Cookies { get; }
    public abstract bool HasStarted { get; }
    public abstract void OnStarting(Func<object, Task> callback, object state);
    public virtual void OnStarting(Func<Task> callback) => OnStarting(_callbackDelegate, callback);
    public abstract void OnCompleted(Func<object, Task> callback, object state);
    public virtual void RegisterForDispose(IDisposable disposable) => OnCompleted(_disposeDelegate, disposable);
    public virtual void OnCompleted(Func<Task> callback) => OnCompleted(_callbackDelegate, callback);
    public virtual void Redirect(string location) => Redirect(location, permanent: false);
    public abstract void Redirect(string location, bool permanent);
}

HttpResponse也是一個抽象類,我們使用它來輸出對請求的響應,如設置HTTP狀態碼,Cookies,HTTP響應報文頭,響應主體等,以及提供了一些將響應發送到客戶端時的相關事件。

HasStarted 屬性用來表示響應是否已開始發往客戶端,在我們第一次調用 response.Body.WriteAsync 方法時,該屬性便會被設置為 True。需要註意的是,一旦 HasStarted 設置為 true 後,便不能再修改響應頭,否則將會拋出 InvalidOperationException 異常,也建議我們在HasStarted設置為true後,不要再對 Response 進行寫入,因為此時 content-length 的值已經確定,繼續寫入可能會造成協議衝突。

HttpResponse 的預設實現為 DefaultHttpResponse ,它與 DefaultHttpRequest 類似,只是對 IHttpResponseFeature 的封裝,不過 ASP.NET Core 也為我們提供了一些擴展方法,如:我們在寫入響應時,通常使用的是 Response 的擴展方法 WriteAsync

public static class HttpResponseWritingExtensions
{
    public static Task WriteAsync(this HttpResponse response, string text, CancellationToken cancellationToken = default(CancellationToken))
    {
        return response.WriteAsync(text, Encoding.UTF8, cancellationToken);
    }

    public static Task WriteAsync(this HttpResponse response, string text, Encoding encoding, CancellationToken cancellationToken = default(CancellationToken))
    {
        byte[] data = encoding.GetBytes(text);
        return response.Body.WriteAsync(data, 0, data.Length, cancellationToken);
    }
}

ASP.NET Core 還為 Response 提供了用來一個清空響應頭和響應體的擴展方法:

public static class ResponseExtensions
{
    public static void Clear(this HttpResponse response)
    {
        if (response.HasStarted)
        {
            throw new InvalidOperationException("The response cannot be cleared, it has already started sending.");
        }
        response.StatusCode = 200;
        response.HttpContext.Features.Get<IHttpResponseFeature>().ReasonPhrase = null;
        response.Headers.Clear();
        if (response.Body.CanSeek)
        {
            response.Body.SetLength(0);
        }
    }
}

還有比較常用的發送文件的擴展方法:SendFileAsync ,獲取響應頭的擴展方法:GetTypedHeaders 等等,就不再細說。

IHttpContextAccessor

在 ASP.NET 4.x 我們經常會通過 HttpContext.Current 來獲取當前請求的 HttpContext 對象,而在 ASP.NET Core 中,HttpContext 不再有 Current 屬性,並且在 ASP.NET Core 中一切皆註入,更加推薦使用註入的方式來獲取實例,而非使用靜態變數。因此,ASP.NET Core 提供了一個 IHttpContextAccessor 介面,用來統一獲取當前請求的 HttpContext 實例的方式:

public interface IHttpContextAccessor
{
    HttpContext HttpContext { get; set; }
}

它的定義非常簡單,就只有一個 HttpContext 屬性,它在ASP.NET Core 中還有一個內置的實現類:HttpContextAccessor

public class HttpContextAccessor : IHttpContextAccessor
{
    private static AsyncLocal<HttpContext> _httpContextCurrent = new AsyncLocal<HttpContext>();

    public HttpContext HttpContext
    {
        get
        {
            return _httpContextCurrent.Value;
        }
        set
        {
            _httpContextCurrent.Value = value;
        }
    }
}

這裡使用了一個 AsyncLocal<T> 類型來保存 HttpContext 對象,可能很多人對 AsyncLocal 不太瞭解,這裡就來介紹一下:

在.NET 4.5 中引用了 async await 等關鍵字,使我們可以像編寫同步方法一樣方便的來執行非同步操作,因此我們的大部分代碼都會使用非同步。以往我們所使用的 ThreadLocal 在同步方法中沒有問題,但是在 await 後有可能會創建新實的例(await 之後可能還交給之前的線程執行,也有可能是一個新的線程來執行),而不再適合用來保存線程內的唯一實例,因此在 .NET 4.6 中引用了 AsyncLocal<T> 類型,它類似於 ThreadLocal,但是在 await 之後就算切換線程也仍然可以保持同一實例。我們知道在 ASP.NET 4.x 中,HttpContext的 Current 實例是通過 CallContext 對象來保存的,但是 ASP.NET Core 中不再支持CallContext,故使用 AsyncLocal<T> 來保證線程內的唯一實例。

不過,ASP.NET Core 預設並沒有註入 IHttpContextAccessor 對象,如果我們想在應用程式中使用它,則需要手動來註冊:

public void ConfigureServices(IServiceCollection services)
{
    ervices.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
}

在上面介紹的HttpContextFactory類的構造函數中會註入IHttpContextAccessor實例,併為其HttpContext屬性賦值,併在Dispose方法中將其設置為null。

總結

在ASP.NET 4.x 中,我們就對 HttpContext 非常熟悉了,而在 ASP.NET Core 中,它的變化並不大,只是做了一些簡化,因此本文較為簡單,主要描述了一下 HttpContext 是如何創建的,以及它的構成,最後則介紹了一下在每個請求中獲取 HttpContext 唯一實例的方式,而在 ASP.NET Core 2.0 中 HttpContext 的 AuthenticationManager 對象已標記為過時,添加了一些擴展方法來實現AuthenticationManager中的功能,下一章就來介紹一下 ASP.NET Core 中的認證系統。


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

-Advertisement-
Play Games
更多相關文章
  • 轉:http://blog.csdn.net/hongchangfirst/article/details/7075026 大家都知道進程,可是知道linux是怎麼管理其進程的嗎?每一個進程都有一個進程描述符,具體是task_struct結構體存儲相關的信息,在linux/sched.h文件里定義, ...
  • 一、分佈位置上的區別: kmalloc()和__get_free_pages()函數申請的記憶體位於物理記憶體的映射區域,而且在物理上也是連續的,它們與真實的物理地址只有一個固定的偏移,因此存在簡單的線性關係;(3G+896M)(低端記憶體); vmalloc函數申請的虛擬記憶體與物理記憶體之間也沒有簡單的換 ...
  • 添加樣式: 在html中,需要創建2層div來實現。一個div包含另一個div: 效果: ...
  • 表格控制項 Spread Studio 發佈了全新的 V11 CTP 版本。在此版本中,Spread For WinForms 引入了 Spread Common,也帶來了 Spread 性能的巨大提升和記憶體消耗的急劇下降。 ...
  • 1. 前言 做了WPF開發多年,一直未曾自己實現一個自定義Window Style,無論是《WPF編程寶典》或是各種博客都建議使用WindowStyle="None" 和 AllowsTransparency="True",於是想當然以為這樣就可以了。最近來了興緻想自己實現一個,才知道WindowS ...
  • Time Protocol(RFC-868)是一種非常簡單的應用層協議:它返回一個32位的二進位數字,這個數字描述了從1900年1月1日0時0分0秒到現在的秒數,伺服器在TCP的37號埠監聽時間協議請求。本函數將伺服器返回值轉化成本地時間。 先前不知道有現成的IPAddress.NetworkTo ...
  • 一、首先我們先講一下ref與out的區別和使用方法; 1、ref與out的區別: out:需要在使用前聲明變數,分配地址但不能賦值,但是需要在使用中的時候需要初始化(進入方法體中的時候需要先賦值在使用),至於為什麼要在方法體中使用,我個人認為是為了區別ref;(即只出不進) ref:需要在使用前聲明 ...
  • 請求過來,根據ip和埠,由iis伺服器進行接收,如果是靜態文件則直接返迴文件內容,如果無法解析,則根據映射規則找到對應請求尾碼 的ASPNET_ISAPI.dll處理程式集,交由其進行處理。 1.此時會生成IsapRuntime,其創建了WorkRequest對象, 2.接下來實例化HttpRun ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...