web-dev-qa-db-fra.com

Utilisation d'un tuple C # 7 dans un contrôleur d'API Web ASP.NET Core

Savez-vous pourquoi cela fonctionne:

    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();
    }

Mais quand je le remplace par un tuple, cela ne fonctionne pas?

     [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 et .password sont tous deux nuls.

N'êtes-vous pas autorisé à utiliser des tuples dans un contrôleur?

16
Eric Vaughan

Cela ne fonctionne pas parce que les noms de Tuple nommés ne sont pas tout à fait "réels", c'est principalement du sucre de syntaxe fourni par le compilateur. Si vous regardez ValueTuple ensemble de types, par lesquels les tuples nommés sont représentés, vous verrez qu'ils ont des propriétés comme Item1, Item2 etc.

Le compilateur réécrira toutes vos références aux noms de Tuple nommés dans leurs vrais noms (Item1 etc). Par exemple, vous avez ceci:

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

Mais quand vous compilez cela, ce que vous aurez vraiment, c'est ceci:

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

Vos noms sont désormais uniquement dans l'attribut de métadonnées TupleElementNames, mais pas dans le code.

Pour cette raison, lorsque vous postez quelque chose comme:

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

à votre action, asp.net ne peut pas se lier. Mais si vous postez:

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

alors il se liera sans problème. Vous pouvez probablement écrire un classeur personnalisé, qui peut utiliser l'attribut TupleElementNames, mais il n'y a aucune raison de vraiment le faire. Utilisez simplement des paramètres séparés ou un modèle réel comme suggéré dans les commentaires. Vos paramètres d'entrée d'action ne sont pas quelque chose de jetable. Vous voudrez peut-être plus tard les valider, générer de la documentation à partir du modèle, etc.

17
Evk

Vous pouvez utiliser le package this . Ce package lie le corps json à vos modèles.

Repo Github

Installation

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

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

Usage

Modifiez startup.cs comme

using M6T.Core.TupleModelBinder;
....

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

Corps de la demande de publication

{
  "user" : {
    "Name":"Test",
    "Surname":"Test2",
    "Email":"[email protected]"
  },
  "someData" : "If you like it, you put a data on it"
}

Et dans votre contrôleur, utilisez-le comme

[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});
    }
}
5
Alper Tokcan