如何在 ASP NET 中嵌套 [FromHeader] 验证的模型

How to nest [FromHeader] validated models in ASP NET

提问人:liamocuz 提问时间:11/9/2023 最后编辑:Renaliamocuz 更新时间:11/9/2023 访问量:41

问:

对于我的 API,我有三个标头,这三个标头几乎都用于所有端点。但是,某些端点不需要这三个端点。但对于每个终结点,它将要求存在任何标头。

我希望能够创建模型来单独验证每个标头,然后创建另一个模型来将它们包装在一起,这样只指定一个模型会很有用,因为大多数端点需要验证所有三个,只有少数需要一两个。

我的目标是使方法签名更干净一些,并避免重复的代码。

// Models
public class MainId
{
    [FromHeader(Name = "id")]
    [Required]
    public string Id { get; set; }
}

public class UserId
{
    [FromHeader(Name = "userId")]
    [Required]
    public string Id { get; set; }
}

public class OtherId
{
    [FromHeader(Name = "otherId")]
    [Required]
    public string Id { get; set; }
}

public class AllHeaders
{
    [FromHeader]
    public MainId MainId { get; set; }
    
    [FromHeader]
    public UserId UserId { get; set; }

    [FromHeader]
    public OtherId OtherId { get; set; }
}



// Controllers
[ApiController]
public class MyController: ControllerBase 
{
    [HttpGet("/allThree")]
    public string allThree([FromHeader] AllHeaders headers)
    {
        // be able to access all three headers here
        string h1 = headers.MainId.Id;
        string h2 = headers.UserId.Id;
        string h3 = headers.OtherId.Id;
    }

    // I know I can do the above like so, but if I ever want to add headers, it just makes the function signature sloppier
    [HttpGet("allThreeV2")]
    public string allThreeV2([FromHeader] MainId mainId, [FromHeader] UserId userId, [FromHeader] OtherId otherId)
    {
        // can access all three
        string h1 = mainId.Id;
        string h2 = userId.Id;
        string h3 = userId.Id;
    }

    [HttpGet("/singleHeader")]
    public string oneHeader([FromHeader] MainId mainId)
    {
        // be able to access the one header here
        string h1 = mainId.Id;
    }
}

我遇到的错误是 AllHeaders 为 null,如果我在那里省略 [FromHeader],那么控制器将返回 415,因为它正在寻找 AllHeaders 类内部的字段,而不是类内部的字段。

我想知道这种模块化是否可以实现,或者我是否可以以另一种方式构建模型来实现这一点。我希望验证者可以以某种方式扁平化 AllHeaders 类以填充大多数字段。

C# REST ASP.Net-Core 验证

评论


答:

1赞 scharnyw 11/9/2023 #1

经过一些试验后,可以使用以下设置在 .NET 6 中实现此目的:

// Models
public class MainId
{
    [FromHeader(Name = "id")]
    [Required]
    public string Id { get; set; }
}

public class UserId
{
    [FromHeader(Name = "userId")]
    [Required]
    public string Id { get; set; }
}

public class OtherId
{
    [FromHeader(Name = "otherId")]
    [Required]
    public string Id { get; set; }
}

public class AllHeaders
{
    // No FromHeaderAttribute here because the MainId property is not bound from Header - it's the child property that's bound from header
    // But RequiredAttribute is necessary here otherwise MainId == null would be a valid setup, violating the invariants expected in controller code
    [Required]
    public MainId MainId { get; set; }

    [Required]
    public UserId UserId { get; set; }

    [Required]
    public OtherId OtherId { get; set; }
}

和控制器动作方法:

[HttpGet("/allThree")]
public string allThree(
    // You need the FromHeaderAttribute here to override the default binding logic for complex types, which is from request body for API controllers
    [FromHeader] AllHeaders headers)
{
    // be able to access all three headers here
    string h1 = headers.MainId.Id;
    string h2 = headers.UserId.Id;
    string h3 = headers.OtherId.Id;
}