无法在 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.
如何在 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
您的问题与您注册全局设置的方式无关 -- 根据 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.