WebApi Core 2.2 OData POST /Deals(Id)/DealFees 报404问题求助
首先明确:/Deals(14)/DealFees是完全合法的OData URI,这种路径属于OData中"通过父实体的导航属性创建子实体"的标准用法,被广泛支持。你遇到的404问题,本质是WebApi Core服务端没有配置对应的处理逻辑,而非URI本身不合法。
接下来拆解原因和解决步骤:
1. 为什么会发起两次POST请求?
Microsoft OData Client在默认情况下,如果检测到服务端未开启**深度插入(Deep Insert)**支持,会拆分父子实体的创建操作:
- 先POST父实体
Deal到/odata/Deals,获取生成的ID - 再通过绑定路径
/odata/Deals({dealId})/DealFees发起POST,创建关联的子实体DealFee
如果想改成一次请求完成深度插入,需要服务端和客户端同时配置,但先解决当前404问题更优先。
2. 服务端缺失的核心配置:绑定路径的Action处理
你的DealFees控制器大概率只有处理POST /odata/DealFees的常规Action,但没有处理绑定到Deal的POST请求的逻辑。需要添加对应的Action:
方法一:使用OData路由特性显式定义
在你的DealFeesController(或DealsController,更推荐前者)中添加如下Action:
using Microsoft.AspNetCore.OData; using Microsoft.AspNetCore.Mvc; [ApiController] [Route("odata/[controller]")] public class DealFeesController : ODataController { private readonly YourDbContext _context; public DealFeesController(YourDbContext context) { _context = context; } // 处理常规的独立POST创建 [HttpPost] public async Task<IActionResult> Post(DealFee dealFee) { if (!ModelState.IsValid) { return BadRequest(ModelState); } _context.DealFees.Add(dealFee); await _context.SaveChangesAsync(); return Created(dealFee); } // 处理绑定到Deal的POST创建(关键!) [HttpPost] [ODataRoute("Deals({dealId})/DealFees")] public async Task<IActionResult> PostToDealFees([FromODataUri] int dealId, [FromBody] DealFee dealFee) { if (!ModelState.IsValid) { return BadRequest(ModelState); } // 关联父实体ID dealFee.DealId = dealId; _context.DealFees.Add(dealFee); await _context.SaveChangesAsync(); return Created(dealFee); } }
方法二:依赖OData约定路由
如果你的控制器继承自ODataController,并且遵循OData的命名约定,也可以不用显式加[ODataRoute]:
- Action命名为
PostToDealFees(格式:PostTo{NavigationPropertyName}) - 参数包含父实体的主键(比如
[FromODataUri] int dealId)
OData会自动匹配/Deals({dealId})/DealFees的POST请求到这个Action。
3. 确认OData模型的导航属性配置
还要确保你的EdmModel中正确定义了Deal和DealFee的关联关系:
public IEdmModel GetEdmModel() { ODataConventionModelBuilder builder = new ODataConventionModelBuilder(); builder.EntitySet<Deal>("Deals"); builder.EntitySet<DealFee>("DealFees"); // 显式配置父子关联(如果约定识别不到的话) var dealEntity = builder.EntityType<Deal>(); dealEntity.HasMany(d => d.DealFees).WithOne(df => df.Deal).HasForeignKey(df => df.DealId); return builder.GetEdmModel(); }
4. 客户端侧的小检查
确保你在WPF中是通过父实体的导航属性添加子项,而不是单独添加:
// 正确的方式:通过父实体导航属性关联 var newDeal = new Deal { /* 父实体属性 */ }; var newDealFee = new DealFee { /* 子实体属性 */ }; newDeal.DealFees.Add(newDealFee); container.AddToDeals(newDeal); // 错误的方式:单独添加子项,会导致关联问题 // container.AddToDealFees(newDealFee); container.SaveChanges();
完成以上配置后,POST /odata/Deals(14)/DealFees的请求就能被服务端正确处理,不再返回404。
如果后续想启用深度插入(一次POST创建父子实体),可以在服务端开启支持,并在客户端设置SaveChangesOptions.PostOnlySetProperties或对应的深度插入配置,但这属于进阶优化了。
内容的提问来源于stack exchange,提问作者Chris Bordeman

