目录
FromBody的JavaScript fetch调用有问题
JS获取FromBody & 使用JSON.Stringify
GitHub - raddevus/BindApi:在dotnet webapi和JavaScript Fetch中使用自动绑定的文章示例代码[^]
介绍
这是一篇快速文章,其中包含多个示例,演示了如何利用.NET Core WebAPI自动绑定功能的用户。我知道这通常被称为模型绑定,但我的示例涵盖了您只需将一个参数值传递给WebAPI方法的情况。
本文将如何工作
我将为每个示例提供两件事,以便你可以确切地了解自动绑定功能在.NET Core WebAPI中的工作原理。
- WebAPI方法定义(通过最小API的示例代码)
- JavaScript Fetch示例,可用于POST到WebAPI并查看结果
WebAPI参数属性
.NET Core WebAPI方法允许你使用不同属性的数量来标记参数:
- [FromQuery]
- [FromForm]
- [FromBody]
- [FromHeader]
- [FromServices]*
- [FromKeyedServices]*
*最后两个是.NET Core 8.x的全新功能,我不会在这里介绍它们。但是,我将通过完整的示例介绍前四个属性。
背景
在撰写另一篇文章时,每个用户都有自己的数据库(即将推出),我遇到了.NET Core WebAPIs中的自动绑定参数问题。
我已经编写了许多.NET Core WebAPI,但我注意到有时它比其他Web API更容易。我通常喜欢我的WebAPI使用[FromBody]从发布的正文中获取数据。
但是,我发现了在某些情况下这对我来说会失败的确切原因。
在WebAPI参数上使用FromQuery
让我们从我认为最简单的发布数据方法开始,在WebAPI方法上使用[FromQuery]属性。
源代码
- 我将在此处添加最短的代码,以便创建快速讨论,但如果您想查看源代码,可以从本文顶部下载或在我的GitHub存储库中获取。
- 我将创建包含最小WebAPI的非常小的项目,并且几乎在每种情况下方法的名称都相同,只是它们将为每个示例包含一个数字,以便您可以在本地启动WebAPI,并根据需要自己尝试这些示例。
启动WebAPI:使用浏览器进行测试
启动WebAPI(从下载的代码)后,您可以加载URL并使用浏览器控制台使用JavaScript Fetch API POST到WebAPI。以下是其外观的快照:
[FromQuery]示例代码
[HttpPost] public ActionResult RegisterUser1([FromQuery] string uuid)
在我们的最小API中,您将看到此方法按以下方式定义:
app.MapPost("/RegisterUser1", IResult ([FromQuery] string uuid) => {
return Results.Ok(new { Message = $"QUERY - You sent in uuid: {uuid}" });
});
JavaScript获取[FromQuery]
fetch(`http://localhost:5247/RegisterUser1?uuid=987-fake-uuid-1234`,
{method: 'POST'})
.then(response => response.json())
.then(data => console.log(data));
这个很容易。如果您在URL中提供queryString项(?uuid),则该项目将自动绑定到uuid字符串变量,您将获得有效结果。但是,如果未提供queryString值,则当WebAPI尝试自动绑定时,它将发生错误。
错误
An unhandled exception has occurred while executing the request.
Microsoft.AspNetCore.Http.BadHttpRequestException:
Required parameter "string uuid" was not provided from query string.
在WebAPI参数上使用FromForm
让我们使用该[FromForm]属性定义第二个WebAPI方法。
app.MapPost("/RegisterUser2", IResult ([FromForm] string uuid) => {
return Results.Ok(new { Message = $"FORM - You sent in uuid: {uuid}" });
})
注意——防伪(AntiForgery)
一旦我开始针对上述命令运行Fetch,我就开始在WebAPI端收到一条奇怪的错误消息,如下所示:
Unhandled exception. System.InvalidOperationException: Unable to find the required
services. Please add all the required services by calling
'IServiceCollection.AddAntiforgery' in the application startup code.
at Microsoft.AspNetCore.Builder
.AntiforgeryApplicationBuilderExtensions
.VerifyAntiforgeryServicesAreRegistered(IApplicationBuilder builder)
对.NET Core最小WebAPIs的重大更改
幸运的是,我能够搜索并发现问题是什么以及如何解决它。嘘!
中断性变更:IFormFile参数需要防伪检查——.NET|Microsoft 学习[^]
FromForm的WebAPI略有变化
app.MapPost("/RegisterUser2", IResult ([FromForm] string uuid) => {
return Results.Ok(new { Message = $"FORM - You sent in uuid: {uuid}" });
})
.DisableAntiforgery()
嘘!它总是一些东西!
FromForm的JS获取调用
有一些设置可以将我们的数据传递到Web表单上。首先,我们必须创建FormData对象并添加我们的名称/值对。之后,我们可以发布数据。
// 1. Create the FormData object
var fd = new FormData();
// 2. Append the name/value pair(s) for values you want to send
fd.append("uuid", "123-test2-2345-uuid-551");
fetch(`http://localhost:5247/RegisterUser2`,{
method:'POST',
body:fd, // add the FormData object to the body data which will be posted
})
.then(response => response.json())
.then(data => console.log(data));
现在,我们已经使用了两个运行良好的不同属性。让我们深入研究一下使用[FromBody]属性,这将带来很大的困难。
在WebAPI参数上使用FromBody
app.MapPost("/RegisterUser3", IResult ([FromBody] string uuid) => {
return Results.Ok(new { Message = $"FORM - You sent in uuid: {uuid}" });
})
FromBody的JavaScript fetch调用有问题
乍一看,这个应该很容易,因为你可能会认为你应该能够在正文上传入一个字符串值。无论如何,我就是这么想的。
不起作用
app.MapPost("/RegisterUser3", IResult ([FromBody] string uuid) => {
return Results.Ok(new { Message = $"FORM - You sent in uuid: {uuid}" });
})
但是,这甚至不会通过您的浏览器,因为它希望您为正文定义一个对象(在两个{ }大括号之间)。
接下来,您可能认为您可以创建一个包含目标参数(uuid)名称的对象并传递它,如下所示:
不起作用2
var data = {"uuid":"yaka-yaka"};
fetch(`http://localhost:5247/RegisterUser3`,{
method:'POST',
body:data,
})
.then(response => response.json())
.then(data => console.log(data));
这是行不通的,也不会通过您的浏览器。同样,它认为身体物体的构造不正确。
不起作用3
fetch(`http://localhost:5247/RegisterUser3`,{
method:'POST',
body:{"uuid":"this-is-uuid-123"},
})
.then(response => response.json())
.then(data => console.log(data));
还是行不通。你会得到一个415 Unsupported Media type。因此,您需要添加Content-Type对象并将其设置为JSON,以便Fetch调用知道您打算使用JSON进行调用。这是因为您在正文中添加了大括号。
不起作用4:但确实击中了WebAPI
这个实际上确实击中了WebAPI。
fetch(`http://localhost:5247/RegisterUser3`,{
method:'POST',
body:{"uuid":"this-is-uuid-123"},
headers: {
'Content-type':'application/json; charset=UTF-8',
},
})
.then(response => response.json())
.then(data => console.log(data));
但是,现在您从服务器收到错误,其中指出:
自动绑定错误:WebAPI无法绑定到变量
Microsoft.AspNetCore.Http.BadHttpRequestException: Failed to read parameter
"string uuid" from the request body as JSON.
这是一个自动绑定错误,因为WebAPI没有看到uuid值,即使我们确实将其与body对象一起传递。
在这一点上,我大吃一惊。
问题是什么:你如何解决它?
好吧,你不能在JavaScript方面解决它。后来发现,你不能将字符串值作为参数直接传递给WebAPI。相反,您必须创建一个与client-side object 匹配的server-side object。
创建服务器端对象
下面是我们要添加到Program.cs中的新类(仅用于示例目的):
record UuidHolder{
public string uuid{get;set;}
}
为工作示例定义 RegisterUser4
我们将使用以下WebAPI作为我们的工作示例:
app.MapPost("/RegisterUser4", IResult ([FromBody] UuidHolder uuid) => {
return Results.Ok(new { Message = $"BODY - You sent in uuid: {uuid.uuid}" });
})
这将保留RegisterUser3 WebAPI方法,因此您可以看到无法对其进行绑定。
现在,我们必须稍微改变我们的JavaScript Fetch调用以使用JSON.stringify(),然后一切都会正常工作。
JS获取FromBody & 使用JSON.Stringify
fetch(`http://localhost:5247/RegisterUser4`,{
method:'POST',
body:JSON.stringify({"uuid":"this-is-uuid-123"}),
headers: {
'Content-type':'application/json; charset=UTF-8',
},
})
.then(response => response.json())
.then(data => console.log(data));
它是有效的!
我以为将数据发送到身体上是最简单的方法,但这是最困难的。让我们介绍最后一个(FromHeader)并总结一下。
在WebAPI参数上使用FromHeader
这是最后一个,它允许我们将数据放在标题中并发布它。
app.MapPost("/RegisterUser5", IResult ([FromHeader] string uuid) => {
return Results.Ok(new { Message = $"HEADER - You sent in uuid: {uuid}" });
})
JavaScript获取[FromHeader]
这个很容易做到,但我不完全确定为什么我们要使用标题发布数据。
但是,它确实可以帮助我们将数据自动绑定到我们可能具有的某些标头值。
fetch(`http://localhost:5247/RegisterUser5`,{
method:'POST',
headers: {
'Content-type':'application/json; charset=UTF-8',
"uuid":"123-456-789"
},
})
.then(response => response.json())
.then(data => console.log(data));
现在,可以使用任何基本属性在.NET Core WebAPI方法中自动绑定,数据将通过。当它没有时,你就会明白去哪里看。
额外材料OpenApi文档
哇!我刚刚发现(在发表我的文章后)我实际上通过OpenApi获得了一些关于我的API的免费文档。
dotnet项目模板在Program.cs文件中添加了几行,用于处理生成文档。
app.UseSwagger();
app.UseSwaggerUI();
然后,在每个post方法上,我添加了以下调用:
.WithOpenApi();
如何查看OpenApi文档?
我在网上搜索了所有内容,并扫描了这个解释OpenApi文档的冗长文档,但它从未向我展示如何查看文档。这太疯狂了!我终于找到了如何在此处查看自动生成的文档。
要检查文档,请启动webapi并转到:http://localhost:5247/swagger
您将看到以下内容:
您也可以通过curl试用api
文档处于活动状态,您现在可以通过UI试用API。它在后台使用curl发布到webapi您将看到curl可以正确地发布到body方法。但是,它在FromForm上失败。真的很有意思。
https://www.codeproject.com/Articles/5383531/NET-Core-WebAPI-Auto-Binding-Parameters-When-Calli