.NET Core 控制檯如何嵌入執行 Web API網站?

【導讀】

我們知道在。NET Framework中可以嵌入執行Web APi,那麼在。NET Core(。NET 6+稱之為。NET)中如何內嵌執行Web Api呢,在實際專案中這種場景非常常見,那麼我們本節以。NET 6。0作為演示示例一起來瞅瞅。

內嵌執行.NET Core Web APi

接下來我們透過控制檯作為主程式來啟動Web APi,首先我們建立名為EmbedWebApi的控制檯程式,然後建立Embed。WebApi類庫執行Web APi,我們在此Web APi中建立如下介面,並實現相關方法來執行Web APi

public

class

InitTest

IInitTest

{

public

void

Init

{

var

builder = WebApplication。CreateBuilder();

builder。Services。AddControllers();

var

app = builder。Build();

app。UseRouting();

app。UseEndpoints(endpoints =>

{

endpoints。MapDefaultControllerRoute();

});

app。Run();

}

}

public

interface

IInitTest

{

void

Init

}

透過寫介面並在對應方法中執行Web APi主要是達到在控制中呼叫該介面進行模擬實現,這裡需要注意一點的是,因為我們建立的Web APi是類庫,要想使用Web裡面的Api等等,直接在專案檔案中新增如下一行以表明我們要引用框架,這樣一來框架裡面所包含的APi等等版本都一致統一,

而不是透過NuGet一一下載,這是錯誤的做法

“Microsoft。AspNetCore。App”

/>

接下來我們在該類庫中按照規範建立Controllers資料夾,並建立測試控制器,如下

using

Microsoft。AspNetCore。Mvc;

namespace

Embed。WebApi。Controllers

{

ApiController

Route(

“api/[controller]/[action]”

public

class

TestController

ControllerBase

{

HttpGet

public

IActionResult

Test

{

return

Ok(

“Hello World”

);

}

}

}

最後我們在控制檯程式中註冊上述介面並呼叫初始化方法,如下:

internal

class

Program

{

static

void

Main

string

[] args

{

var

services =

new

ServiceCollection();

services。AddTransient();

var

serviceProvider = services。BuildServiceProvider();

var

initTest = serviceProvider。GetRequiredService();

initTest。Init();

Console。Read();

}

}

.NET Core 控制檯如何嵌入執行 Web API網站?

蕪湖,我們透過Postman模擬呼叫測試介面,結果驚呆了,404了~~~

.NET Core 控制檯如何嵌入執行 Web API網站?

當我們將類庫中的控制器移動到控制檯中,此時請求測試介面併成功返回對世界的問候,這是什麼原因呢?

不難猜測可知,WebAPi控制器的啟用以作為入口的主程式集進行查詢啟用。

雖然這樣看似解決了問題,假設呼叫嵌入執行的主程式是底層已經封裝好的基礎設施,那麼豈不是遭到了程式碼入侵,所以我們就想在執行的Web APi類庫裡面去啟用,此時我們想到將類庫作為Web APi應用程式一部分應用手動載入並激活,在初始化方法裡面修改為如下即可請求測試介面成功

public

class

InitTest

IInitTest

{

private

static

readonly

string

AssemblyName =

typeof

(InitTest)。Assembly。GetName()。Name;

public

void

Init

()

{

var

builder = WebApplication。CreateBuilder();

builder。Services。AddControllers()

。AddApplicationPart(Assembly。Load(

new

AssemblyName(AssemblyName)));

var

app = builder。Build();

app。UseRouting();

app。UseEndpoints(endpoints =>

{

endpoints。MapDefaultControllerRoute();

});

app。Run();

}

}

上述直接在執行Web APi類庫中新增控制器啟用,這種場景完全限定於底層主入口已封裝好,所以只能採用這種方式,若是主入口我們自己可控制,當然還有另外一種方式,來,我們瞧瞧擷取的關鍵性原始碼

///

///

Populates the given

using the list of

///

s configured on the

///

///

///

The type of the feature。

///

The feature instance to populate。

public

void

PopulateFeature(TFeature feature)

{

if

(feature ==

{

throw

new

ArgumentException(

nameof

(feature));

}

foreach

var

provider

in

FeatureProviders。OfType>())

{

provider。PopulateFeature(ApplicationParts, feature);

}

}

internal

void

PopulateDefaultParts

string

entryAssemblyName

{

var

assemblies = GetApplicationPartAssemblies(entryAssemblyName);

var

seenAssemblies =

new

HashSet();

foreach

var

assembly

in

assemblies)

{

if

(!seenAssemblies。Add(assembly))

{

// “assemblies” may contain duplicate values, but we want unique ApplicationPart instances。

// Note that we prefer using a HashSet over Distinct since the latter isn‘t

// guaranteed to preserve the original ordering。

continue

}

var

partFactory = ApplicationPartFactory。GetApplicationPartFactory(assembly);

foreach

var

applicationPart

in

partFactory。GetApplicationParts(assembly))

{

ApplicationParts。Add(applicationPart);

}

}

}

