またまた
MagicOnionの話。
ASP.NET Coreには標準でバリデーションの仕組みがあり、APIの引数やDTOのプロパティに属性を付けることでバリデーションが出来て便利なんだけど、MagicOnionは残念ながらそういうの対応していないっぽい。
そこで、自力でValidationFilterみたいなのを作って、同じようなことができるようにやってみたので、その内容を書いておく。確認したバージョンは4.2.0。
結論から書くと、フィルタの実装はこんな感じ。
using System;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using MagicOnion;
using MagicOnion.Server;
using MessagePack;
public class ValidationFilterAttribute : MagicOnionFilterAttribute
{
public override async ValueTask Invoke(ServiceContext context, Func<ServiceContext, ValueTask> next)
{
this.Validate(context);
await next(context);
}
private void Validate(ServiceContext context)
{
var parameterTypes = context.MethodInfo.GetParameters();
if (parameterTypes.Length == 0)
{
return;
}
var resolver = context.SerializerOptions.Resolver;
var requestType = MagicOnionMarshallers.CreateRequestTypeAndSetResolver(context.CallContext.Method, parameterTypes, ref resolver);
var param = MessagePackSerializer.Deserialize(requestType, context.GetRawRequest(), context.SerializerOptions);
ValidateParameters(parameterTypes, param);
}
private static void ValidateParameters(ParameterInfo[] parameterTypes, object valueOrTuple)
{
var valueType = valueOrTuple != null ? valueOrTuple.GetType() : typeof(object);
if (valueType.IsGenericType && valueType.Name == typeof(DynamicArgumentTuple<,>).Name)
{
for (var i = 0; i < parameterTypes.Length; i++)
{
var prop = valueType.GetField("Item" + (i + 1));
ValidateParameter(parameterTypes[i], prop != null ? prop.GetValue(valueOrTuple) : null);
}
}
else
{
ValidateParameter(parameterTypes[0], valueOrTuple);
}
}
private static void ValidateParameter(ParameterInfo type, object value)
{
var customAttributes = type.GetCustomAttributes<ValidationAttribute>(true);
if (customAttributes.Any())
{
Validator.ValidateValue(value, new ValidationContext(value ?? new object()), customAttributes);
}
if (type.ParameterType.IsClass && value != null)
{
Validator.ValidateObject(value, new ValidationContext(value), true);
}
}
}
これで、こんな感じに引数やDTOのプロパティに属性を付けてバリデーションが出来る。
public async UnaryResult Login([Required] string id, [Required] string password)
{
// 中略
}
以下解説。
Tag:
.NET
MagicOnion