title: webapi, how to read the file from a POST/PUT action with custom model binder
body: <p>I have the following react component, which apparently its working fine:</p>
  
  <pre><code>import React, { Component } from 'react';
  import { Row, Col } from 'antd';
  import PageHeader from '../../components/utility/pageHeader';
  import Box from '../../components/utility/box';
  import LayoutWrapper from '../../components/utility/layoutWrapper.js';
  import ContentHolder from '../../components/utility/contentHolder';
  import basicStyle from '../../settings/basicStyle';
  import IntlMessages from '../../components/utility/intlMessages';
  import { adalApiFetch } from '../../adalConfig';
  
  export default class extends Component {
    constructor(props) {
      super(props);
      this.state = {TenantId: '', TenantUrl: '', TenantPassword: '' };
      this.handleChangeTenantUrl = this.handleChangeTenantUrl.bind(this);
      this.handleChangeTenantPassword = this.handleChangeTenantPassword.bind(this);
      this.handleChangeTenantId= this.handleChangeTenantId.bind(this);
      this.handleSubmit = this.handleSubmit.bind(this);
    };
  
  
    handleChangeTenantUrl(event){
      this.setState({TenantUrl: event.target.value});
    }
  
    handleChangeTenantPassword(event){
      this.setState({TenantPassword: event.target.value});
    }
  
    handleChangeTenantId(event){
      this.setState({TenantId: event.target.value});
    }
  
    handleSubmit(event){
      event.preventDefault();
  
      let data = new FormData();
      //Append files to form data
      data.append("model", {"TenantId": this.state.TenantId, "TenantUrl": this.state.TenantUrl, "TenantPassword": this.state.TenantPassword });
  
      let files = this.state.selectedFiles;
      for (let i = 0; i &lt; files.length; i++) {
        data.append("file", files[i], files[i].name);
      }
  
      const options = {
        method: 'put',
        body: data,
        config: {
          headers: {
            'Content-Type': 'multipart/form-data'
          }
        }
      };
  
      adalApiFetch(fetch, "/Tenant", options)
        .then(response =&gt; response.json())
        .then(responseJson =&gt; {
          if (!this.isCancelled) {
            this.setState({ data: responseJson });
          }
        })
        .catch(error =&gt; {
          console.error(error);
        });
    }
  
  
    upload(e){
      let files = e.target.files;
      this.setState({ 'selectedFiles': files });
  }
  
    render(){
      const { data } = this.state;
      const { rowStyle, colStyle, gutter } = basicStyle;
  
      return (
        &lt;div&gt;
          &lt;LayoutWrapper&gt;
          &lt;PageHeader&gt;{&lt;IntlMessages id="pageTitles.TenantAdministration" /&gt;}&lt;/PageHeader&gt;
          &lt;Row style={rowStyle} gutter={gutter} justify="start"&gt;
            &lt;Col md={12} sm={12} xs={24} style={colStyle}&gt;
              &lt;Box
                title={&lt;IntlMessages id="pageTitles.TenantAdministration" /&gt;}
                subtitle={&lt;IntlMessages id="pageTitles.TenantAdministration" /&gt;}
              &gt;
                &lt;ContentHolder&gt;
                &lt;form onSubmit={this.handleSubmit}&gt;
                  &lt;label&gt;
                    TenantId:
                    &lt;input type="text" value={this.state.TenantId} onChange={this.handleChangeTenantId} /&gt;
                  &lt;/label&gt;
                  &lt;label&gt;
                    TenantUrl:
                    &lt;input type="text" value={this.state.TenantUrl} onChange={this.handleChangeTenantUrl} /&gt;
                  &lt;/label&gt;
                  &lt;label&gt;
                    TenantPassword:
                    &lt;input type="text" value={this.state.TenantPassword} onChange={this.handleChangeTenantPassword} /&gt;
                  &lt;/label&gt;
                  &lt;label&gt;
                    Certificate:
                    &lt;input onChange = { e =&gt; this.upload(e) } type = "file" id = "files" ref = { file =&gt; this.fileUpload } /&gt;
                  &lt;/label&gt;            
                &lt;input type="submit" value="Submit" /&gt;
                &lt;/form&gt;
                &lt;/ContentHolder&gt;
              &lt;/Box&gt;
            &lt;/Col&gt;
          &lt;/Row&gt;
        &lt;/LayoutWrapper&gt;
        &lt;/div&gt;
      );
    }
  }
  </code></pre>
  
  <p>However, I have a custom model binder like this:</p>
  
  <pre><code>public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext)
          {
              if (bindingContext.ModelType != typeof(Tenant))
              {
                  return false;
              }
  
              var task = Task.Run(async () =&gt;
              {
                  var model = new Tenant();
  
                  if (!actionContext.Request.Content.IsMimeMultipartContent())
                  {
                      bindingContext.ModelState.AddModelError(bindingContext.ModelName, "WebRequeest content 'multipart/form-data' is valid");
                  }
                  else
                  {
                      var provider = await actionContext.Request.Content.ReadAsMultipartAsync();
  
                      var fileContent = provider.Contents.FirstOrDefault(n =&gt; n.Headers.ContentDisposition.Name.Equals("file"));
                      if (fileContent == null)
                      {
                          bindingContext.ModelState.AddModelError(bindingContext.ModelName, "Section 'file' is missed");
                      }
  
                      var modelContent = provider.Contents.FirstOrDefault(n =&gt; n.Headers.ContentDisposition.Name.Equals("model"));
                      if (modelContent == null)
                      {
                          bindingContext.ModelState.AddModelError(bindingContext.ModelName, "Section 'model' is missed");
                      }
  
                      if (fileContent != null &amp;&amp; modelContent != null)
                      {
                          model = JsonConvert.DeserializeObject&lt;Tenant&gt;(await modelContent.ReadAsStringAsync());
                          //model.Text = "&lt;NativeTranslation&gt;";
                          model.ContentType = provider.Contents[0].Headers.ContentType.MediaType;
                          model.CertificateFile = await fileContent.ReadAsByteArrayAsync();
                          //model.TenantId = fileContent.Headers.ContentDisposition.FileName;
                      }
                  }
  
                  return model;
              });
  
              task.Wait();
  
              bindingContext.Model = task.Result;
              return true;
          }
      }
  </code></pre>
  
  <p>but filecontent is null and model is null too</p>
  
  <p><a href="https://i.stack.imgur.com/BqM8C.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/BqM8C.png" alt="enter image description here"></a></p>
  
  <p>Update 1:</p>
  
  <p><a href="https://i.stack.imgur.com/OMAnT.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/OMAnT.png" alt="enter image description here"></a></p>