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

如何解决ASP.NET Core API中「UserId为Null」的错误?

ASP.NET Core API中CreateStoryCommand的UserId为空问题排查与解决

问题现象

在ASP.NET Core API中创建故事时,CreateStoryRequest和StoryController中能正确获取到UserId,但CreateStoryCommand中的UserId始终为Null,最终触发404错误:

{
  "type": "https://tools.ietf.org/html/rfc9110#section-15.5.5",
  "title": "A 'Not Found' error has occurred.",
  "status": 404,
  "traceId": "00-e985c50c5dfd8a5c3fc981580dd24c3f-ae4d5bd6e3d3f789-00",
  "errorCodes": "customValue"
}

代码结构概述

  • StoryEntity:包含UserId等核心属性的实体类
  • StoryController:处理创建故事的POST请求
  • CreateStoryRequest:接收前端请求的DTO,包含Content、Image、UserId
  • CreateStoryCommand:MediatR命令,包含创建故事所需参数
  • CreateStoryCommandHandler:命令处理逻辑,负责创建故事实体并校验用户存在性
  • CreateStoryAsync:仓储层创建故事实体的方法

核心问题分析

从提供的代码片段来看,问题出在映射配置与参数传递不匹配,以及验证器规则逻辑错误:

1. 映射配置与控制器传参不匹配

StoryMappingConfig中定义的映射规则是针对元组(CreateStoryRequest request, string UserId, byte[] Image)到CreateStoryCommand:

public class StoryMappingConfig 
{
    public StoryMappingConfig(TypeAdapterConfig config)
    {
        config.NewConfig<(CreateStoryRequest request, string UserId, byte[] Image), CreateStoryCommand>()
            .Map(dest => dest.Image, src => src.Image)
            .Map(dest => dest.UserId, src => src.UserId)
            .Map(dest => dest, src => src.request);
    }
}

但在StoryController的CreateAsync方法中,传入的元组只有两个元素:(request, image),并没有传递UserId:

var createStoryResult = await _mediatr.Send(_mapper
    .Map<CreateStoryCommand>((request, image)));

这导致映射时无法获取到UserId,最终CreateStoryCommand中的UserId为Null。

2. 命令验证器逻辑错误

CreateStoryCommandValidator中的UserId验证规则逻辑倒置:

RuleFor(r => r.UserId)
    .NotEmpty().WithMessage("UserId must not be empty").When(r => r.UserId != Guid.Empty);

这里的When(r => r.UserId != Guid.Empty)表示只有当UserId不为空时才验证NotEmpty,完全违背了验证的初衷。当UserId为Null(或Empty Guid)时,验证规则不会触发,导致非法参数直接进入Handler,查询用户时返回404。

解决步骤

步骤1:修正映射传参或映射配置

方案A:修正控制器中的映射传参

在控制器中传递包含UserId的完整元组,确保映射能获取到UserId:

var createStoryResult = await _mediatr.Send(_mapper
    .Map<CreateStoryCommand>((request, request.UserId, image)));

方案B:调整映射配置(更简洁)

修改映射配置,直接从CreateStoryRequest和Image参数映射,无需单独传递UserId:

public class StoryMappingConfig 
{
    public StoryMappingConfig(TypeAdapterConfig config)
    {
        config.NewConfig<(CreateStoryRequest request, byte[] Image), CreateStoryCommand>()
            .Map(dest => dest.Image, src => src.Image)
            .Map(dest => dest.UserId, src => src.request.UserId)
            .Map(dest => dest.Content, src => src.request.Content);
    }
}

控制器中保持原有传参即可:

var createStoryResult = await _mediatr.Send(_mapper
    .Map<CreateStoryCommand>((request, image)));

步骤2:修正命令验证器规则

移除错误的When条件,确保UserId为空时触发验证:

public CreateStoryCommandValidator()
{
    RuleFor(r => r.UserId)
        .NotEmpty().WithMessage("UserId must not be empty");
    
    RuleFor(r => r.Content)
        .MaximumLength(1000).WithMessage("Content must not exceed 1000 characters.");

    RuleFor(r => r.Image)
        .Custom((image, context) =>
        {
            if (image != null && image.Length > (2 * 1024 * 1024))
            {
                context.AddFailure("Image", "File size must not exceed 2MB");
            }
        }); 
}

步骤3:调试确认参数传递

在StoryController的CreateAsync方法中添加日志或断点,确认request.UserId不为空;在CreateStoryCommandHandler的Handle方法中添加断点,验证request.UserId已正确传递。

额外优化建议

  • 避免在Handler中调用request.UserId.ToString(),直接使用Guid类型查询用户,减少类型转换:
    var userOrError = await _userRepository.GetUserByIdAsync(request.UserId);
    
  • 将CreatedAt = DateTime.Now改为CreatedAt = DateTime.UtcNow,确保时间一致性(实体类中已处理DateTimeKind.Utc,直接用UtcNow更规范)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 13:45:54