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

C# WebAPI强制反序列化不同于控制器方法中定义的对象,c#,asp.net-web-api,C#,Asp.net Web Api,我试图以这样一种方式介绍automapper,即对于WebAPI,所有DTO都是透明的,所以基本上我希望在运行时映射对象,并在它们到达控制器方法之前将它们转换为域对象 我有自动映射过滤器属性 [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] public class AutoMapAttribute : ActionFilterAttribute { private readonly Type _so

我试图以这样一种方式介绍automapper,即对于WebAPI,所有DTO都是透明的,所以基本上我希望在运行时映射对象,并在它们到达控制器方法之前将它们转换为域对象

我有自动映射过滤器属性

[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; }
    }
}
它工作得很好,因为controlelr方法中的产品变量实际上是我的领域产品现在我想将对象类型更改为控制器方法定义中的具体Domain.Product类型。不幸的是,WebAPI试图立即将来自请求的对象反序列化到此对象类型,这打破了整个想法


如果有帮助的话,我可以使用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);        
        }
    }
    

  • 您正在使用OWIN吗?请查看我的更新答案。是否要将具体类型设置为公共DTO类型,而不是域模型类型?或者,如果您将域模型设置为具体类型,那么您肯定不需要automapper??或者是我搞错了吗?不,我只是希望DTO对WebAPI完全透明,所以到DTO的映射是按约定完成的。您能再解释一下为什么我们需要“重读”actionfilter中的内容吗?这是否意味着我们仍然尝试反序列化到控制器中定义的对象中,然后使用第2点放弃该值?有没有一种方法不首先反序列化到控制器类型?@VsMax方法
    Post(Domain.model.Product)
    中的模型绑定将首先被处理,请求上下文将被读取,这样上下文流在之后就不再可读了。因此,是的,我们将内容反序列化两次,首先在控制器中,然后在过滤器中。我希望有一个干净/正确的方法来实现相同的目标,而不需要多次反序列化。您可以通过误用
    authorized属性
    并在
    OnAuthorization
    方法中进行类型映射,并重写
    IsAuthorized(HttpActionContext actionContext)=>true
    来实现它。但正如我所说的,它并没有那么干净。它可以工作,尽管我不得不调整一些其他代码使其工作:)谢谢。
    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);
    }
    
    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);        
        }
    }