Asp.net mvc 4 使用MVC4 ApicController时如何清理JSON输入参数?

Asp.net mvc 4 使用MVC4 ApicController时如何清理JSON输入参数?,asp.net-mvc-4,json.net,modelbinders,defaultmodelbinder,Asp.net Mvc 4,Json.net,Modelbinders,Defaultmodelbinder,我构建了一个基于AntiXSS的HTML清理器,通过覆盖默认的模型绑定器自动清理用户输入字符串,该绑定器在标准post请求上运行良好。然而,当使用新的ApiController时,默认的模型绑定器从未被调用,我认为这是因为这个新的MVC控制器使用JSON格式化程序来绑定来自请求主体的输入数据 那么,如何扩展格式化程序,以便在字符串属性被JSON绑定后修改它们呢?我不希望必须在控制器级别实现这一点,而且在它到达控制器之前应该有一种方法来实现这一点。我通过创建一个经过修改的Json格式化程序来解决我

我构建了一个基于AntiXSS的HTML清理器,通过覆盖默认的模型绑定器自动清理用户输入字符串,该绑定器在标准post请求上运行良好。然而,当使用新的ApiController时,默认的模型绑定器从未被调用,我认为这是因为这个新的MVC控制器使用JSON格式化程序来绑定来自请求主体的输入数据


那么,如何扩展格式化程序,以便在字符串属性被JSON绑定后修改它们呢?我不希望必须在控制器级别实现这一点,而且在它到达控制器之前应该有一种方法来实现这一点。

我通过创建一个经过修改的Json格式化程序来解决我的问题,但是关于如何实现这一点的大多数文档都基于.Net 4.0的预发布代码

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Linq;
using System.IO;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Net.Http.Formatting;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;
using System.Text;
using System.Reflection;
using System.Threading.Tasks;
using System.Web;
using System.Web.Script.Serialization;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;

public class JsonNetFormatterAntiXss : JsonMediaTypeFormatter
{
    public override bool CanReadType(Type type)
    {
        return base.CanReadType(type);
    }
    public override bool CanWriteType(Type type)
    {
        return base.CanWriteType(type);
    }

    public override Task<object> ReadFromStreamAsync(Type type, Stream readStream, HttpContent content, IFormatterLogger formatterLogger)
    {
        HttpContentHeaders contentHeaders = content == null ? null : content.Headers;
        // If content length is 0 then return default value for this type
        if (contentHeaders != null && contentHeaders.ContentLength == 0)
        {
            return Task.FromResult(MediaTypeFormatter.GetDefaultValueForType(type));
        }

        // Get the character encoding for the content
        Encoding effectiveEncoding = SelectCharacterEncoding(contentHeaders);

        try
        {
            using (JsonTextReader jsonTextReader = new JsonTextReader(new StreamReader(readStream, effectiveEncoding)) { CloseInput = false, MaxDepth = _maxDepth })
            {
                JsonSerializer jsonSerializer = JsonSerializer.Create(_jsonSerializerSettings);
                if (formatterLogger != null)
                {
                    // Error must always be marked as handled
                    // Failure to do so can cause the exception to be rethrown at every recursive level and overflow the stack for x64 CLR processes
                    jsonSerializer.Error += (sender, e) =>
                    {
                        Exception exception = e.ErrorContext.Error;
                            formatterLogger.LogError(e.ErrorContext.Path, exception);
                        e.ErrorContext.Handled = true;
                    };
                }

                return Task.FromResult(DeserializeJsonString(jsonTextReader, jsonSerializer, type));
            }

        }
        catch (Exception e)
        {
            if (formatterLogger == null)
            {
                throw;
            }
            formatterLogger.LogError(String.Empty, e);
            return Task.FromResult(MediaTypeFormatter.GetDefaultValueForType(type));
        }
    }

    private object DeserializeJsonString(JsonTextReader jsonTextReader, JsonSerializer jsonSerializer, Type type)
    {
        object data = jsonSerializer.Deserialize(jsonTextReader, type);

        // sanitize strings if we are told to do so
        if(_antiXssOptions != AntiXssOption.None)
            data = CleanAntiXssStrings(data); // call your custom XSS cleaner

        return data;
    }

    /// <summary>
    /// Clean all strings using internal AntiXss sanitize operation
    /// </summary>
    /// <param name="data"></param>
    /// <returns></returns>
    private object CleanAntiXssStrings(object data)
    {
        PropertyInfo[] properties = data.GetType().GetProperties();
        foreach (PropertyInfo property in properties)
        {
            Type ptype = property.PropertyType;
            if (ptype == typeof(string) && ptype != null)
            {
                // sanitize the value using the preferences set
                property.SetValue(data, DO_MY_SANITIZE(property.GetValue(data).ToString()));
                }
            }
            return data;
        }

    public override Task WriteToStreamAsync(Type type, object value, Stream writeStream, HttpContent content, System.Net.TransportContext transportContext)
    {
        return base.WriteToStreamAsync(type, value, writeStream, content, transportContext);
    }

    private DataContractJsonSerializer GetDataContractSerializer(Type type)
    {
        Contract.Assert(type != null, "Type cannot be null");
        DataContractJsonSerializer serializer = _dataContractSerializerCache.GetOrAdd(type, (t) => CreateDataContractSerializer(type, throwOnError: true));

        if (serializer == null)
        {
            // A null serializer means the type cannot be serialized
            throw new InvalidOperationException(String.Format("Cannot serialize '{0}'", type.Name));
        }

        return serializer;
    }

