Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Do you know why this works:

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

But when I replace it with a tuple, this doesn’t work?

[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 and .password are both null.

Are you not allowed to use tuples in a controller?

like image 985
Eric Vaughan Avatar asked Apr 24 '18 15:04

Eric Vaughan


People also ask

What is the most efficient way to use air conditioning?

Run your air conditioner on the lowest setting required and turn the ceiling fan on. This way, cool air will spread across a wide area, maintaining a comfortable environment. Instead of choosing between an air conditioner and a fan, it's best to combine the two for maximum comfort and efficiency.

Is it better to leave AC on all day?

Your AC will actually run longer overall if it is left on all day instead of being shut off. If you turn it off for part of the day, it runs less and results in more energy savings for you. In almost all cases, it will save you money to shut off your AC while you are away from home.

How do you use AC for the first time?

For most central air systems, the process is simple. Simply move the switch on your thermostat from “Heat” to “Cool”. If your system was off entirely, you may need to move the switch from “Off” to “Cool” instead. Once you turn your system on, be sure to close any open windows to conserve energy.


2 Answers

It doesn't work because named tuple names are not quite "real", it's mostly syntax sugar provided by compiler. If you look at ValueTuple set of types, by which named tuples are represented, you will see that they have properties like Item1, Item2 and so on.

Compiler will rewrite all your references to named tuple names to their real names (Item1 etc). For example you have this:

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

But when you compile that, what you really will have is this:

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

Your names are now only in metadata attribute TupleElementNames, but not in code.

For that reason, when you post something like:

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

to your action, asp.net cannot bind. But if you would post:

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

then it will bind with no problems. You can write custom binder probably, which can use TupleElementNames attribute, but there is no reason to really. Just use separate parameters or real model as suggested in comments. Your action input parameters is not some throwaway thing. You might later want to validate them, generate documentation from the model and so on.

like image 101
Evk Avatar answered Oct 20 '22 05:10

Evk


You can use this package. This package binds json body to your models.

Github Repo

Installation

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

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

Usage

Modify startup.cs like

using M6T.Core.TupleModelBinder;
....

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

Post request body

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

And in your controller use it like

[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});
    }
}
like image 34
Alper Tokcan Avatar answered Oct 20 '22 04:10

Alper Tokcan