无法在 Web api 配置中设置自定义合同解析器

Cannot set a custom contract resolver in web api configuration

如何在 Web api 配置中设置自定义合同解析器?我的代码比较新,到目前为止还没有自定义合约解析器。

除了路由之外,我没有添加任何其他定制。

我尝试了三种不同的方法并且 none 成功了:

public static void Register(HttpConfiguration config)
{            
    // Web API routes
    config.MapHttpAttributeRoutes();

    config.Routes.MapHttpRoute(
        name: "DefaultApi",
        routeTemplate: "api/{controller}/{action}/{id}",
        defaults: new { id = RouteParameter.Optional }
    );

    //attempt 1
    config.Formatters.JsonFormatter.SerializerSettings.ContractResolver = new CustomContractResolver();

    //attempt 2
    GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.ContractResolver = new CustomContractResolver();

    //attempt 3
    JsonConvert.DefaultSettings = () => new JsonSerializerSettings
    {
        ContractResolver = new CustomContractResolver()
    };           
}

自定义合约解析器代码,我调试的时候断点一直没到这里:

public class CustomContractResolver : CamelCasePropertyNamesContractResolver
{
    protected override string ResolvePropertyName(string propertyName)
    {
        var regex = new Regex(@"([_])(\w)");

        if (regex.IsMatch(propertyName))
        {
            var result = regex.Replace(propertyName.ToLower(), (match) => { return match.Groups[2].Value.ToUpper(); });
            return result;
        }
        else
            return base.ResolvePropertyName(propertyName);
    }
}

有没有什么东西不见了?

编辑 1:

我正在使用 ASP.NET WebApi 5.2.1 和 MVC 5.2.7,JSON.NET (Newtonsoft.Json) v13.0.1(并且已经尝试过旧的 v12)

我的 Global Asax 也很简单:

public class MvcApplication : System.Web.HttpApplication
{
    protected void Application_Start()
    {
        AreaRegistration.RegisterAllAreas();
        GlobalConfiguration.Configure(WebApiConfig.Register); //<- web api configuration
        FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
        RouteConfig.RegisterRoutes(RouteTable.Routes); //<- mvc configuration
        BundleConfig.RegisterBundles(BundleTable.Bundles);
    }
}

MVC RouteConfig class:

public class RouteConfig
{
    public static void RegisterRoutes(RouteCollection routes)
    {
        routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

        routes.IgnoreRoute("{resource}.ashx/{*pathInfo}");

        routes.MapRoute(
            name: "Default",
            url: "{controller}/{action}/{id}",
            defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
        );
    }
}

编辑 2

这是一些测试网络 api 控制器:

using System.Web.Http;

namespace Kronos.Web.Geolocalizacao.Controllers.Api
{
    public class TestController : ApiController
    {
        [HttpGet]
        public TestModel Obtain()
        {
            return new TestModel { CODE_IDENTIFICATION = 1, DEFAULT_DESCRIPTION = "TEST DAT THING" };
        }
    }

    public class TestModel
    {
        public decimal CODE_IDENTIFICATION { get; set; }

        public string DEFAULT_DESCRIPTION { get; set; }
    }
}

使用 Tabbed Postman chrome 插件进行测试

Postman tests

您的问题与您注册全局设置的方式无关 -- 根据 . Your problem is that Json.NET does not call ResolvePropertyName() when the contract resolver also has a NamingStrategy 设置 config.Formatters.JsonFormatter.SerializerSettings.ContractResolver 是正确的 -- 而您的基础 class CamelCasePropertyNamesContractResolver确实有命名策略

这可以通过检查当前 Json.NET reference source for DefaultContractResolver.SetPropertySettingsFromAttributes():

来验证
if (namingStrategy != null)
{
    property.PropertyName = namingStrategy.GetPropertyName(mappedName, hasSpecifiedName);
}
else
{
    property.PropertyName = ResolvePropertyName(mappedName);
}

损坏的演示 fiddle #1 here.

如果我简单地修改您的 CustomContractResolver 以继承自 DefaultContractResolver(默认情况下有一个空的 NamingStrategy),那么它会起作用:

public class CustomContractResolver : DefaultContractResolver
{
    readonly NamingStrategy baseNamingStrategy = new CamelCaseNamingStrategy();

    protected override string ResolvePropertyName(string propertyName)
    {
        var regex = new Regex(@"([_])(\w)");

        if (regex.IsMatch(propertyName))
        {
            var result = regex.Replace(propertyName.ToLower(), (match) => { return match.Groups[2].Value.ToUpper(); });
            return result;
        }
        else
            return baseNamingStrategy.GetPropertyName(propertyName, false);
    }
}

固定演示 fiddle #2 here.

但是,更简洁的解决方案是用自定义命名策略替换您的自定义合同解析器:

public class CustomNamingStrategy : CamelCaseNamingStrategy
{
    public CustomNamingStrategy() : base() { }
    public CustomNamingStrategy(bool processDictionaryKeys, bool overrideSpecifiedNames) : base(processDictionaryKeys, overrideSpecifiedNames) { }
    public CustomNamingStrategy(bool processDictionaryKeys, bool overrideSpecifiedNames, bool processExtensionDataNames) : base(processDictionaryKeys, overrideSpecifiedNames, processExtensionDataNames) { }

    readonly Regex regex = new Regex(@"([_])(\w)");
    protected  override string ResolvePropertyName(string name)
    {
        if (regex.IsMatch(name))
        {
            var result = regex.Replace(name.ToLower(), (match) => { return match.Groups[2].Value.ToUpper(); });
            return result;
        }
        return base.ResolvePropertyName(name);
    }
}

然后像这样在设置中配置它:

settings.ContractResolver = new DefaultContractResolver
{
    // Set the constructor parameters as per your preference.  These values are consistent with CamelCasePropertyNamesContractResolver
    NamingStrategy = new CustomNamingStrategy(processDictionaryKeys: true, overrideSpecifiedNames: true),
};

演示 fiddle #3 here.