从c#中的WebApi OData(EF)响应中排除属性

Mar*_*tín 8 c# linq entity-framework odata asp.net-web-api

我正在使用C#中的WebApi项目(EF代码优先),我正在使用OData.我有一个"用户"模型,包含Id,Name,LastName,Email和Password.

在控制器例如我有这个代码:

// GET: odata/Users
[EnableQuery]
public IQueryable<User> GetUsers()
{
    return db.Users;
}
Run Code Online (Sandbox Code Playgroud)

如果我调用/ odata/Users,我将获得所有数据:Id,Name,LastName,Email和Password.

如何从结果中排除密码,但在控制器中保持可用以进行Linq查询?

小智 15

我对这个话题有点迟,但我认为这可能对你有帮助.

我假设您需要加密密码以用于存储目的.你有没有看过使用odata动作来设置密码?使用操作可以在设置实体时忽略密码属性,同时仍向最终用户公开干净的方式来更新密码.

第一:忽略密码属性

builder.EntitySet<UserInfo>("UserInfo").EntityType.Ignore(ui => ui.Password);
Run Code Online (Sandbox Code Playgroud)

第二:添加你的odata动作

builder.EntityType<UserInfo>().Action("SetPassword").Returns<IHttpActionResult>();
Run Code Online (Sandbox Code Playgroud)

然后将SetPassword方法添加到UserInfoController.

  • 这不是一个好的解决方案.正如我所说,我需要在EDM中提供密码!我只是想从响应中删除它! (2认同)

Cor*_*iuc 8

它可能有点晚,但优雅的解决方案是添加自定义QueryableSelectAttribute,然后只列出要在服务端选择的字段.在你的情况下,它看起来像这样:

public class QueryableSelectAttribute : ActionFilterAttribute
{
    private const string ODataSelectOption = "$select=";
    private string selectValue;

    public QueryableSelectAttribute(string select)
    {
        this.selectValue = select;
    }

    public override void OnActionExecuting(HttpActionContext actionContext)
    {
        base.OnActionExecuting(actionContext);

        var request = actionContext.Request;
        var query = request.RequestUri.Query.Substring(1);
        var parts = query.Split('&').ToList();

        for (int i = 0; i < parts.Count; i++)
        {
            string segment = parts[i];
            if (segment.StartsWith(ODataSelectOption, StringComparison.Ordinal))
            {
                parts.Remove(segment);
                break;
            }
        }

        parts.Add(ODataSelectOption + this.selectValue);

        var modifiedRequestUri = new UriBuilder(request.RequestUri);
        modifiedRequestUri.Query = string.Join("&", parts.Where(p => p.Length > 0));
        request.RequestUri = modifiedRequestUri.Uri;

        base.OnActionExecuting(actionContext);
    }
}
Run Code Online (Sandbox Code Playgroud)

在控制器中,您只需添加具有所需属性的属性:

[EnableQuery]
[QueryableSelect("Name,LastName,Email")]
public IQueryable<User> GetUsers()
{
    return db.Users;
}
Run Code Online (Sandbox Code Playgroud)

就是这样!

当然,相同的原则可以应用于定制QueryableExpandAttribute.


小智 6

在User Class的Password属性中添加[NotMapped]属性,如下所示:

public class User
{
    public int Id { get; set; }

    public string Name { get; set; }

    public string Email { get; set; }

    public string LastName {get; set; }

    [NotMapped]
    public string Password {get; set;}
}
Run Code Online (Sandbox Code Playgroud)

  • Maya,这对我不起作用,因为正如我在帖子中所说,我需要在控制器中保留属性以进行linq查询.如果我尝试使用NotMapped,则实体模型中不存在该属性.我需要该属性,但我只想将其隐藏在API响应中. (3认同)

ta.*_*.is 6

如何从结果中排除密码,但在控制器中保持可用以进行Linq查询?

忽略它.从ASP.NET Web API 2 OData的安全指南:

有两种方法可以从EDM中排除财产.您可以在模型类中的属性上设置[IgnoreDataMember]属性:

public class Employee
{
    public string Name { get; set; }
    public string Title { get; set; }
    [IgnoreDataMember]
    public decimal Salary { get; set; } // Not visible in the EDM
}
Run Code Online (Sandbox Code Playgroud)

您还可以通过编程方式从EDM中删除该属性:

var employees = modelBuilder.EntitySet<Employee>("Employees");
employees.EntityType.Ignore(emp => emp.Salary);
Run Code Online (Sandbox Code Playgroud)

  • 如果从EDM中排除该属性,则不会出现在GET响应中(这没关系),但是如果您尝试进行POST以在API中创建新用户,则不会收到密码,因为该属性不是EDM的一部分.这就是为什么我只需要为响应而不是所有EDM隐藏密码(我认为唯一的解决方案是使用我只需要响应的参数创建一个新类). (3认同)

Mar*_*tín 0

我为这个问题做了一个临时的解决方案(不是最好的解决方案,因为 UserInfo 不是实体类型并且不支持 $select 或 $expand)。我创建了一个名为 UserInfo 的新模型,其中包含我需要的属性(除了 User 之外):

public class UserInfo
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Email { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

然后我改变了控制器中的方法:

// GET: odata/Users
[EnableQuery]
public IQueryable<UserInfo> GetUsers()
{
    List<UserInfo> lstUserInfo = new List<UserInfo>();

    foreach(User usr in db.Users)
    {
        UserInfo userInfo = new UserInfo();
        userInfo.Id = usr.Id;
        userInfo.Name = usr.Name;
        userInfo.Email = usr.Email;

        lstUserInfo.Add(userInfo);
    }

    return lstUserInfo.AsQueryable();
}
Run Code Online (Sandbox Code Playgroud)

  • 这不会破坏 IQueryable 的设计吗?如果您想要 10,000,000 条记录中的 5 条记录怎么办? (11认同)