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

Azure函数自定义类异常处理问题:自定义抛错触发ArgumentNullException

问题分析:Azure函数中自定义异常未被捕获反而出现Logger空引用错误

在tokenGenerator.GenerateToken方法中抛出自定义异常,服务类重新抛出后,预期会被Azure函数中对应的catch块捕获处理,但实际触发了System.ArgumentNullException(参数'logger'为null),以下是相关代码及异常信息:

Azure函数Login代码

[Function("Loginxyz")]
public async Task<IActionResult> GenerateToken(
        [HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "user/login")] HttpRequestData req,
        ILogger log)
{
    try
    {
        if (requestData.BarCode != null)
        {
            var localLoginRequest = JsonConvert.DeserializeObject<xyz>(requestBody);
            token = await _tokenService.GenerateToken(localLoginRequest);
        }

        if (token == null)
            return new UnauthorizedResult();

        return new OkObjectResult(new { access_token = token });
    }
    catch (AggregateException ae)
    {
        // Handle multiple exceptions
        foreach (var innerException in ae.InnerExceptions)
        {
            log.LogError(innerException, "An error occurred");
            // Handle the inner exception based on its type
        }

        // Return a specific error response based on the aggregate exception
        return new BadRequestObjectResult("An error occurred");
    }
    catch (AuthenticationServiceException ex)
    {
        // Log the error with detailed message
        log.LogError($"Error during token generation: {ex.Message}", ex);

        // Create a custom error response object
        var errorResponse = new
            {
                Error = ex.Message,
                StatusCode = StatusCodes.Status400BadRequest
            };

        // Return the error response with the appropriate status code
        return new JsonResult(errorResponse) { StatusCode = StatusCodes.Status400BadRequest };
    }
    catch (LoginConflictException ex)
    {
        // Log the error with detailed message
        log.LogError($"Error during token generation: {ex.Message}", ex);

        // Create a custom error response object
        var errorResponse = new
            {
                Error = ex.Message,
                StatusCode = StatusCodes.Status409Conflict
            };

        // Return the error response with the appropriate status code
        return new JsonResult(errorResponse) { StatusCode = StatusCodes.Status409Conflict };
    }
    catch (Exception ex)
    {
        log.LogError($"Error during token generation: {ex.Message}");
        return new StatusCodeResult(StatusCodes.Status500InternalServerError);
    }
}

服务类代码

public async Task<TokenResponseDto> GenerateToken(object tokenRequest)
{
    try
    {
        return await tokenGenerator.GenerateToken(tokenRequest);
    }
    catch (Exception ex)
    {
        throw; // Rethrow the exception
    }
}

异常跟踪信息