private

static

IEnumerable

GetApplicationPartAssemblies

string

entryAssemblyName

{

var

entryAssembly = Assembly。Load(

new

AssemblyName(entryAssemblyName));

// Use ApplicationPartAttribute to get the closure of direct or transitive dependencies

// that reference MVC。

var

assembliesFromAttributes = entryAssembly。GetCustomAttributes()

。Select(name => Assembly。Load(name。AssemblyName))

。OrderBy(assembly => assembly。FullName, StringComparer。Ordinal)

。SelectMany(GetAssemblyClosure);

// The SDK will not include the entry assembly as an application part。 We’ll explicitly list it

// and have it appear before all other assemblies \ ApplicationParts。

return

GetAssemblyClosure(entryAssembly)

。Concat(assembliesFromAttributes);

}

private

static

IEnumerable

GetAssemblyClosure

Assembly assembly

{

yield

return

assembly;

var

relatedAssemblies = RelatedAssemblyAttribute。GetRelatedAssemblies(assembly, throwOnError:

false

。OrderBy(assembly => assembly。FullName, StringComparer。Ordinal);

foreach

var

relatedAssembly

in

relatedAssemblies)

{

yield

return

relatedAssembly;

}

}

從上述原始碼可知,透過主入口程式集還會載入引用的程式集去查詢並激活相關特性(比如控制器),當然前提是實現ApplicationPartAttribute特性,此特性必須在主入口程式集裡定義,定義在程式集上

所以我們只需一行程式碼即可搞定,我們在控制檯主入口名稱空間頂部新增特性,引入Web APi類庫程式集作為應用程式的一部分,如下:

assembly: ApplicationPart(

“Embed。WebApi”

.NET Core 控制檯如何嵌入執行 Web API網站?

那麼接下來問題又來了,要是需要執行多個Web APi我們又當如何呢?

按照上述方式一一新增未嘗不可,我們也可以透過MSBuild任務來進行構建將相關特性自動新增到主入口程式集描述資訊裡面

去,例如:

“Microsoft。AspNetCore。Mvc。ApplicationParts。ApplicationPartAttribute”

>

<_Parameter1>Embed。WebApi

有的童鞋就問了,這不寫死了麼,那還不如透過新增特性的方式去處理,請注意這裡只是使用示例

實際情況下,我們可將多個Web APi放在同一解決方案下,然後在此解決方案下建立可構建任務的。targets檔案,並在主專案檔案裡引入,將程式集名稱作為變數引入,剩下事情自行統一處理,若不清楚怎麼搞,就在程式碼中使用特性方式也未嘗不可,例如如下:

“Microsoft。AspNetCore。Mvc。ApplicationParts。ApplicationPartAttribute”

>

<_Parameter1>$(AssemblyName)

.NET Core 控制檯如何嵌入執行 Web API網站?

本節我們重點討論如何內嵌執行。NET Core Web APi類庫,同時介紹了兩種啟用比如控制器特性方案, 希望對您有所幫助,謝謝,我們下節再會