0

This is a follow up to the questions Custom form data with multiple files to Web API controller and Filling Parameter with Attribute [FromForm] with Fiddler, which are both answered by citing A MediaTypeFormatter for WebApi for multipart/form-data including file uploads.

Essentially this is the same question Tocana has asked before in the last link.

I copied the code from the latter, registered the media type formatter and tried it with fiddler, but as it enters UploadController.Post(UploadRequestViewModel model) model is instanciated but its attributes are all equal to null.

I debugged it and found that FormMultipartEncodedMediaTypeFormatter.BindToModel(IDictionary<string, object> data, Type type, IFormatterLogger formatterLogger) is called with a correct data dictionary, but from then on I cannot make head from tails. The binder used to bind the model is CompositeModelBinder, but wether that is correct or not I cannot tell.

The fiddler request looks like this:

Header:

Content-Type: multipart/form-data; boundary=-------------------------acebdf13572468
User-Agent: Fiddler
Host: localhost:49473
Content-Length: 13192

Body

---------------------------acebdf13572468
Content-Disposition: form-data; name="SiteId"
Content-Type: text/plain

987654321
---------------------------acebdf13572468
Content-Disposition: form-data; name="StartDate"
Content-Type: text/plain

24.08.1979
---------------------------acebdf13572468
Content-Disposition: form-data; name="Zulu"; filename="ExifTest.tif"
Content-Type: image/tiff

<@INCLUDE *C:\...\ExifTest.tif*@>
---------------------------acebdf13572468--

What do I have to do to get my model correctly initialized, i.e. its values are not null?

EDIT:
All models and controllers are those of A MediaTypeFormatter for WebApi for multipart/form-data including file uploads, i.e.

Model:

public class UploadRequestViewModel
{
    [Required]
    public string Title { get; set; }

    [Required]
    public string Description { get; set; }

    [Required]
    public HttpPostedFileBase File { get; set; }
}

Controller:

[Route("UploadTest")]
[HttpPost]
public IHttpActionResult Post(UploadRequestViewModel model)
{
}

FormMultipartEncodedMediaTypeFormatter.BindToModel

private object BindToModel(IDictionary<string, object> data, Type type, IFormatterLogger formatterLogger)
{
    if (data == null) throw new ArgumentNullException(nameof(data));
    if (type == null) throw new ArgumentNullException(nameof(type));

    using (var config = new HttpConfiguration())
    {
        // if there is a requiredMemberSelector set, use this one by replacing the validator provider
        var validateRequiredMembers = RequiredMemberSelector != null && formatterLogger != null;
        if (validateRequiredMembers)
        {
            config.Services.Replace(typeof(ModelValidatorProvider), new RequiredMemberModelValidatorProvider(RequiredMemberSelector));
        }

        // create a action context for model binding
        var actionContext = new HttpActionContext
        {
            ControllerContext = new HttpControllerContext
            {
                Configuration = config,
                ControllerDescriptor = new HttpControllerDescriptor
                {
                    Configuration = config
                }
            }
        };

        // create model binder context 
        var valueProvider = new NameValuePairsValueProvider(data, CultureInfo.InvariantCulture);
        var metadataProvider = actionContext.ControllerContext.Configuration.Services.GetModelMetadataProvider();
        var metadata = metadataProvider.GetMetadataForType(null, type);
        var modelBindingContext = new ModelBindingContext
        {
            ModelName = string.Empty,
            FallbackToEmptyPrefix = false,
            ModelMetadata = metadata,
            ModelState = actionContext.ModelState,
            ValueProvider = valueProvider
        };

        // bind model 
        var modelBinderProvider = new CompositeModelBinderProvider(config.Services.GetModelBinderProviders());
        var binder = modelBinderProvider.GetBinder(config, type);
        var haveResult = binder.BindModel(actionContext, modelBindingContext);

        // log validation errors 
        if (formatterLogger != null)
        {
            foreach (var modelStatePair in actionContext.ModelState)
            {
                foreach (var modelError in modelStatePair.Value.Errors)
                {
                    if (modelError.Exception != null)
                    {
                        formatterLogger.LogError(modelStatePair.Key, modelError.Exception);
                    }
                    else
                    {
                        formatterLogger.LogError(modelStatePair.Key, modelError.ErrorMessage);
                    }
                }
            }
        }

        return haveResult ? modelBindingContext.Model : GetDefaultValueForType(type);
    }
}
Sebastian
  • 754
  • 4
  • 7
  • 20

0 Answers0