You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ASP.NET WebService使用PuppeteerSharp转PDF时异步操作报错求助

问题:ASP.NET WebService中PuppeteerSharp异步操作报错

报错场景

执行PuppeteerSharp的Chromium下载代码时触发错误:

await new BrowserFetcher().DownloadAsync(BrowserFetcher.DefaultChromiumRevision);

错误信息

An asynchronous operation cannot be started at this time.
Asynchronous operations may only be started within an asynchronous handler or module or during certain events in the Page lifecycle.
If this exception occurred while executing a Page, ensure that the Page is marked <%@ Page Async="true" %>.
This exception may also indicate an attempt to call an "async void" method, which is generally unsupported within ASP.NET request processing.
Instead, the asynchronous method should return a Task, and the caller should await it.

现有代码

WebMethod方法

[WebMethod]
public string HtmlToPdf(string htmlString, string baseURL, int anchoPagina, string tamanoPagina, string orientacionPagina, string calidad, string leftMargin, string rightMargin, string topMargin, string bottomMargin, bool bCabecera, int bImagenCabecera, string imagenCabecera, bool bJS, bool bActiveX, bool bJpeg, bool bPie, bool bFirma, bool bPaginador, string textoPaginador, int tipoTextoPie, string textoPie, int anchoTextoPie, bool bImagenPie, string imagenPie, int ImagenPieX, int ImagenPieY, string textoLateral, int iPosX, int iPosY, string entorno, int posYnumPagina, int altoCabecera, bool bLineaPie, bool bCabeceraPortada, bool bPiePortada, string portadaPDF)
{
    try
    {
        GestionTraza.Utilidades.escribirLog("WSTOPDF", string.Join(" ", " -- Arranca el proceso de transformación de HTML a PDF"), LogTrazas.DBG, LogCapas.CN, 34);                

        bool orientacion = (orientacionPagina == "V" ? false : true);

        var fichero = GenerarPdf(htmlString, imagenCabecera, altoCabecera, orientacionPagina, anchoPagina, leftMargin, rightMargin, topMargin, bottomMargin, textoPie);
        fichero.Wait();

        return fichero.Result;

    } catch (Exception e)
    {
        GestionTraza.Utilidades.escribirLogError(e, "WSTOPDF", "HtmlToPdf", "HtmlToPdf", (int)LogTrazas.ERR, (int)LogCapas.CN, 34);
        throw e;
    }
    
}

异步PDF生成方法

private async Task<string> GenerarPdf(string htmlString, string imagenCabecera, int altoCabecera, string orientacionPagina, int anchoPagina, string leftMargin, string rightMargin, string topMargin, string bottomMargin, string textoPie) {

    string outputFile = "";

    try
    {
        bool orientacion = (orientacionPagina == "V" ? false : true);                

        Console.WriteLine("Downloading chromium");
        await new BrowserFetcher().DownloadAsync(BrowserFetcher.DefaultChromiumRevision);

        Console.WriteLine("Navigating google");
        var browser = await Puppeteer.LaunchAsync(new LaunchOptions
        {
            Headless = true
        });
        var page = await browser.NewPageAsync();
        await page.GoToAsync(htmlString);
        await page.PdfAsync(outputFile, new PdfOptions
        {
            HeaderTemplate = imagenCabecera,
            Height = altoCabecera,
            Landscape = orientacion,
            Width = anchoPagina,
            Format = PaperFormat.A4,
            DisplayHeaderFooter = true,
            MarginOptions = new MarginOptions
            {
                Top = topMargin,
                Right = rightMargin,
                Bottom = bottomMargin,
                Left = leftMargin
            },
            FooterTemplate = textoPie
        });

        return outputFile;
    }
    catch (Exception e)
    {
        GestionTraza.Utilidades.escribirLogError(e, "WSTOPDF", "GenerarPdf", "GenerarPdf", (int)LogTrazas.ERR, (int)LogCapas.CN, 34);
        throw e;
    }            
}

问题原因

核心问题是同步方法中阻塞等待异步任务(fichero.Wait()和fichero.Result),导致ASP.NET的同步上下文被阻塞,异步操作无法在正确的上下文环境中启动,触发线程上下文冲突。

解决方案

1. 将WebMethod改为异步版本

ASP.NET WebMethod支持异步实现,修改要点:

  • 方法返回Task<string>而非string
  • 用await替代Wait()和Result,保持异步流程一致性

修改后的WebMethod代码:

[WebMethod]
public async Task<string> HtmlToPdf(string htmlString, string baseURL, int anchoPagina, string tamanoPagina, string orientacionPagina, string calidad, string leftMargin, string rightMargin, string topMargin, string bottomMargin, bool bCabecera, int bImagenCabecera, string imagenCabecera, bool bJS, bool bActiveX, bool bJpeg, bool bPie, bool bFirma, bool bPaginador, string textoPaginador, int tipoTextoPie, string textoPie, int anchoTextoPie, bool bImagenPie, string imagenPie, int ImagenPieX, int ImagenPieY, string textoLateral, int iPosX, int iPosY, string entorno, int posYnumPagina, int altoCabecera, bool bLineaPie, bool bCabeceraPortada, bool bPiePortada, string portadaPDF)
{
    try
    {
        GestionTraza.Utilidades.escribirLog("WSTOPDF", string.Join(" ", " -- Arranca el proceso de transformación de HTML a PDF"), LogTrazas.DBG, LogCapas.CN, 34);                

        bool orientacion = (orientacionPagina == "V" ? false : true);

        var fichero = await GenerarPdf(htmlString, imagenCabecera, altoCabecera, orientacionPagina, anchoPagina, leftMargin, rightMargin, topMargin, bottomMargin, textoPie);

        return fichero;

    } catch (Exception e)
    {
        GestionTraza.Utilidades.escribirLogError(e, "WSTOPDF", "HtmlToPdf", "HtmlToPdf", (int)LogTrazas.ERR, (int)LogCapas.CN, 34);
        throw e;
    }
    
}

2. 额外优化:提前下载Chromium

BrowserFetcher.DownloadAsync不需要每次请求都执行,建议在WebService启动时一次性下载,避免重复消耗资源:

  • 在Global.asax的Application_Start事件中添加初始化代码:
protected void Application_Start(object sender, EventArgs e)
{
    // 异步初始化Chromium,避免阻塞启动流程
    _ = new BrowserFetcher().DownloadAsync(BrowserFetcher.DefaultChromiumRevision);
}

后续请求可直接使用已下载的Chromium,提升性能同时避免重复触发异步下载的上下文问题。

3. 通用原则:避免同步阻塞异步任务

在ASP.NET环境中,永远不要在同步方法中使用Task.Wait()或Task.Result,这会导致死锁风险和上下文冲突。所有异步操作必须通过await链式调用,保持异步流程的一致性。

内容的提问来源于stack exchange,提问作者Juan Carlos Macho

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 01:27:20