内容简介:先前介紹過好用的 Headless Chrome以 Headless Chrome 為例,有種簡單解法是將呼叫 Chorme 的功能抽取成迷你獨立 ASP.NET 網站,將其 IIS AppPool 設定成一般使用者(並建議只開放本機存取),運轉前可先用該使用者身分登入 Windows 執行一次 Chrome,確保 Profile 資料已初始化,即可在 ASP.NET 網站執行 Headless Chrome。(註:除了改 IIS 執行身分,我常用的另一種技巧是將這類功能包成 Windows Service
先前介紹過好用的 Headless Chrome 可以擷圖、轉存 PDF 與爬資料 ,手邊有個應用想將其整合為網站功能,理論上從 C# 啟動外部程式 chrome.exe 即可搞定,但如同在背景執行 Office Word/Excel 有個挑戰。Word/Excel/Chrome 原本為前景程式,依賴使用者 Profile 且對執行權限也有較多要求,故常遇到的狀況是用 Visual Studio 測試除錯正常(因為 Dev Server 或 IIS Express 是以開發者登入帳號執行),等部署到 IIS 改用 AppPool 專屬帳號執行卻屢屢卡住無回應讓人傻眼。
以 Headless Chrome 為例,有種簡單解法是將呼叫 Chorme 的功能抽取成迷你獨立 ASP.NET 網站,將其 IIS AppPool 設定成一般使用者(並建議只開放本機存取),運轉前可先用該使用者身分登入 Windows 執行一次 Chrome,確保 Profile 資料已初始化,即可在 ASP.NET 網站執行 Headless Chrome。(註: 務必將有特殊要求的功能拆成小網站分離處理,切忌為此修改整個 ASP.NET 網站的執行身分,以避免提高資安風險 )
除了改 IIS 執行身分,我常用的另一種技巧是將這類功能包成 Windows Service 以指定使用者帳號執行並限定本機存取。之前我習慣用 NancyFx (參考: NancyFx-打造小型 WebAPI 與 Microservice 的輕巧利器 ) 處理這類情境,隨著 ASP.NET Core 的推出,其輕巧簡便性不輸 NancyFx,最酷的是 ASP.NET Core 支援 Windows Service 執行模式,所以這回我們來試試用 ASP.NET Core 打造一個 Headless Chrome 抓圖 Windows Service,順便巡覽 ASP.NET Core 的核心結構,以求練武強身。註: 此類服務有被當成跳板機的風險,請參考文末提醒。
首先,打開 Visual Studio 2017 建立 ASP.NET Core 2.1 專案,為求簡潔我連 MVC/Controller 都省了,開專案時選 Empty 開局:
Empty 模版所建立的專案結構極簡單,就只有一個 Program.cs 與 Starup.cs。Program.cs 是啟動網站的主程式所在:
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
namespace HeadlessChromeService
{
public class Program
{
public static void Main(string[] args)
{
CreateWebHostBuilder(args).Build().Run();
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>();
}
}
Main() 是程式進入點,呼叫 CreateWebHostBuilder() 取得 IWebHostBuilder,執行 Build() 建立並配置網站,再執行 Run() 啟動網站。在 CreateWebHostBuilder() 中,WebHost.CreateDefaultBuilder() 封裝了網站配置、Logging、與 Kestrel/IIS 整合等細節,接著呼叫 UseStartup<Startup>(),依 Startup 類別內的邏輯定義網站所需服務、設定執行 Pipeline 等。
預設的 Startup.cs 長這樣:
public class Startup
{
// This method gets called by the runtime. Use this method to add services to the container.
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.Run(async (context) =>
{
await context.Response.WriteAsync("Hello World!");
});
}
}
app.Run() 這段定義如何處理 URL 請求,預設規則為不管連到哪個路徑,一律回應 Hello World!。
對於以上運作想更深入了解的同學可參考:
- ASP.NET Core 基本概念
- Exploring Program.cs, Startup.cs and CreateDefaultBuilder in ASP.NET Core 2 preview 1
接著我用最簡單的寫法將回傳 Hello World 改寫成接收網址、尺寸等參數,並傳回該網頁畫面擷圖的服務。構想是透過 u、w、h 三個 QueryString 參數傳入網址(記得要 HttpUtility.UrlEncode 或 encodeURIComponent() 編碼)、寬度與高度,二話不說呼叫 Headless Chrome 抓圖,吐回 png。
app.Run(async (context) =>
{
//await context.Response.WriteAsync("Hello World!");
Process p = null;
context.Response.ContentType = "text/plain";
try
{
var url = context.Request.Query["u"];
if (string.IsNullOrEmpty(url))
throw new ApplicationException("missing parameter - u");
if (!int.TryParse(context.Request.Query["w"].FirstOrDefault(), out var w))
w = 1024;
if (!int.TryParse(context.Request.Query["h"].FirstOrDefault(), out var h))
h = 768;
var tmpFile = Path.GetTempFileName() + ".png";
var si = new ProcessStartInfo()
{
CreateNoWindow = true,
FileName = @"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe",
Arguments =
$"--headless --disable-gpu --window-size={w},{h} --screenshot={tmpFile} {url}",
UseShellExecute = false,
RedirectStandardError = true
};
p = Process.Start(si);
p.WaitForExit(5000);
if (File.Exists(tmpFile))
{
var img = File.ReadAllBytes(tmpFile);
File.Delete(tmpFile);
context.Response.ContentType = "image/png";
await context.Response.Body.WriteAsync(img, 0, img.Length);
}
else await context.Response.WriteAsync("Failed");
}
catch (Exception ex)
{
if (p != null)
{
try
{
//若Process仍存在,清除之
Process.GetProcessById(p.Id);
p.Kill();
}
catch
{
// ignored
}
}
await context.Response.WriteAsync(ex.Message);
}
});
這樣子一個簡單的傳參數取圖檔功能就完成了~
下一步我們來體驗 ASP.NET Core 的威力,加幾行程式把它轉成 Window Service。
(官方文件有完整說明: 在 Windows 服務上裝載 ASP.NET Core )
首先從 NuGet 安裝 Microsoft.AspNetCore.Hosting.WindowsServices 程式庫:
我們對 Program.cs 稍作修改,先 using Microsof.AspNetCore.Hosting.WindowsServices 以擴充 RunAsService() 方法,在 Main() 裡依據參數決定要 .Run() 跑 Console 模式或是 .RunAsService() 跑成 Windows Service。
public static void Main(string[] args)
{
var isService = !(Debugger.IsAttached || args.Contains("--console"));
if (isService)
{
//將工作目錄指向ContentRoot
var pathToExe = Process.GetCurrentProcess().MainModule.FileName;
var pathToContentRoot = Path.GetDirectoryName(pathToExe);
Directory.SetCurrentDirectory(pathToContentRoot);
}
var host = CreateWebHostBuilder(
//剔除--console參數
args.Where(arg => arg != "--console").ToArray()).Build();
if (isService) host.RunAsService();
else host.Run();
}
再來是調整 .csproj 設定,指定 RuntimeIdentifier(RID) 為 win7-x64,設定 SelfContained = true 使用 SCD (Self-Contained Deployment) 打包 ASP.NET 相關程式庫並輸出成 .exe ,IsTransformWebConfigDisabled 則是設定不要輸出 web.config。
<PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework>
<RuntimeIdentifier>win7-x64</RuntimeIdentifier>
<SelfContained>true</SelfContained>
<IsTransformWebConfigDisabled>true</IsTransformWebConfigDisabled>
</PropertyGroup>
使用 Visual Studio 2017 發佈(Publish)網站,可得到如下檔案,SCD 部署包含 .NET Core 程式庫,檔案較多,有近 400 個檔案約 100MB,好處是部署時不需安裝 .NET Core,複製檔案就能執行。
下一步是使用 sc.exe 註冊並啟動服務:(如果要為服務另建專屬執行帳號,記得要授與檔案目錄存取權限,建議用該帳號登入執行一次 Chrome 以確保 Profile 初始化完成及功能正常。)
sc create WebSnapshotService binPath="X:\Lab\HCS\Svc\HeadlessChromeService.exe" obj="JEFFWin10\jeffrey" password="{PASSWORD}"
安裝完成後,ASP.NET Core 便能透過 SC START/STOP 啟動停止,一個簡單的 Windows Service 就做好囉~
ASP.NET Core 預設聽 5000 Port,當主機上有多個服務要錯開監聽 Port,可利用先前教過的 指定 ASP.NET Core Kestrel 接聽 Port 技巧,在 exe 所在目錄放個 appSettings.json 指定不同 Port。
{
"Kestrel": {
"EndPoints": {
"Http": {
"Url": "http://localhost:5123"
}
}
}
}
重要提醒
由於網頁擷圖具有接收參數瀏覽任意 URL 的特性,必須提防被當成跳板機做壞事,在指定 EndPoint 時宜限定 localhost,若要開放遠端客戶端呼叫,請務必限定來源 IP。
This article desmotsrate how to use ASP.NET Core to build a simple web page snapshot service and convert it to Windows service easily.
以上所述就是小编给大家介绍的《使用 ASP.NET Core 快速打造 Windows Service - 以 Headless Chrome 網頁抓圖為例》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- 使用Blynk打造一款物联网产品
- 使用Istio打造微服务(第1部分)
- 使用Istio打造微服务(第1部分)
- 原 荐 使用Nagios打造专业的业务状态监控
- 使用webpack4打造自己的前端工作流
- 使用 CodeMirror 打造属于自己的在线代码编辑器 荐
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Perl语言入门
[美] Randal L.Schwartz、Tom Phoenix / 李晓峰 / 中国电力出版社 / 2002-8 / 48.00元
本书第一版于1993年问世,并从此成为畅销书。本书由Perl社区最著名、最活跃的两位成员写成,是Perl程序设计语言的精髓指南。 Perl最初只是Unix系统管理员的一个工具,在工作日里被用在无数的小任务中。从那以后,它逐步发展成为一种全功能的程序设计语言,特别是在各种计算平台上,它被用作Web编程、数据库处理、XML处理以及系统管理——它能够完成所有这些工作,同时仍然是处理小的日常工作的完......一起来看看 《Perl语言入门》 这本书的介绍吧!