使用 ASP.NET Core 快速打造 Windows Service - 以 Headless Chrome 網頁抓圖為例

栏目: ASP.NET · 发布时间: 5年前

内容简介:先前介紹過好用的 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 開局:

使用 ASP.NET Core 快速打造 Windows Service - 以 Headless Chrome 網頁抓圖為例

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!。

對於以上運作想更深入了解的同學可參考:

接著我用最簡單的寫法將回傳 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);
    }
});

補充: chrome.exe 完整參數清單

這樣子一個簡單的傳參數取圖檔功能就完成了~

使用 ASP.NET Core 快速打造 Windows Service - 以 Headless Chrome 網頁抓圖為例

下一步我們來體驗 ASP.NET Core 的威力,加幾行程式把它轉成 Window Service。

(官方文件有完整說明: 在 Windows 服務上裝載 ASP.NET Core )

首先從 NuGet 安裝 Microsoft.AspNetCore.Hosting.WindowsServices 程式庫:

使用 ASP.NET Core 快速打造 Windows Service - 以 Headless Chrome 網頁抓圖為例

我們對 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,複製檔案就能執行。

使用 ASP.NET Core 快速打造 Windows Service - 以 Headless Chrome 網頁抓圖為例

下一步是使用 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 快速打造 Windows Service - 以 Headless Chrome 網頁抓圖為例

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 網頁抓圖為例》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

ASP.NET 2.0开发指南

ASP.NET 2.0开发指南

郝刚 / 人民邮电出版社 / 2006 / 88.0

本书紧紧围绕ASP.NET 2.0技术精髓展开深入讲解,全书分为6个部分,共18章。第1部分介绍基础知识,包括ASP.NET 2.0概述、Visual Studio 2005集成开发环境、创建ASP.NET应用程序和C# 2.0程序设计基础。第2部分讲解用户界面方面的特性,包括母版页、主题和皮肤、站点导航控件和其他新增服务器控件。第3部分探讨了数据访问方面的内容,包括数据访问技术概述、数据源控件、......一起来看看 《ASP.NET 2.0开发指南》 这本书的介绍吧!

SHA 加密
SHA 加密

SHA 加密工具

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具