【问题标题】:Using a C# 7 tuple in an ASP.NET Core Web API Controller在 ASP.NET Core Web API 控制器中使用 C# 7 元组
【发布时间】:2018-10-04 23:15:52
【问题描述】:

你知道为什么会这样吗:

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 均为空。

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

【问题讨论】:

  • 很确定元组不会像那样工作,除非您编写自定义模型绑定器。你为什么想要这个,它的可读性要差得多?制作一个合适的类或只有两个参数。
  • 就使用适当的类而言,这就是元组的部分设计目的 - 减少对丢弃类的需求,这些类会造成混乱。
  • 但是这些不是一次性的类,在这种情况下代码看起来更糟。作为方法的返回类型,它看起来更具可读性,但不如参数。
  • 我同意@DavidG。这里的缺陷在于将这些课程视为“一次性”。您绑定的类以创建合同并自行记录您的操作是如何工作的。它们绝不是“扔掉”。
  • 你不能只有一个 FromBody 参数吗?元组的东西对于在体内有多个参数而不为它们创建一个类会有点好。对于查询参数和成熟的主体对象来说,这将是一个很好的中间点。

标签: c# asp.net-core tuples asp.net-core-webapi c#-7.0


【解决方案1】:

您可以使用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());
  });
}

发布请求正文

{
  "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});
    }
}

【讨论】:

    【解决方案2】:

    它不起作用,因为命名元组名称不是很“真实”,它主要是编译器提供的语法糖。如果您查看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 中,但不在代码中。

    因此,当您发布类似以下内容时:

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

    对于您的操作,asp.net 无法绑定。但如果你要发帖:

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

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

    【讨论】:

    • 你拯救了我的一天..!!很好解释。非常感谢
    猜你喜欢
    • 1970-01-01
    • 2022-01-27
    • 1970-01-01
    • 2021-01-24
    • 2016-04-07
    • 1970-01-01
    • 2018-11-29
    • 2018-09-29
    • 2019-11-13
    相关资源
    最近更新 更多