C# Owin 自托管服务控制器读取方法参数对象的所有 属性 值

C# Owin self hosted service controller reads all property values of the method argument objects

我正在尝试用 Asp.Net 由 Owin 自托管的 Rest WebApi 服务替换随我的应用程序发布的 WCF 服务。

我认为序列化/反序列化只是针对 DataMember 属性完成的,但它似乎对不是这样的属性做了一些事情。

我将大致解释一下问题的背景是什么:

例如,WCF 服务接口是这样的:

[ServiceContract]
public interface ISessionService
{
    [OperationContract]
    LoginResult Login(LoginRequest request);
}

WebApi 版本控制器是这样的:

[RoutePrefix(nameof(ISessionService))]
public class SessionServiceController : ApiController
{
    [HttpPost]
    [Route(nameof(ISessionService.Login))]
    public IHttpActionResult Login([FromBody] LoginRequest request)
    {
        return this.Ok(this.LoginExecute(request));
    }
}

LoginExecute 方法并不重要,它只是 returns LoginResult class 的一个实例,并且仅用于 运行 服务方法逻辑。

我在服务参数的反序列化方面遇到了麻烦,因为用作参数的对象的所有属性似乎都被 Owin 管道中的某些内容读取。 我不打算深入解释为什么这会导致问题,但我可以简单地说,在某些情况下,各种 class 之间存在循环引用。 这会在读取对象实例的所有属性时导致无限循环,应该避免。
我注意到这个问题暂停了应用程序的执行,并且看到调用的堆栈跟踪正在执行 DelegatingHandler.SendAsync 方法。从这里我开始按F10一步步调试,看到读取了很多属性个getter。

我知道,要解决这个问题,我应该避免循环引用,但目前这是我无法承担的任务。

所以我想问:是否有一种解决方法可以防止 Web Api 反序列化读取反序列化参数对象的所有属性,就像 WCF 使用 DataContractSerializer 所做的那样?

编辑 24/11/2020 14:18

经过多次搜索,我找到了导致读取模型所有属性的原因:IBodyModelValidator 服务。 我找到了一个适合我的解决方案,我将用它来回答遇到这个“问题”的任何人

经过一些研究,我发现问题不是序列化,而是 IBodyModelValidator 服务,它正在读取模型的所有属性元数据,包括 属性 值。

我找到的解决方案是覆盖 HttpConfiguration.Services 中的 BodyModelValidator 服务。

这个 class 有一个名为 bool ShouldValidateType(Type type) 的方法,它可以避免对类型进行验证,并且由于我没有对模型使用任何集成验证,所以我可以从中跳过整个类型。

我使用自定义属性来确定 class 是否必须执行验证

步骤如下:

创建 WebApiOptionsAttribute class

[AttributeUsage(AttributeTargets.Class, Inherited = true, AllowMultiple = false)]
public sealed class WebApiOptionsAttribute : Attribute
{

    public bool IsBodyModelValidatorDisabled { get; set; } = false;

    public static bool GetIsBodyModelValidatorDisabled(Type type)
    {
        return type.GetCustomAttributes(typeof(WebApiOptionsAttribute), true).FirstOrDefault() is WebApiOptionsAttribute attribute ? attribute.IsBodyModelValidatorDisabled : false;
    }

}

您可以将属性应用于任何 class 以控制是否应避免验证

创建一个 WebApiOptionsBodyModelValidator class

public class WebApiOptionsBodyModelValidator : DefaultBodyModelValidator
{

    public override bool ShouldValidateType(Type type)
    {
        if (WebApiOptionsAttribute.GetIsBodyModelValidatorDisabled(type))
        {
            return false;
        }
        else
        {
            return true;
        }
    }
}

覆盖默认的 IBodyModelValidator 服务

在 Global.asax 中 Asp.Net:

protected void Application_Start(object sender, EventArgs e)
{
    GlobalConfiguration.Configure(Register);
}

public static void Register(HttpConfiguration config)
{
    config.Services.Replace(typeof(IBodyModelValidator), new WebApiOptionsBodyModelValidator());
}

或在自托管应用程序的 Owin 启动中:

public void StartWebApi()
{
    this.WebService = WebApp.Start(new StartOptions(), Configuration);
}

private static void Configuration(IAppBuilder appBuilder)
{
    // Configure Web API for self-host.
    var config = new HttpConfiguration();
    config.Services.Replace(typeof(IBodyModelValidator), new NSBodyModelValidator());
    appBuilder.UseWebApi(config);
}

使用属性修饰classes

[WebApiOptions(IsBodyModelValidatorDisabled = true)]
public class LoginRequest
{
    public bool Test
    {
        get
        {
            //if you place a breakpoint here, it should never hit
            //if you set IsBodyModelValidatorDisabled = false, the breakpoint should hit
            return true;
        }
    }
}