使用.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);
}
}
现在,我可以将payload
与new StreamReader(request.Body)
一起使用,其余的..
但是,例如,如果我有密码字段,则我不希望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;}
}
但是在这里,我无法在有有效载荷的地方做最后的组合,并且我想省略任何sensitiveData。
如果我有请求模型,则可以查看其字段并检查它们是否敏感,是否可以更改模型中的值(假设它不会损害控制器实际接收的模型)。
谢谢。
答案 0 :(得分:2)
这是使用自定义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..
}
要处理不更改原件的情况,您可以按照以下说明进行克隆:Deep cloning objects
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);