【问题标题】:Entity Framework / WebApi 2 permission check implementationEntity Framework / WebApi 2 权限检查实现
【发布时间】:2014-01-24 23:55:23
【问题描述】:

我正在使用 ASP.NET WebApi 2 和 Entity Framework 6.1.0-alpha1 编写一个简单的“Todo App”。我的目标是限制访问,每个用户只能查看/编辑自己的待办事项。

例子:

    // GET api/Todo/5
    [ResponseType(typeof(Todo))]
    public async Task<IHttpActionResult> GetTodo(int id)
    {
        var todo = await _db.Todos.FindAsync(id);

        if (todo == null)
        {
            return NotFound();
        }

        if (todo.CreatorId != _currentUser.Id)
        {
            return StatusCode(HttpStatusCode.Forbidden); 
        }

        return Ok(todo);
    }

没关系。为删除添加了类似的检查,并在创建时将 CreatorId 设置为当前用户的 ID。但是,我在更新时遇到了问题。

我试过了:

    // PUT api/Todo/5
    public async Task<IHttpActionResult> PutTodo(int id, Todo todo)
    {
        if (!ModelState.IsValid)
        {
            return BadRequest(ModelState);
        }

        if (id != todo.Id)
        {
            return BadRequest();
        }


        // --- No exception if I remove this block - BEGIN ---
        var original = await _db.Todos.FindAsync(id);

        if (original.CreatorId != _currentUser.Id || original.CreatorId != todo.CreatorId)
        {
            return StatusCode(HttpStatusCode.Forbidden);
        }
        // --- No exception if I remove this block - END ---

        _db.Entry(todo).State = EntityState.Modified; // Exception thrown here

        try
        {
            await _db.SaveChangesAsync();
        }
        catch (DbUpdateConcurrencyException)
        {
            if (!TodoExists(id))
            {
                return NotFound();
            }
            else
            {
                throw;
            }
        }

        return StatusCode(HttpStatusCode.NoContent);
    }

但是,System.InvalidOperationException 它抛出了标记线:

System.InvalidOperationException

附加类型为“ModernWeb.Domain.Models.Todo”的实体失败 因为另一个相同类型的实体已经有相同的主 核心价值。使用“附加”方法或设置时可能会发生这种情况 实体的状态为“未更改”或“已修改”,如果存在 该图具有冲突的键值。这可能是因为一些 实体是新的,尚未收到数据库生成的密钥 价值观。在这种情况下,使用“添加”方法或“添加”实体状态 跟踪图,然后将非新实体的状态设置为 “未更改”或“已修改”(视情况而定)。

如果我使用 FindByAsync() 删除块,它不会抛出异常。

我也尝试使用_db.Entry(todo).OriginalValue,但找不到有效的语法。

我该如何克服这个问题?对于这种情况有什么最佳做法吗?

【问题讨论】:

    标签: c# entity-framework asp.net-web-api entity-framework-6 asp.net-web-api2


    【解决方案1】:

    当您调用FindAsync 时,返回的实体实例已附加到上下文。所以没有理由_db.Entry(todo).State = EntityState.Modified;

    更新

    我想我明白你在这里想要做什么。试试这个:

    var original = await _db.Todos.AsNoTracking()
        .SingleOrDefaultAsync(x => x.Id == id);
    
    if (original.CreatorId != _currentUser.Id || original.CreatorId != todo.CreatorId)
    {
        return StatusCode(HttpStatusCode.Forbidden);
    }
    // --- No exception if I remove this block - END ---
    
    _db.Entry(todo).State = EntityState.Modified;
    

    当您调用 .AsNoTracking().SingleOrDefaultAsync 而不是 FindAsync 时,返回的 original 实体将不会附加到上下文中。然后,您可以将传递给控制器​​操作的那个设置为Modified,并且由于上下文尚未跟踪具有相同 id 的不同实体,因此您不应再收到该异常。

    作为辅助说明,由于传入参数的 Todo 实体已经具有 Id 属性,因此不需要在控制器操作中将其作为单独的参数传入。你应该可以这样做:

    public async Task<IHttpActionResult> PutTodo(Todo todo)
    {
        if (!ModelState.IsValid || todo == null)
        {
            return BadRequest(ModelState);
        }
    
        var original = await _db.Todos.AsNoTracking()
            .SingleOrDefaultAsync(x => x.Id == todo.Id);
    
        if (original == null) return NotFound();
    
        if (original.CreatorId != _currentUser.Id || original.CreatorId != todo.CreatorId)
        {
            return StatusCode(HttpStatusCode.Forbidden);
        }
    
        _db.Entry(todo).State = EntityState.Modified; // Exception thrown here
    
        try
        {
            await _db.SaveChangesAsync();
        }
        catch (DbUpdateConcurrencyException)
        {
            if (!TodoExists(todo.Id))
            {
                return NotFound();
            }
            else
            {
                throw;
            }
        }
    
        return StatusCode(HttpStatusCode.NoContent);
    }
    

    【讨论】:

    • 如果省略_db.Entry(todo),我必须手动修改original的属性,对吧? _db.Entry(todo) 的一大优势是我不必手动更新属性。还是我误会了什么?
    • 非常感谢,我会试试这个。此外,关于您的第二条注释:该方法(及其签名)是由 Visual Studio 脚手架创建的。有什么想法为什么还要创建一个单独的id
    • 假设您的实体的 id 为 TodoId 而不仅仅是 Id。这就是脚手架生成附加参数的原因。
    • 不,它的 ID 叫做Id
    • ... 这就是为什么您可以从控制器操作方法中省略参数。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-30
    • 2016-02-07
    • 1970-01-01
    • 1970-01-01
    • 2014-04-17
    • 1970-01-01
    相关资源
    最近更新 更多