我正在开发一个。NET 5 Web Api,具有默认的System.Text.Json
模型绑定。对于几个dto,我需要这样的东西:
public class MyDto
{
public string Name { get; set; }
public string PublicDetails { get; set; }
[IncludeForRoles("admin", "staff")]
public string InternalDetails { get; set; }
}
如果用户具有除";管理员";或";工作人员";调用返回上述dto的端点;内部细节";应在模型绑定时忽略,而不应添加到序列化的dto中。有标准的方法吗?如果没有,我如何手动实现此行为?
有一个[JsonIgnore]
属性,它接受一个条件,如果属性为null或默认值,该条件将忽略属性。
假设您知道控制器中的角色,则仅在具有相关角色的情况下获取InternalDetails信息,如果没有,则将其保持为null。这也减少了查询时间,因为您没有得到不需要的信息。
我建议使用Newtonsoft。用于序列化的JSON库,并实现自定义ContractResolver以动态决定要序列化的字段。
我找到了一种满足要求但有以下缺点的变通方法:
- 从数据库加载不需要的数据(不能使用Automapper的
.ProjectTo
( - 映射后修改数据(无法将服务注入Automapper的
Profile
,请参阅此处( - 每个属性都必须添加两个属性,在某些情况下应该忽略这两个属性
你看,这不是一个美丽而优雅的解决方案。请随意推荐一个更好的!
属性:
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class IncludeForRolesAttribute : Attribute
{
public IncludeForRolesAttribute(params string[] roleNames)
{
RoleNames = roleNames;
}
public string[] RoleNames { get; }
}
Automapper映射操作:
public class RoleBasedSetNullAction<TSource, TDestination> : IMappingAction<TSource, TDestination>
{
private readonly ITokenAccessor _tokenAccessor;
public RoleBasedSetNullAction(ITokenAccessor tokenAccessor)
{
_tokenAccessor = tokenAccessor ?? throw new ArgumentNullException(nameof(tokenAccessor));
}
public void Process(TSource source, TDestination destination, ResolutionContext context)
{
IEnumerable<PropertyInfo> props = typeof(TDestination).GetProperties().Where(
prop => Attribute.IsDefined(prop, typeof(IncludeForRolesAttribute)));
foreach (PropertyInfo prop in props)
{
IncludeForRolesAttribute attr = prop.GetCustomAttribute<IncludeForRolesAttribute>();
if (!_tokenAccessor.UserRoles.Intersect(attr.RoleNames).Any())
{
prop.SetValue(destination, null);
}
}
}
}
DTO:
public class MyDto : BaseEntityDto
{
public string Name { get; set; }
public string PublicDetails { get; set; }
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
[IncludeForRoles("admin", "staff")]
public string InternalDetails { get; set; }
}
Automapper映射配置文件:
public class MyDtoMappingProfile : Profile
{
public MyDtoMappingProfile()
{
CreateMap<MyEntity, MyDto>()
.AfterMap<RoleBasedSetNullAction<MyEntity, MyDto>>();
}
}