在 ASP.NET Core Web API 控制器中使用 C# 7 元组

Using a C# 7 tuple in an ASP.NET Core Web API Controller

你知道为什么会这样吗:

public struct UserNameAndPassword
{
    public string username;
    public string password;
}


[HttpPost]
public IActionResult Create([FromBody]UserNameAndPassword usernameAndPassword)
{
    Console.WriteLine(usernameAndPassword);
    if (this.AuthenticationService.IsValidUserAndPasswordCombination(usernameAndPassword.username, usernameAndPassword.password))
        return new ObjectResult(GenerateToken(usernameAndPassword.username));
    return BadRequest();
}

但是当我用元组替换它时,这不起作用?

[HttpPost]
public IActionResult Create([FromBody](string username, string password) usernameAndPassword) //encrypt password?
{
    Console.WriteLine(usernameAndPassword);
    if (this.AuthenticationService.IsValidUserAndPasswordCombination(usernameAndPassword.username, usernameAndPassword.password))
        return new ObjectResult(GenerateToken(usernameAndPassword.username));
    return BadRequest();
}

usernameAndPassword.username.password 均为 null。

你不允许在控制器中使用元组吗?

它不起作用,因为命名的元组名称不完全 "real",它主要是编译器提供的语法糖。如果您查看 ValueTuple 类型集,命名元组由其表示,您会看到它们具有 Item1Item2 等属性。

编译器会将您对命名元组名称的所有引用重写为它们的真实名称(Item1 等)。例如你有这个:

static void Create((string username, string password) usernameAndPassword) {
    Console.WriteLine(usernameAndPassword.username);
    Console.WriteLine(usernameAndPassword.password);
}

但是当你编译它时,你真正拥有的是:

static void Create([TupleElementNames(new string[] {"username", "password"})] ValueTuple<string, string> usernameAndPassword)
{
  Console.WriteLine(usernameAndPassword.Item1);
  Console.WriteLine(usernameAndPassword.Item2);
}

您的名字现在仅在元数据属性 TupleElementNames 中,但不在代码中。

出于这个原因,当您 post 像这样:

{"username": "x", "password": "y"}

您的操作,asp.net无法绑定。但是如果你愿意 post:

{"item1": "x", "item2": "y"}

那么绑定就没有问题了。您可能可以编写自定义活页夹,它可以使用 TupleElementNames 属性,但没有理由真的这样做。只需按照评论中的建议使用单独的参数或真实模型。您的操作输入参数不是一次性的东西。您稍后可能想要验证它们、从模型生成文档等。

您可以使用 this 包。此包将 json 主体绑定到您的模型。

Github Repo

安装

//Nuget
Install-Package M6T.Core.TupleModelBinder -Version 1.0.0

//dotnet cli
dotnet add package M6T.Core.TupleModelBinder --version 1.0.0

用法

修改startup.cs喜欢

using M6T.Core.TupleModelBinder;
....

public void ConfigureServices(IServiceCollection services)
{
  services.AddMvc(options =>
  {
      options.ModelBinderProviders.Insert(0, new TupleModelBinderProvider());
  });
}

Post 请求正文

{
  "user" : {
    "Name":"Test",
    "Surname":"Test2",
    "Email":"example@example.com"
  },
  "someData" : "If you like it, you put a data on it"
}

并且在你的控制器中像

一样使用它
[HttpPost]
public IActionResult CreateUser((User user, string someData) request)
{
    using (var db = new DBContext())
    {
        var newUser = db.Users.Add(request.user);
        db.SaveChanges();
        return Json(new { userId = request.user.Id, someData = request.someData});
    }
}