    private static DataContractJsonSerializer CreateDataContractSerializer(Type type, bool throwOnError)
    {
        if (type == null)
        {
            throw new ArgumentNullException("type");
        }

        DataContractJsonSerializer serializer = null;
        Exception exception = null;

        try
        {
            // Verify that type is a valid data contract by forcing the serializer to try to create a data contract
            XsdDataContractExporter xsdDataContractExporter = new XsdDataContractExporter();
            xsdDataContractExporter.GetRootElementName(type);
            serializer = new DataContractJsonSerializer(type);
        }
        catch (InvalidDataContractException invalidDataContractException)
        {
            exception = invalidDataContractException;
        }

        if (exception != null)
        {
            if (throwOnError)
            {
                throw new InvalidOperationException(String.Format("Can not serialize type '{0}'.", type.Name), exception);
            }
        }

        return serializer;
    }

}
使用系统;
使用System.Collections.Concurrent;
使用System.Collections.Generic;
使用System.Diagnostics.Contracts;
使用System.Linq;
使用System.IO;
使用System.Net.Http;
使用System.Net.Http.Header;
使用System.Net.Http.Formatting;
使用System.Runtime.Serialization;
使用System.Runtime.Serialization.Json;
使用系统文本;
运用系统反思;
使用System.Threading.Tasks;
使用System.Web;
使用System.Web.Script.Serialization;
使用Newtonsoft.Json;
使用Newtonsoft.Json.Serialization;
公共类JsonNetFormatterAntiXss:JsonMediaTypeFormatter
{
公共覆盖布尔CanReadType(类型)
{
返回base.CanReadType(类型);
}
公共重写bool CanWriteType(类型)
{
返回base.CanWriteType(类型);
}
公共重写任务ReadFromStreamAsync(类型类型、流readStream、HttpContent内容、IFormatterLogger formatterLogger)
{
HttpContentHeaders contentHeaders=content==null?null:content.Headers;
//如果内容长度为0,则返回此类型的默认值
if(contentHeaders!=null&&contentHeaders.ContentLength==0)
{
返回Task.FromResult(MediaTypeFormatter.GetDefaultValueForType(type));
}
//获取内容的字符编码
编码有效编码=SelectCharacterEncoding(contentHeaders);
尝试
{
使用(JsonTextReader JsonTextReader=newjsontextreader(newstreamreader(readStream,effectiveEncoding)){CloseInput=false,MaxDepth=\u MaxDepth})
{
JsonSerializer JsonSerializer=JsonSerializer.Create(_jsonSerializerSettings);
if(formatterLogger!=null)
{
//错误必须始终标记为已处理
//如果不这样做,可能会导致在每个递归级别上重新调用异常,并使x64 CLR进程的堆栈溢出
jsonSerializer.Error+=(发送方,e)=>
{
异常异常=e.ErrorContext.Error;
LogError(例如ErrorContext.Path,异常);
e、 ErrorContext.Handled=true;
};
}
返回Task.FromResult(反序列化JSONString(jsonTextReader,jsonSerializer,type));
}
}
捕获(例外e)
{
如果(formatterLogger==null)
{
投掷;
}
formatterLogger.LogError(String.Empty,e);
返回Task.FromResult(MediaTypeFormatter.GetDefaultValueForType(type));
}
}
私有对象反序列化JSONString(JsonTextReader JsonTextReader,JsonSerializer JsonSerializer,类型类型)
{
对象数据=jsonSerializer.Deserialize(jsonTextReader,类型);
//如果我们被告知要清理字符串,请对其进行清理
if(_antiXssOptions!=AntiXssOption.None)
data=CleanAntiXssStrings(data);//调用自定义XSS清理器
返回数据;
}
/// 
///使用内部AntiXss清理操作清理所有字符串
/// 
/// 
/// 
私有对象CleanAntiXSSSString(对象数据)
{
PropertyInfo[]properties=data.GetType().GetProperties();
foreach(属性中的PropertyInfo属性)
{
类型ptype=property.PropertyType;
if(ptype==typeof(string)&&ptype!=null)
{
//使用首选项集清理该值
property.SetValue(data,DO_MY__SANITIZE(property.GetValue(data.ToString()));
}
}
返回数据;
}
公共重写任务WriteToStreamAsync(类型类型、对象值、流writeStream、HttpContent内容、System.Net.TransportContext TransportContext)
{
返回base.WriteToStreamAsync(类型、值、writeStream、内容、transportContext);
}
专用DataContractJsonSerializer GetDataContractSerializer(类型)
{
Assert(type!=null,“type不能为null”);
DataContractJsonSerializer序列化程序=_dataContractSerializerCache.GetOrAdd(类型,(t)=>CreateDataContractSerializer(类型,throwOnError:true));
if(序列化程序==null)
{
//空序列化程序表示无法序列化该类型
抛出新的InvalidOperationException(String.Format(“无法序列化”{0}',type.Name));
}
返回序列化程序;
}
专用静态DataContractJsonSerializer CreateDataContractSerializer(类型,bool throwOnError)
{
if(type==null)
{
抛出新的ArgumentNullException(“类型”);
}
DataContractJsonSerializer序列化程序=null;
异常=空;
尝试
{
//通过强制序列化程序尝试创建数据协定,验证类型是否为有效的数据协定
XsdDataContractExporter XsdDataContractExporter=新XsdDataContractExporter();
xsdDat