aspnetcore
aspnetcore copied to clipboard
Support Form binding for Minimal endpoints
- [ ] Need to scope which features we will support for form binding.
- [ ] Support for IFormCollection
Thanks for contacting us.
We're moving this issue to the .NET 7 Planning milestone for future evaluation / consideration. We would like to keep this around to collect more feedback, which can help us with prioritizing this work. We will re-evaluate this issue, during our next planning meeting(s).
If we later determine, that the issue has no community involvement, or it's very rare and low-impact issue, we will close it - so that the team can focus on more important and high impact issues.
To learn more about what to expect next and how this issue will be handled you can read more about our triage process here.
As a reminder this will require CSRF supoprt.
I strongly belive this should be a priority!!
This has many uses and isn't what I would call a frivolous request. Many people have smashed their heads into this issue upgrading older code. The workaround is not pretty and can introduce confusion.
This is waiting on #38630.
We've moved this issue to the Backlog milestone. This means that it is not going to be worked on for the coming release. We will reassess the backlog following the current release and consider this item at that time. To learn more about our issue management process and to have better expectation regarding different types of issues you can read our Triage Process.
This is a show-stopper for us. We have got objects with plenty of properties (web pages with a long list of fields) in them that too with files (i.e. text, pdf, etc.). So, if there is no support for [FromForm], we have to go back to the controllers which we desperately would like to avoid. @DamianEdwards and @davidfowl please consider adding the in the next release. 🙏🙏🙏
What's the API scenario for form posts?
Isn't this "done" now as of 7.0 preview 7?
What's the API scenario for form posts?
@davidfowl , Thanks for your quick reply. Let's assume there is a product registration page where the product (class) has a bunch of properties, which has got some complex objects in it as well. In addition, each of the complex objects along with the product has a property to receive files (List<IFormFile> Files { get; set; } ). So it would be really helpful to have an option to receive it all via [FromForm] instead of finding everything from the HttpContext and applying some mapping. Mapping from the HttpContext can be tedious, and error-prone, which can easily be avoided with [FromForm], the way it works for the controller.
Isn't this "done" now as of 7.0 preview 7?
Files are supported but we have no intention of supporting forms for APIs.
@ak-hasanuzzaman Thanks for that. Can you elaborate more about the client, what sort of client is this and what does that code look like that produced the form and what is making the post and what does that code look like?
@davidfowl, The client is a react application, where we use axios to post data to the backend. The following is an example where an object and other complex objects in it, they do have the option to attach files. Eventually, we need to construct a big object and post it to the backend.
For example, the post method looks like somewhat similar to the one below (contrived example):
The point I am trying to make is to have the option to post data in one stroke. If you are not willing to support forms what are the alternatives when it comes to, posting data with everything in one go?
If you want me to clarify even more, please let me know. Thanks again.
Where's the form? I'd like to see the syntax of the form and how it was created (the syntax used for the fields). The thing I'm trying to understand the serialization format of your fields. Are you using the html helpers from mvc?
What's the API scenario for form posts?
FWIW the OAuth and OIDC specs require form-url-encoded requests payloads for all its programmatic endpoints. So there's an example for you.
https://www.rfc-editor.org/rfc/rfc6749.html#section-3.2
form.txt [This is still a work in progress, so please excuse the bad naming etc ]
We used object-to-formdata which serializes Objects to FormData instances. The onSubmit method in the .txt file attached might be interesting to have a look.
I'm actually OK with very simple form binding. I am not ok with lists, dictionaries, and the custom serialization format the MVC currently supports.
This is a major problem.
I have to transmit BOTH files AND parameters in the same request, which is ONLY possible using FormData!
Example:
POST /user/edit
- user id (request parameter)
- user name (request parameter)
- profile picture (file)
The profile picture file already uses the body so how should i transmit the request parameters? Only FormData can transmit both at the same time!
Other methods don't work:
- query string DOESNT WORK: should only be used in a GET request because it is against the spec to use query sting in a POST request, and is a security risk.
- json DOESNT WORK: can't use body because body is already used to transmit the file
- raw data DOESNT WORK: can't use body because body is already used to transmit the file
- encoded data DOESNT WORK: can't use body because body is already used to transmit the file
- file DOESNT WORK: can't use body because body is already used to transmit the file
- formdata WORKS! FormData serializes BOTH files AND parameters inside the body
The only option is FORM DATA !
I hope you understand the importance now.
We'll investigate in .NET 8.
@SnGmng Thanks for bringing it up mate. @davidfowl I have got something similar. I find the minimal API approach incredible for all the good reasons. But to POST other parameters with the file(s) does require [FormData]. To circumvent that issue we are using a handful of controller endpoints and the rest is minimal which was the only option we were left with.
It will be absolutely great if we could get away with the controllers completely by having the [FormData].
Thanks.
Other methods don't work:
From JavaScript for sure, I looked into this last week and I couldn't find a way to do a multi part post with the fetch APIs. So FormData is the only viable approach for forms + metadata it seems.
Generally multipart posts supports sending different content types in the same request, but it's not common.
@davidfowl thanks for considering. A simple example is a form where you put two text boxes and a place to upload a file. The user fills out those text fields, adds a file to upload and clicks 'submit'.
The only way to send the file and the simple text fields is a form. I wish there was another way, but there is not.
Always very simple form binding - just ints/strings/etc.
Hate this did not get additional consideration to be included in .NET 7 - lets cross fingers for 8.
(We have converted every endpoint to minimal APIs except for these - we have to leave as classic controllers...so not a deal breaker, just not clean)
Hi, just checking if the work being done here will support a list of IFormFiles and lists of other objects as formdata?
This seams like quite a standard thing that should be built in by default.
My scenario - Im building a centralised email system that all our system will be able to utilise. The api has the following properties -
- body -
string - Attachments -
List<IFormFile> - Recipients -
List<Recipient>(the recipient model contains 'name' and 'email' -etc
As this is a mixture of data and files formdata is the only valid option
Yes the below works as of 8.0.0-preview.4. Note that binding to complex objects or enumerables of complex objects is not directly supported though, so to populate your List<Recipient> you'll need to create them manually from the bound primitives, or create a custom type to bind to that implements BindAsync for the whole request body.
Note however that arrays of primitives are supported:
Thanks @DamianEdwards
Is there any docs or articles that demonstrate this?
Note that binding to complex objects or enumerables of complex objects is not directly supported though, so to populate your List<Recipient> you'll need to create them manually from the bound primitives, or create a custom type to bind to that implements BindAsync for the whole request body
See https://learn.microsoft.com/en-us/aspnet/core/fundamentals/minimal-apis/parameter-binding?view=aspnetcore-7.0#bindasync for docs
You could also take a look at https://github.com/DamianEdwards/MinimalApis.Extensions for some examples of implementing custom helpers/wrappers for binding.
I think we should keep the same behavior for minimal api and mvc controller (also see https://github.com/dotnet/aspnetcore/issues/49437 )
The inconsistence will make people confuse.
For example: in mvc controller:
IFormFileCollection files1 will only get those fileContents with name files1.
IFormFileCollection files2 will only get those fileContents with name files2.
But in minimal api, IFormFileCollection files1 and IFormFileCollection files2 both get all files.
app.MapPost("/MinimalApi/Upload", async (
string testStr,
int testInt,
IFormFileCollection files1,
IFormFileCollection files2,
IFormFile file1,
IFormFile file2) =>
{
await Task.CompletedTask;
return Results.Ok(files1.Count);
});
[HttpPost("Upload")]
public async Task<IActionResult> Upload(
[FromForm] string testStr,
[FromForm] int testInt,
[FromForm] IFormFileCollection files1,
[FromForm] IFormFileCollection files2,
[FromForm] IFormFile file1,
[FromForm] IFormFile file2)
{
return NoContent();
}
MultipartFormDataContent content = new()
{
// file
{ fileContent1, "files1", "Files1_1.txt" },
{ fileContent2, "files1", "Files1_2.txt" },
{ fileContent3, "files2", "Files2_1.txt" },
{ fileContent4, "files2", "Files2_2.txt" },
{ fileContent5, "file1", "File1.txt" },
{ fileContent6, "file2", "File2.txt" },
// payload
{ new StringContent("Hello"), "testStr" },
{ new StringContent("1"), "testInt" },
};
var response = await httpClient.PostAsync(url, content);
response.EnsureSuccessStatusCode();
Minimal API won’t be implementing model binding so there’s behavior inconsistency already. For this particular issue it might be worth considering a way to make the behavior consistent with MVC (maybe opt in)
We've shipped support for form-binding in Minimal APIs as of .NET 8. Closing this for now. We'll track follow-up work in other issues.