我正在尝试使用
ODataController
在我的 8.0.0-beta
上实现创建和更新逻辑。我的控制器看起来像这样:
[HttpPost]
[Route("users/{key}/{operation}")]
public async Task<IActionResult> UpdateUser([FromODataUri] string key, [FromODataUri] string operation, [FromBody] Delta<User> userPatch)
{
IActionResult result = BadRequest($"Key:{key} / Operation:{operation.ToLower()}");
if (operation.ToLower() == "update")
{
result = await Patch(key, userPatch);
return result;
}
else if (operation.ToLower() == "create")
{
var newUser = new User();
userPatch.Patch(newUser);
var entity = await AddNewEntity(newUser, key);
result = Created(entity);
}
return result;
}
update
工作正常,我可以使用 REST 客户端进行验证(http://localhost:5000/users/MY-USER-6/update
)
然而,当我使用 REST 客户端调用
create
方法时 (http://localhost:5000/users/MY-USER-6/create
) 我看到 方法有效 并且数据被插入数据库并且函数退出没有问题,但是我在 REST 上得到的结果客户的回应是Status Code: 500 Internal Server Error
,但有以下例外:
System.InvalidOperationException: The request must have an associated EDM model. Consider using the extension method HttpConfiguration.MapODataServiceRoute to register a route that parses the OData URI and attaches the model information.
at Microsoft.AspNetCore.OData.Results.ResultHelpers.GenerateODataLink(HttpRequest request, Object entity, Boolean isEntityId)
at Microsoft.AspNetCore.OData.Results.CreatedODataResult`1.GenerateLocationHeader(HttpRequest request)
at Microsoft.AspNetCore.OData.Results.CreatedODataResult`1.ExecuteResultAsync(ActionContext context)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResultFilterAsync>g__Awaited|29_0[TFilter,TFilterAsync](ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResultExecutedContextSealed context)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.ResultNext[TFilter,TFilterAsync](State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeResultFilters()
--- End of stack trace from previous location ---
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResourceFilter>g__Awaited|24_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResourceExecutedContextSealed context)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeFilterPipelineAsync>g__Awaited|19_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Logged|17_1(ResourceInvoker invoker)
at Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)
顺便说一句,如果我将
result = Created(entity);
更改为 result = Ok(entity);
它工作正常!
根据一些博客和 channel9 视频,我正在尝试添加
endpoints.EnableDependencyInjection();
但看起来这不再可用或可能在 8.0.0-beta
.
我错过了什么?
当你使用
ODataController
你需要命名你的方法Post
如果它应该作为OData端点添加到你的url路径中。
确保从
UpdateUser
返回的类型已添加到您的 EDM 模型并链接到您的控制器
static IEdmModel GetEdmModel()
{
var builder = new ODataConventionModelBuilder();
builder.EntitySet<User>("Users");
return builder.GetEdmModel();
}
然后像这样添加控制器
services
.AddControllers()
.AddOData(opt => opt
.AddRouteComponents("v1", GetEdmModel())
.Filter().Select().Expand().OrderBy());
Users 端点将在
/v1/Users
可用 - 重要的是您的控制器名称是 UsersController
并且您的方法 UpdateUser
被重命名为 Post
.
您的端点当前可访问的原因是因为您添加了
[Route("users/{key}/{operation}")]
属性。因此,它将可用但不能作为 OData 端点。
如果你看
ODataController
的源代码是覆盖Created
和Updated
https://github.com/OData/AspNetCoreOData/blob/dd8b5cd73bd8cfa1756241171aa359452f60ae80/src/Microsoft.AspNetCore.OData/Routing/Controllers/ODataController.cs
查看
Created
的实现,它在创建响应时访问 EDM 模型
Uri location = GenerateLocationHeader(request);
由于您的目标端点(方法
UpdateUser
)不是具有链接 EDM 模型的 OData 端点,因此失败。
看看
Updated
的实现,它不使用 EDM 模型——这就是为什么即使您的端点不是 OData 端点,您也能成功地将其作为返回类型。
如果您不使用 OData 中间件,您可以将控制器更改为继承自
ControllerBase
。然后端点将像现在一样在 [Route("users/{key}/{operation}")]
处访问,并且 Created
将正常工作。
尝试像这样制作 EDM 模型和控制器
static IEdmModel GetEdmModel()
{
var builder = new ODataConventionModelBuilder();
builder.EntitySet<Droid>("Droids");
return builder.GetEdmModel();
}
...
services
.AddControllers()
.AddOData(opt => opt
.AddRouteComponents("v1", GetEdmModel())
.Filter().Select().Expand().OrderBy());
...
app.MapControllers();
然后创建控制器
public class DroidsController : ODataController
{
[EnableQuery]
public IActionResult WrongNamePost([FromBody]Droid droid)
{
return Created(droid);
}
}
您的端点应该在
/v1/Droids
处可用,但事实并非如此。
将您的方法重命名为
[EnableQuery]
public IActionResult Post([FromBody]Droid droid)
{
return Created(droid);
}
现在它可以作为 OData 端点在
/v1/Droids
上使用。
您可以通过添加 Swagger 来验证您的端点。
在
Program.cs
services
.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new() { Title = "OdataTurorial", Version = "v1" });
});
...
app.UseSwagger();
然后就可以在
http://localhost:YOUR_PORT/swagger/v1/swagger.json
看到端点是否添加正确了。