参数中带有 IEnumerable 的 C# OData Web API POST 端点返回错误 400,输入无效
Posted
技术标签:
【中文标题】参数中带有 IEnumerable 的 C# OData Web API POST 端点返回错误 400,输入无效【英文标题】:C# OData Web API POST endpoint with IEnumerable in parameters returns error 400 the input was not valid 【发布时间】:2021-05-06 02:30:35 【问题描述】:我在这里有一个怪异的行为。我这里有一个控制器(我在这里作弊和混淆了很多东西......它仍然相关)
[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
仅在我传递复杂类的一个实例时才有效。如果我传递IEnumerable
(并在邮递员[ "bla":12" , "bla":24 ]
中正确传递数据),它将不起作用。
我尝试了很多东西。仅举几例:
删除控制器属性中的[ApiController]
(抱歉,我没有找到链接..) - 不起作用:我在参数中获得了NULL
值(但仍然在输入方法)
尝试传递 DTO(同样的问题 - 错误 400)
试图在参数中创建一个动态/对象/字符串的传递(丑陋,必须争取将其转换回来)
尝试过 OData Actions 和 Odata CollectionParameter 函数(没能幸存下来) - EDIT:这就是解决方案!
试图通过 [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 路由的服务容器)。这很正常,我只是删除了它!
【问题讨论】:
【参考方案1】:我刚刚发现了这个(在写完我的帖子之后):WebApi OData 4 , second POST endpoint for bulk insertion in the controller
似乎是答案,并保持一切“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();
然后,在控制器的方法中:
[HttpPost]
public async Task<ActionResult> BulkAdd(ODataActionParameters parameters)
if (!ModelState.IsValid)
return BadRequest();
List<MycomplexType> bookings = ((IEnumerable<MycomplexType>)parameters["MyXREF"]).ToList();
瞧!
【讨论】:
以上是关于参数中带有 IEnumerable 的 C# OData Web API POST 端点返回错误 400,输入无效的主要内容,如果未能解决你的问题,请参考以下文章
在 C# 中为 IEnumerable<string> 的每个成员添加前缀/后缀