I'm trying to configure ForwardedHeadersOptions
which is part of Microsoft.AspNetCore.HttpOverrides
. I've added options to my appsettings.json
"ForwardedHeadersOptions": {
"ForwardedHeaders": 5,
"ForwardLimit": 1,
"KnownProxies": [
"111.111.111.111"
]
},
First two properties (ForwardedHeaders
and ForwardLimit
) are mapped properly, while KnownProxies
is not. It is expected, because the type of KnownProxies
is IList<IPAddress>
.
So, in order to map this property, I've created type converter:
public class IPAddressTypeConverter : TypeConverter
{
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{
return sourceType == typeof(string) || base.CanConvertFrom(context, sourceType);
}
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
return base.ConvertFrom(context, culture, value);
}
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
{
return destinationType == typeof(IPAddress) || base.CanConvertTo(context, destinationType);
}
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
{
if (value != null && value is string)
{
return IPAddress.Parse(value.ToString());
}
return base.ConvertTo(context, culture, value, destinationType);
}
}
Then, I've registered the IPAddressTypeConverter
in the Startup.cs
like this:
public void ConfigureServices(IServiceCollection services)
{
TypeDescriptor.AddAttributes(typeof(ForwardedHeadersOptions), new TypeConverterAttribute(typeof(IPAddressTypeConverter)));
services.Configure<ForwardedHeadersOptions>(Configuration.GetSection(nameof(ForwardedHeadersOptions)));
}
But when I launch the app type converter methods are never called. Any idea what is wrong here?
When the configuration framework tries to convert a string in your appsettings file, it converts FROM string. Your TypeConverter is registered to handle a certain type (IPAddress) which it will convert to. But it doesn't need to test for this type.
The [Can]ConvertTo-methods are for serializing, i.e. converting from the registered type to another type like string.
So, your type converter should look something like:
public class IPAddressTypeConverter : TypeConverter
{
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{
if (sourceType == typeof(string) || sourceType == typeof(String))
{
return true;
}
return base.CanConvertFrom(context, sourceType);
}
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
if (value != null && (value is string || value is String))
{
return IPAddress.Parse((string)value);
}
return base.ConvertFrom(context, culture, value);
}
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
{
if (destinationType == typeof(string))
{
return true;
}
return base.CanConvertTo(context, destinationType);
}
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
{
if (destinationType == typeof(string) && value is IPAddress)
{
return ((IPAddress)value).ToString();
}
return base.ConvertTo(context, culture, value, destinationType);
}
Then, in Startup.cs, ConfigureServices(), with several lines for clarity and debugging breakpoint opportunities:
TypeDescriptor.AddAttributes(typeof(IPAddress), new TypeConverterAttribute(typeof(IPAddressTypeConverter)));
var forwardedHeadersOptions = new ForwardedHeadersOptions();
var forwardedHeadersSection = Configuration.GetSection("ForwardedHeadersOptions");
forwardedHeadersSection.Bind(forwardedHeadersOptions);
services.Configure<ForwardedHeadersOptions>(o =>
{
o.ForwardedHeaders = ForwardedHeaders.All;
o.ForwardLimit = forwardedHeadersOptions.ForwardLimit;
foreach(var knownProxy in forwardedHeadersOptions.KnownProxies)
{
o.KnownProxies.Add(knownProxy);
}
foreach(var knownNetwork in forwardedHeadersOptions.KnownNetworks)
{
o.KnownNetworks.Add(knownNetwork);
}
});
I tried making a similar type converter for KnownNetworks but it didn's stick, perhaps because of the typing (string / int). So I ended up creating a separate IPNetworkSettingClass where the Prefix-field was a string rather than an object of the IPAdress class. Then I could get the networks from appsettings using:
var knownNetworks = Configuration.GetSection("ForwardedHeadersOptions").GetSection("KnownNetworks").Get<List<IPNetworkSetting>>();
Finally I set the KnownNetworks programmatically in the bound ForwardedHeadersOptions-instance.
Hope this helps