Using a C# 7 tuple in an ASP.NET Core Web API Controller
Solution 1:
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.
Solution 2:
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});
}
}