[英]C# OData Web API POST endpoint with IEnumerable in parameters returns error 400 the input was not valid
我在这里有一个怪异的行为。 我在这里有一个 controller (我在这里欺骗和混淆了很多东西......它仍然相关)
[Route("api/[controller]")]
[ApiController]
public class MyComplexTypeController : ControllerBase
{
//context...
public MyComplexTypeController()
{
//context..
}
[HttpPost]
// For simplicity, I have renamed the endpoint..
// In reality I just have ONE Post endpoint here
public ActionResult MyCallThatWorks(MyComplexObj param_origData)
{
// The param_origData is GREAT and working!
return Ok();
}
[HttpPost]
// For simplicity, I have renamed the endpoint.
// In reality I just have ONE Post endpoint here
public ActionResult MyCallThatDontWorks(IEnumerable<MyComplexObj> param_origData)
{
// I get a 400 "The input was not valid." in PostMan!
return Ok();
}
在这里你有startup.cs
:
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddAutoMapper(typeof(Startup2));
services.AddCors();
services.AddRazorPages();
services.AddOData();
services.AddControllers()
//https://docs.microsoft.com/en-us/aspnet/core/web-api/advanced/formatting?view=aspnetcore-5.0
.AddJsonOptions(options =>
options.JsonSerializerOptions.PropertyNamingPolicy = null);
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseCors(x => x.AllowAnyMethod().AllowCredentials().AllowAnyHeader().SetIsOriginAllowed(x => true));
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();
endpoints.MapControllers();
endpoints.Select().Filter().OrderBy().Expand().Count().MaxTop(null);
endpoints.MapODataRoute("odata", "api", MyGetEdmModel());
});
}
private static IEdmModel MyGetEdmModel()
{
ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
builder.EntitySet<MyComplexObj>("MyComplexType");
return builder.GetEdmModel();
}
}
事实上,我有 3 个复杂类型和 3 个端点。 我的两个端点与 OData 一起正常工作。
我的第三种类型,这里解释的那种,也适用(GET,POST,DELETE)!
但是:我的端点的POST
仅在我传递我的复杂 class 的一个实例时才有效。 如果我通过IEnumerable
(并正确传递 Postman [ { "bla":12" }, { "bla":24 }]
中的数据,它将不起作用。
我尝试了很多东西。 仅举几个:
删除 controller 属性中的[ApiController]
(抱歉,我找不到链接..) - 没用:我在我的参数中获得了NULL
值(但仍在输入方法)
尝试传递 DTO(同样的问题 - 错误 400)
试图在参数中创建一个动态/对象/字符串的传递(丑陋并且必须努力将其转换回来)
尝试过 OData Actions 和 Odata CollectionParameter function (没能幸存下来) -编辑:这就是解决方案!
试图通过它[FromBody]
,并且没有[FromBody]
(所以 xxx-form-encoded) - 正在输入方法,但得到了NULL
!
在 Postman 中,我尝试使用
{"":[{"Mycollection":"Of"},{"Complex":"Objet"}]}
也试过
=[{"Mycollection":"Of"},{"Complex":"Objet"}]
(如 MS docs..ahah 中某处所述)
我在 Google 中做了标准的“为什么 Odata 如此糟糕”,打算尝试 GraphQL,但我确实抗拒了......
我终于做了一个聪明的程序员必须做的事情:我从基地重新开始。
长话短说:OData 不能容忍我的参数签名与初始路由不匹配。
所以我不得不在我的Startup.cs
中删除这一行:
private static IEdmModel MyGetEdmModel()
{
ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
builder.EntitySet<MyComplexObj>("MyComplexType"); <--THIS ONE
builder.EntitySet<OtherComplexType>("OtherComplexType");
builder.EntitySet<OtherComplexType2>("OtherComplexType2");
return builder.GetEdmModel();
}
并且通过IEnumerable
效果很好!
问题:为什么?
现在,我无法对该特定实体进行完整的 OData 获取(无法找到非 OData 路由的服务容器)。 这很正常,我刚刚删除了它!
我刚刚找到了这个(在写完我的帖子之后):
WebApi OData 4,用于在 controller 中批量插入的第二个 POST 端点
似乎是答案,并保持一切“OData 批准”!
如此大的图景,为了能够发布与注册的 OData 类型不同的 TYPE,您必须创建一个名为 Action 的新端点(方法)。 此操作可以采用您定义的任何类型(在 startup.cs 中):
private static IEdmModel MyGetEdmModel()
{
ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
builder.EntitySet<xxx>("xx");
builder.EntitySet<yyyy>("yyy");
builder.EntitySet<MyComplexType>("MyComplexType");
// added this line here:
builder.EntityType<MyComplexType>().Collection
.Action("BulkAdd")
.CollectionParameter<MyComplexType>("MyXREF");
return builder.GetEdmModel();
}
然后,在controller的方法中:
[HttpPost]
public async Task<ActionResult> BulkAdd(ODataActionParameters parameters)
{
if (!ModelState.IsValid)
{
return BadRequest();
}
List<MycomplexType> bookings = ((IEnumerable<MycomplexType>)parameters["MyXREF"]).ToList();
瞧!
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.