WebAPI强制反序列化不同于控制器方法中定义的对象

时间:2016-07-26 07:10:02

标签: c# asp.net-web-api

我试图以这样的方式引入automapper:对于WebAPI,所有DTO都是透明的,所以基本上我想在运行时映射对象并在它们到达控制器方法之前将它们转换为Domain对象。

我有自动播放器过滤器属性

[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
    public class AutoMapAttribute : ActionFilterAttribute
{
    private readonly Type _sourceType;
    private readonly Type _destType;

    public AutoMapAttribute(Type sourceType, Type destType)
    {
        _sourceType = sourceType;
        _destType = destType;
    }

    #region Overrides of ActionFilterAttribute

    /// <summary>Occurs before the action method is invoked.</summary>
    /// <param name="actionContext">The action context.</param>
    public override void OnActionExecuting(HttpActionContext actionContext)
    {
        var mapper = ServiceLocator.Locator.GetInstance<IMapper>();
        var jsonObject = actionContext.ActionArguments.FirstOrDefault(x => x.Value.GetType() == typeof(Newtonsoft.Json.Linq.JObject));
        var dto = JsonConvert.DeserializeObject(jsonObject.Value.ToString(), _sourceType);
        object model = mapper.Map(dto, _sourceType, _destType);

        actionContext.ActionArguments[jsonObject.Key] = model;

        base.OnActionExecuting(actionContext);
    }

    #endregion

    #region Overrides of ActionFilterAttribute

    /// <summary>Occurs after the action method is invoked.</summary>
    /// <param name="actionExecutedContext">The action executed context.</param>
    public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
    {
        base.OnActionExecuted(actionExecutedContext);
    }

    #endregion

    public Type SourceType
    {
        get { return _sourceType; }
    }

    public Type DestType
    {
        get { return _destType; }
    }
}

和控制器方法:

    [HttpPost]
    [Route("")]
    [AutoMap(typeof(Public.Dto.Product), typeof(Domain.Model.Product))]
    public IHttpActionResult Post(object product)
    {
        _productBusinessLogic.Create(product);
        return Ok();
    }

并且它工作得很好,因为controlelr方法中的产品变量实际上是我的域产品。 现在我想在控制器方法定义中将对象类型更改为具体的Domain.Product类型。不幸的是,WebAPI会尝试立即反序列化来自Request的对象,这会破坏整个想法。

如果有帮助,我可以使用OWIN。

1 个答案:

答案 0 :(得分:0)

关键点是动作过滤器的处理是在Web API request stack中进行模型绑定后进行的。

使用OWIN我们可以将原始主体读取为源类型,然后将其映射到目标类型:

  1. 覆盖OnActionExecutingAsync方法:

    public override async Task OnActionExecutingAsync(HttpActionContext actionContext,
        CancellationToken cancellationToken)
    {
        var argumentKey = actionContext.ActionArguments.FirstOrDefault().Key;
    
        var owinContext = (OwinContext) actionContext.Request.Properties["MS_OwinContext"];
    
        owinContext.Request.Body.Seek(0, SeekOrigin.Begin);
    
        var source = await actionContext.Request.Content.ReadAsAsync(_sourceType, cancellationToken);
    
        actionContext.ActionArguments[argumentKey] = Mapper.Map(source, _sourceType, _destType);
    
        await base.OnActionExecutingAsync(actionContext, cancellationToken);
    }
    
  2. 要在动作过滤器中重新读取请求的内容,我们需要在OWIN Startup类中为管道添加一些处理程序:

    public class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            var config = new HttpConfiguration();
    
            app.Use(async (context, next) =>
            {
                var content = context.Request.Body;
    
                if (content == Stream.Null)
                {
                   await next();
                }
                else
                {
                    using (var stream = new MemoryStream())
                    {
                        await content.CopyToAsync(stream);
    
                        stream.Position = 0;
    
                        context.Request.Body = stream;
    
                        await next();
                    }
    
                    context.Request.Body = content;
                }
            });
    
            app.UseWebApi(config);        
        }
    }