ASP.Net Core ODataController 属性路由在插入期间抛出 500 错误

问题描述 投票:0回答:1

我正在尝试使用

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
.

我错过了什么?

asp.net-core asp.net-core-mvc odata asp.net-core-webapi
1个回答
0
投票

当你使用

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);

https://github.com/OData/AspNetCoreOData/blob/dd8b5cd73bd8cfa1756241171aa359452f60ae80/src/Microsoft.AspNetCore.OData/Results/CreatedODataResult.cs#L52

由于您的目标端点(方法

UpdateUser
)不是具有链接 EDM 模型的 OData 端点,因此失败。

看看

Updated
的实现,它不使用 EDM 模型——这就是为什么即使您的端点不是 OData 端点,您也能成功地将其作为返回类型。

https://github.com/OData/AspNetCoreOData/blob/dd8b5cd73bd8cfa1756241171aa359452f60ae80/src/Microsoft.AspNetCore.OData/Results/UpdatedODataResult.cs#L40

如果您不使用 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
看到端点是否添加正确了。

© www.soinside.com 2019 - 2024. All rights reserved.