使用 .net core 2.1 C# 7.1.
我有一个 API REST 服务器。
我有以下课程:
public class ActionTimingAttribute : IAsyncActionFilter
{
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
// do something before the action executes
DateTime execStart = DateTime.UtcNow;
var request = context.HttpContext.Request;
string payload = ""; // HERE I NEED THE PAYLOAD WITHOUT SENSITIVE DATA
//doing funky stuff
await next();
DateTime execEnd = DateTime.UtcNow;
double elapsed = (execEnd - execStart).TotalMilliseconds;
Log(payload, elapsed);
}
}
现在,我可以和new StreamReader(request.Body)
一起拿payload
,其余的。 但是,例如,如果我有一个密码字段,我不希望payload
具有此值。例如,我想将值更改为"#####"。
我对很多请求和不同的要求都有这个。 对于每个POST
请求,我都有一个代表收到的 json 的类......
显然我不关心HttpGet。
我的服务器中的一个方法看起来像这样(一个仅用于登录的示例..)
[HttpPost, Route("Auth/Login")]
public async Task<Responses.Login> Login (Requests.Login request)
我假设我需要某种属性,所以它看起来像:
public class Login
{
public string Email {set;get;}
[SensitiveData]
public string Password {set;get;}
}
但是在这里,我无法在我有有效负载的地方进行最终组合,我想省略任何敏感数据。
如果我有请求模型,我可以查看它的字段并检查它们是否敏感,如果是,则更改模型中的值(假设它不会损害控制器实际接收的模型)。
谢谢。
下面是使用自定义 NewtonSoft 序列化的解决方案。 反射代码可能有一些改进的余地。
public class SensitiveDataAttribute: Attribute
{
}
public sealed class SensitiveDataJsonConverter : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
foreach (PropertyInfo prop in value.GetType().GetProperties())
{
object[] attrs = prop.GetCustomAttributes(true);
if (attrs.Any(x => x is SensitiveDataAttribute))
prop.SetValue(value, "#####");
}
var t = JToken.FromObject(value);
if (t.Type != JTokenType.Object)
{
t.WriteTo(writer);
}
else
{
JObject o = (JObject)t;
o.WriteTo(writer);
}
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException("Unnecessary because CanRead is false. The type will skip the converter.");
}
public override bool CanRead => false;
public override bool CanConvert(Type objectType)
{
return true;
}
}
这就是它的使用方式。
public class MyObject
{
[SensitiveData]
public string Password { get; set; }
public string UserName { get; set; }
}
内ActionTimingAttribute : IAsyncActionFilter
private static readonly SensitiveDataJsonConverter SensitiveDataConverter = new SensitiveDataJsonConverter();
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
string safePayload;
if (context.ActionArguments.Count == 1)
{
var safeObject = context.ActionArguments[context.ActionArguments.Keys.ElementAt(0)];
safePayload = JsonConvert.SerializeObject(safeObject, Formatting.None, SensitiveDataConverter);
}
else
safePayload = request.StoreAndGetPayload();
// the rest..
}
要处理不更改原始文件,您可以像下面描述的那样进行克隆: 深度克隆对象
public static class ObjectCopier
{
public static T CloneJson<T>(this T source)
{
// Don't serialize a null object, simply return the default for that object
if (Object.ReferenceEquals(source, null))
{
return default(T);
}
// initialize inner objects individually
// for example in default constructor some list property initialized with some values,
// but in 'source' these items are cleaned -
// without ObjectCreationHandling.Replace default constructor values will be added to result
var deserializeSettings = new JsonSerializerSettings { ObjectCreationHandling = ObjectCreationHandling.Replace };
return JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(source), deserializeSettings);
}
}
然后,您的使用情况将如下所示:
safePayload = JsonConvert.SerializeObject(safeObject.CloneJson(), Formatting.None, SensitiveDataConverter);