AllowHTML and custom IValueProviders in MVC3

2019-08-03 17:01发布

问题:

I have a custom IValueProvider that I wrote to handle json values. It is registered in the globa.asax via

ValueProviderFactories.Factories.Insert(0, new JsonValueProviderFactory());

It works fine, but I just recently needed to post a model back that contains HTML. By default this spawns the old

A potentially dangerous Request.Form value was detected from the client

Error message. It looks like the way to get around that normally is to decorate the model property with an AllowHtml attribute. The problem is my value provider is still throwing the error. Any idea how to get my value provider to respect the AllowHtml attribute?

Here is the relevant code:

public class JsonValueProvider : IValueProvider, IValueDeserializer
{
    private ControllerContext context;

    public JsonValueProvider(ControllerContext controllerContext)
    {
        this.context = controllerContext;
    }

    public bool ContainsPrefix(string prefix)
    {
        return context.HttpContext.Request.Form.AllKeys.FirstOrDefault(i => i.StartsWith(prefix)) != null; //<!------- The error is thrown here
    }
    .....

回答1:

You cannot use validated request data (e.g. Request.Form, Request.QueryString) within your custom value provider if you need to handle "potentially dangerous" data.

Instead you should use methods or properties such as Request.Unvalidated, HttpContext.Request.Unvalidated() or HttpContext.Request.InputStream.

If you implement IUnvalidatedValueProvider on your custom ValueProvider, then it can also work nicely with DefaultModelBinder / AllowHtml.

IUnvalidatedValueProvider has an overloaded GetValue method to tell the provider whether to skip validation (which is what ultimately gets set by AllowHtml).

    public interface IUnvalidatedValueProvider : IValueProvider
    {
        ValueProviderResult GetValue(string key, bool skipValidation);
    }

In your implementation, if skipValidation is true, then you should retrieve unvalidated request data. Obviously within your ContainsPrefix you cannot access validated data (e.g. Request.Form).

Having said that it may be easiest to inherit from NameValueCollectionValueProvider which is already "unvalidated" aware. Most of the built in value providers inherhit from it. I have linked to the MVC source code...take a look at how the sub-types are implemented.



回答2:

Allow html works only for model binder IIRC. You can obtain unvalidated form and query strings using ValidationUtility from Microsoft.Web.Infrastructure.DynamicValidationHelper but keep in mind that all values won't be validated, not only those with AllowHtml!

Func<NameValueCollection> formGetter;
Func<NameValueCollection> queryStringGetter;

ValidationUtility.GetUnvalidatedCollections(HttpContext.Current, out formGetter, out queryStringGetter);

var form = formGetter();
var queryString = queryStringGetter();