【问题标题】:Cannot set a custom contract resolver in web api configuration无法在 Web api 配置中设置自定义合同解析器
【发布时间】: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 json asp.net-web-api asp.net-mvc-5 json.net


【解决方案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.

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-02-11
    • 1970-01-01
    • 1970-01-01
    • 2013-01-25
    • 1970-01-01
    • 2018-06-21
    相关资源
    最近更新 更多