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

Azure Function V4依赖注入调用异常排查(PnPCore证书认证)

Azure Function V4 + PnPCore 证书认证空引用问题排查与临时解决

更新记录

更新 2023-02-03 2

已找到临时解决方法并修复问题,具体方案见下文。

更新 2023-02-03 1

已定位异常发生在PnPCoreAuthenticationCredentialConfigurationOptions的证书配置环节:

X509Certificate = new PnPCoreAuthenticationX509CertificateOptions
{
    Certificate = certificate
}

推测依赖注入初始化Function1时,证书数据未被正确持久化。


问题背景

使用.NET6开发Azure Function V4,通过PnPCore实现SharePoint Online认证,采用Azure Key Vault存储的X509Certificate进行身份验证。应用注册、证书及Key Vault权限均已验证无误(通过PowerShell可正常访问SPO),但函数执行时抛出空引用异常:

System.Private.CoreLib: Exception has been thrown by the target 
of an invocation.

Anonymously Hosted DynamicMethods Assembly:

Object reference not set to an instance of an object.

移除builder.Services.AddPnPCoreAuthentication代码后,函数可正常启动,但执行_pnpContextFactory.CreateAsync时会因缺少认证失败。

配置文件(local.settings.json)

{
  "IsEncrypted": false,
  "Values": {
    "AzureWebJobsStorage": "",
    "FUNCTIONS_WORKER_RUNTIME": "dotnet"
  },
  "SharePointApp": {
    "ClientId": "6f5020cb-xxxx-xxxx-xxxx-xxxxxxxxxxx",
    "ClientSecret": "A6q****l6",
    "TenantId": "7bcac91e-xxxx-xxxx-xxxx-xxxxxxxxxxx",
    "ListServerRelativeUrl": "/sites/TestSite/SomeLibrary",
    "AzureKeyVault": {
      "Uri": "https://contoso-keys.vault.azure.net/",
      "AppCertificateKey": "My-integration-app-DEV"
    }
  },
  "PnPCore": {
    "HttpRequests": {
      "UserAgent": "PnPCoreSDK"
    },
    "PnPContext": {
      "GraphFirst": "false",
      "GraphCanUseBeta": "false"
    },
    "Sites": {
      "SiteToWorkWith": {
        "SiteUrl": "https://contoso.sharepoint.com/sites/TestSite/"
      }
    }
  }
}

Startup类代码

using Azure.Identity;
using Azure.Security.KeyVault.Certificates;
using Contoso.Function;
using Microsoft.Azure.Functions.Extensions.DependencyInjection;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using PnP.Core.Auth.Services.Builder.Configuration;
using PnP.Core.Services.Builder.Configuration;
using System;

[assembly: FunctionsStartup(typeof(Startup))]
namespace Contoso.Function
{
    public class Startup : FunctionsStartup
    {
        private static IConfiguration _configuration = null;

        public override void Configure(IFunctionsHostBuilder builder)
        {
            var provider = builder.Services.BuildServiceProvider();
            _configuration = provider.GetRequiredService<IConfiguration>();

            var spOptions = new SharePointAppConfiguration(); // 绑定local.settings.json中SharePointApp节点的自定义类
            _configuration.GetSection("SharePointApp").Bind(spOptions);
            var coreOptions = new PnPCoreOptions();
            _configuration.GetSection("PnPCore").Bind(coreOptions);

            builder.Services.AddSingleton(spOptions);

            builder.Services.AddPnPCore(options =>
            {
                options.PnPContext = coreOptions.PnPContext;
                options.HttpRequests = coreOptions.HttpRequests;

                foreach (var site in coreOptions.Sites)
                {
                    options.Sites.Add(site.Key, site.Value);
                }
            });

            builder.Services.AddPnPCoreAuthentication(options =>
            {
                var azKeyVault = new CertificateClient(spOptions.AzureKeyVault.Uri, new ClientSecretCredential(spOptions.TenantId, spOptions.ClientId, spOptions.ClientSecret));
                var certificate = azKeyVault.DownloadCertificate(spOptions.AzureKeyVault.AppCertificateKey);

                options.Credentials.Configurations.Add("x509certificate", new PnPCoreAuthenticationCredentialConfigurationOptions
                {
                    ClientId = spOptions.ClientId,
                    TenantId = spOptions.TenantId,
                    X509Certificate = new PnPCoreAuthenticationX509CertificateOptions
                    {
                        Certificate = certificate
                    }
                });
                options.Credentials.DefaultConfiguration = "x509certificate";
                options.Sites.Add("SiteToWorkWith",
                    new PnPCoreAuthenticationSiteOptions
                    {
                        AuthenticationProviderName = "x509certificate"
                    });
            });
        }

        public override void ConfigureAppConfiguration(IFunctionsConfigurationBuilder builder)
        {
            var context = builder.GetContext();

            builder.ConfigurationBuilder
                .SetBasePath(Environment.CurrentDirectory)
                .AddJsonFile("local.settings.json", optional: true, reloadOnChange: true)
                .AddEnvironmentVariables();
        }
    }
}

Function类代码

using System;
using Microsoft.Azure.WebJobs;
using Microsoft.Extensions.Logging;
using PnP.Core.Services;

namespace Contoso.Function
{
    public class Function1
    {
        private readonly IPnPContextFactory _pnpContextFactory;

        public Function1(IPnPContextFactory pnpContextFactory)
        {
            _pnpContextFactory = pnpContextFactory;
        }

        [FunctionName("Function1")]
        public void Run([TimerTrigger("*/5 * * * * *")] TimerInfo myTimer, ILogger log)
        {
            using (var context = _pnpContextFactory.CreateAsync("SitesToWorkWith"))
            {
                log.LogInformation($"C# Timer trigger function executed at: {DateTime.Now}");
            }
        }
    }
}

临时解决方法

避免在AddPnPCoreAuthentication中直接传递Key Vault下载的证书实例,改为通过证书的字节数组+密码方式配置:

  1. 从Key Vault获取证书的完整PFX字节数据(可通过Key Vault的Secret节点获取,或下载证书后导出为PFX格式)
  2. 修改认证配置代码:
// 从Key Vault获取证书PFX字节数据及密码(无密码则为空字符串)
var certificateBytes = ...; 
var certificatePassword = "";

options.Credentials.Configurations.Add("x509certificate", new PnPCoreAuthenticationCredentialConfigurationOptions
{
    ClientId = spOptions.ClientId,
    TenantId = spOptions.TenantId,
    X509Certificate = new PnPCoreAuthenticationX509CertificateOptions
    {
        CertificateBytes = certificateBytes,
        CertificatePassword = certificatePassword
    }
});

这种方式确保证书数据在依赖注入容器中被正确序列化和持久化,避免直接传递证书实例导致的空引用问题。


内容的提问来源于stack exchange,提问作者diceler

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.29 15:58:20