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

Posted

技术标签:

【中文标题】无法在 Web api 配置中设置自定义合同解析器【英文标题】:Cannot set a custom contract resolver in web api configuration 【发布时间】:2021-06-27 17:44:32 【问题描述】:

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

除了路由,我没有添加其他自定义。

我尝试了三种不同的方法,但都没有奏效:

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 类:

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

这里是一些测试 web 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

【问题讨论】:

您的问题是asp.net-web-api 还是asp.net-mvc-5?他们使用不同的序列化程序,并且使用的序列化程序随版本而变化,所以请edit您的问题具体说明您使用的是哪个版本和设置? 如果是 MVC,请注意 asp.net-mvc-5 已经很老了,第一个版本是在 2013 年发布的。它默认不使用 Json.NET。如果您想在 MVC5 中使用 Json.NET 进行序列化,请参阅Setting the default JSON serializer in ASP.NET MVC。如果你想将它用于模型绑定,请查看How to use Json.NET for JSON modelbinding in an MVC5 project?。 我同时使用 asp net mvc 5 和 asp net web api,.net framework 4.8 【参考方案1】:

您的问题与您注册全局设置的方式无关——根据this question,设置config.Formatters.JsonFormatter.SerializerSettings.ContractResolver 是正确的。您的问题是 Json.NET 在合同解析器也有 NamingStrategy 时不会调用 ResolvePropertyName() - 而您的基类 CamelCasePropertyNamesContractResolver 确实有命名策略。

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

if (namingStrategy != null)

    property.PropertyName = namingStrategy.GetPropertyName(mappedName, hasSpecifiedName);

else

    property.PropertyName = ResolvePropertyName(mappedName);

损坏的演示小提琴 #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);
    

修复了演示小提琴 #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),
;

演示小提琴#3 here.

【讨论】:

以上是关于无法在 Web api 配置中设置自定义合同解析器的主要内容,如果未能解决你的问题,请参考以下文章

如何从 java api 在 keycloak 中设置自定义用户属性

无法在 WorkManager 中设置自定义工人工厂

在 Google Maps api v2 中设置自定义 InfoWindow 的宽度

在 AFNetworking 2.x 中设置自定义标头后,RESTful API 未接收到参数

在 ObjectMapper 的 DateFormatterTransform 中设置自定义时区

无法在 MapView,Swift 3 中设置自定义图钉图像