Function 'Login', Invocation id '': An exception was thrown by the invocation. [2024-07-13T02:28:21.903Z] Result: Function 'Login', Invocation id '': An exception was thrown by the invocation. Exception: System.ArgumentNullException: Value cannot be null. (Parameter 'logger') [2024-07-13T02:28:21.903Z]    at System.ThrowHelper.Throw(String paramName) [2024-07-13T02:28:21.904Z]    at Microsoft.Extensions.Logging.LoggerExtensions.Log(ILogger logger, LogLevel logLevel, EventId eventId, Exception exception, String message, Object[] args) [2024-07-13T02:28:21.904Z]    at Microsoft.Extensions.Logging.LoggerExtensions.LogError(ILogger logger, String message, Object[] args) [2024-07-13T02:28:21.905Z]    at ..AzureFunctions.Funtions.User.LoginFunction.GenerateToken(HttpRequestData req, ILogger log) in \Funtions\User\LoginFunction.cs:line 86 [2024-07-13T02:28:21.907Z]    at ..DirectFunctionExecutor.ExecuteAsync(FunctionContext context) in ..\Microsoft.Azure.Functions.Worker.Sdk.Generators\Microsoft.Azure.Functions.Worker.Sdk.Generators.FunctionExecutorGenerator\GeneratedFunctionExecutor.g.cs:line 38 [2024-07-13T02:28:21.909Z]    at Microsoft.Azure.Functions.Worker.OutputBindings.OutputBindingsMiddleware.Invoke(FunctionContext context, FunctionExecutionDelegate next) in D:\a\_work\1\s\src\DotNetWorker.Core\OutputBindings\OutputBindingsMiddleware.cs:line 13 [2024-07-13T02:28:21.909Z]    at Microsoft.Azure.Functions.Worker.Extensions.Http.AspNetCore.FunctionsHttpProxyingMiddleware.Invoke(FunctionContext context, FunctionExecutionDelegate next) in D:\a\_work\1\s\extensions\Worker.Extensions.Http.AspNetCore\src\FunctionsMiddleware\FunctionsHttpProxyingMiddleware.cs:line 48 [2024-07-13T02:28:21.910Z]    at Microsoft.Azure.Functions.Worker.FunctionsApplication.InvokeFunctionAsync(FunctionContext context) in D:\a\_work\1\s\src\DotNetWorker.Core\FunctionsApplication.cs:line 89 Stack:    at System.ThrowHelper.Throw(String paramName) [2024-07-13T02:28:21.911Z]    at Microsoft.Extensions.Logging.LoggerExtensions.Log(ILogger logger, LogLevel logLevel, EventId eventId, Exception exception, String message, Object[] args) [2024-07-13T02:28:21.912Z]    at Microsoft.Extensions.Logging.LoggerExtensions.LogError(ILogger logger, String message, Object[] args) [2024-07-13T02:28:21.915Z]    at ..Funtions.User.LoginFunction.GenerateToken(HttpRequestData req, ILogger log) in .\Funtions\User\LoginFunction.cs:line 86 [2024-07-13T02:28:21.916Z]    at ..DirectFunctionExecutor.ExecuteAsync(FunctionContext context) in ..\Microsoft.Azure.Functions.Worker.Sdk.Generators\Microsoft.Azure.Functions.Worker.Sdk.Generators.FunctionExecutorGenerator\GeneratedFunctionExecutor.g.cs:line 38 [2024-07-13T02:28:21.917Z]    at Microsoft.Azure.Functions.Worker.OutputBindings.OutputBindingsMiddleware.Invoke(FunctionContext context, FunctionExecutionDelegate next) in D:\a\_work\1\s\src\DotNetWorker.Core\OutputBindings\OutputBindingsMiddleware.cs:line 13 [2024-07-13T02:28:21.918Z]    at Microsoft.Azure.Functions.Worker.Extensions.Http.AspNetCore.FunctionsHttpProxyingMiddleware.Invoke(FunctionContext context, FunctionExecutionDelegate next) in D:\a\_work\1\s\extensions\Worker.Extensions.Http.AspNetCore\src\FunctionsMiddleware\FunctionsHttpProxyingMiddleware.cs:line 48 [2024-07-13T02:28:21.919Z]    at Microsoft.Azure.Functions.Worker.FunctionsApplication.InvokeFunctionAsync(FunctionContext context) in D:\a\_work\1\s\src\DotNetWorker.Core\FunctionsApplication.cs:line 89. [2024-07-13T02:28:58.728Z] Executed 'Functions.Login' (Failed, Id=b4005c4b-a335-46f7-8dc7-cf24b1d62fe7, Duration=48071ms) [2024-07-13T02:28:58.730Z] System.Private.CoreLib: Exception while executing function: Functions.Login. Microsoft.Azure.WebJobs.Script.Grpc: Failed to proxy request with ForwarderError: RequestCanceled. System.Net.Http: The operation was canceled. System.Net.Sockets: Unable to read data from the transport connection: The I/O operation has been aborted because of either a thread exit or an application request.. The I/O operation has been aborted because of either a thread exit or an application request.

原因分析

  1. Logger参数为空:异常栈显示错误发生在LoginFunction.cs第86行的log.LogError调用,说明自定义异常抛出后进入了catch块,但此时方法参数中的log为null,触发了二次异常,掩盖了原本的自定义异常。
  2. 注入配置问题:在Azure函数Isolated Worker模式下,若函数类未通过构造函数正确注入ILogger,或者方法参数的ILogger未被运行时正确解析,就会导致log参数为空。
  3. 异常传播优先级:自定义异常抛出后,程序先进入catch块执行日志操作,此时Logger空引用异常先被抛出,导致原本的自定义异常未被正常处理。

解决方案

  1. 改用构造函数注入Logger:将ILogger注入到函数类的构造函数中,避免方法参数注入可能出现的空值问题:
    private readonly ILogger<LoginFunction> _logger;
    
    public LoginFunction(ILogger<LoginFunction> logger)
    {
        _logger = logger;
    }
    
    之后在方法中使用类级别的_logger替代方法参数的log。
  2. 添加Logger空值检查:在所有catch块的日志操作前,先判断Logger是否为空,避免触发二次异常:
    catch (AuthenticationServiceException ex)
    {
        _logger?.LogError($"Error during token generation: {ex.Message}", ex);
        // 其余错误响应逻辑不变
    }
    
  3. 验证自定义异常的可见性:确保自定义异常类是public修饰的,且能被Azure函数项目正确引用,避免因异常类型无法识别而进入通用Exception catch块,进而触发Logger空引用。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 22:34:50