ExceptionMessage:“无法将多个参数('tenant' 和 'certificateFile')绑定到请求的内容

ExceptionMessage: "Can't bind multiple parameters ('tenant' and 'certificateFile') to the request's content

我正在尝试从 React 调用 webapi,但出现此错误

responseJson = {Message: "An error has occurred.", ExceptionMessage: "Can't bind multiple parameters ('tenant' and 'certificateFile') to the request's content.", ExceptionType: "System.InvalidOperationException", StackTrace: " at System.Web.Http.Controllers.HttpActionBindin…tpControllerDispatcher.d__1.MoveNext()"}

我的反应代码是这样的:

    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();

        const formData = new FormData();
        formData.append("TenantId", this.state.tenantid);
        formData.append("TenanrUrl", this.state.tenanturl);
        formData.append("TenantPassword", this.state.tenantpassword);

        const options = {
          method: 'put',
          data: formData,
          config: {
            headers: {
              'Content-Type': 'multipart/form-data'
            }
          }
        };

        adalApiFetch(fetch, "/Tenant", options)
          .then(response => response.json())
          .then(responseJson => {
            if (!this.isCancelled) {
              this.setState({ data: responseJson });
            }
          })
          .catch(error => {
            console.error(error);
          });
      }


      upload(e){
          let data = new FormData();
          //Append files to form data
          let files = e.target.files;
          for (let i = 0; i < files.length; i++) {
            data.append('files', files[i], files[i].name);
          }      
      }

      render(){
        const { data } = this.state;
        const { rowStyle, colStyle, gutter } = basicStyle;

        return (
          <div>
            <LayoutWrapper>
            <PageHeader>{<IntlMessages id="pageTitles.TenantAdministration" />}</PageHeader>
            <Row style={rowStyle} gutter={gutter} justify="start">
              <Col md={12} sm={12} xs={24} style={colStyle}>
                <Box
                  title={<IntlMessages id="pageTitles.TenantAdministration" />}
                  subtitle={<IntlMessages id="pageTitles.TenantAdministration" />}
                >
                  <ContentHolder>
                  <form onSubmit={this.handleSubmit}>
                    <label>
                      TenantId:
                      <input type="text" value={this.state.tenantid} onChange={this.handleChangeTenantId} />
                    </label>
                    <label>
                      TenantUrl:
                      <input type="text" value={this.state.tenanturl} onChange={this.handleChangeTenantUrl} />
                    </label>
                    <label>
                      TenantPassword:
                      <input type="text" value={this.state.tenantpassword} onChange={this.handleChangeTenantPassword} />
                    </label>
                    <label>
                      Certificate:
                      <input onChange = { e => this.upload(e) } type = "file" id = "files" ref = { file => this.fileUpload } />
                    </label>             
                  <input type="submit" value="Submit" />
                  </form>
                  </ContentHolder>
                </Box>
              </Col>
            </Row>
          </LayoutWrapper>
          </div>
        );
      }
    }

My web api is this:


public class TenantController : ApiController
    {
        public async Task<List<Tenant>> GetTenants()
        {
            var tenantStore = CosmosStoreFactory.CreateForEntity<Tenant>();
            return await tenantStore.Query().Where(x => x.TenantId != null ).ToListAsync();

        }

        public async Task<IHttpActionResult> GetTenant(string tenantId)
        {
            var tenantStore = CosmosStoreFactory.CreateForEntity<Tenant>();
            var tenant = await tenantStore.Query().FirstOrDefaultAsync(x => x.TenantId == tenantId);
            if (tenant == null)
            {
                return NotFound();
            }
            return Ok(tenant);
        }

        public async Task<IHttpActionResult>  PutTenant([FromBody]Tenant tenant, HttpPostedFile certificateFile)
        {
            CloudStorageAccount storageAccount = CloudStorageAccount.Parse(ConfigurationManager.AppSettings["AzureStorageKey"].ToString());
            // Create the blob client.
            CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient();

            // Retrieve reference to a previously created container.
            CloudBlobContainer container = blobClient.GetContainerReference(ConfigurationManager.AppSettings["certificatesContainer"].ToString());

            // Retrieve reference to a blob named "myblob".
            CloudBlockBlob blockBlob = container.GetBlockBlobReference("myblob");

            // Create or overwrite the "myblob" blob with contents from a local file.
            blockBlob.Properties.ContentType = certificateFile.ContentType;
            blockBlob.UploadFromStream(certificateFile.InputStream);

            var tenantStore = CosmosStoreFactory.CreateForEntity<Tenant>();
            tenant.CertificatePath = blockBlob.Uri;

            if (!ModelState.IsValid)
            {
                return BadRequest(ModelState);
            }
            //if (id != tenant.TenantId)
            //{
            //    return BadRequest();
            //}

            var added = await tenantStore.AddAsync(tenant);
            return StatusCode(HttpStatusCode.NoContent); 
        }

        public async Task<IHttpActionResult> PostTenant(string id, Tenant tenant)
        {
            var tenantStore = CosmosStoreFactory.CreateForEntity<Tenant>();

            if (!ModelState.IsValid)
            {
                return BadRequest(ModelState);
            }

            var result = await tenantStore.UpdateAsync(tenant);
            return Ok(result);
        }

        public async Task<IHttpActionResult> DeleteTenant(string tenantId)
        {
            var tenantStore = CosmosStoreFactory.CreateForEntity<Tenant>();
            await tenantStore.RemoveByIdAsync(tenantId);// Removes an entity with the specified ID
            return Ok(tenantId);
        }
    }

我的租客对象是这样的:

public class Tenant
    {
        public string TenantId { get; set; }
        public string TenantUrl { get; set; }
        public Uri CertificatePath { get; set; }
        public string CertificatePassword { get; set; }

        public override string ToString()
        {
            return JsonConvert.SerializeObject(this);
        }
    }

和控制器

public class TenantController : ApiController
    {
        public async Task<List<Tenant>> GetTenants()
        {
            var tenantStore = CosmosStoreFactory.CreateForEntity<Tenant>();
            return await tenantStore.Query().Where(x => x.TenantId != null ).ToListAsync();

        }

        public async Task<IHttpActionResult> GetTenant(string tenantId)
        {
            var tenantStore = CosmosStoreFactory.CreateForEntity<Tenant>();
            var tenant = await tenantStore.Query().FirstOrDefaultAsync(x => x.TenantId == tenantId);
            if (tenant == null)
            {
                return NotFound();
            }
            return Ok(tenant);
        }

        public async Task<IHttpActionResult>  PutTenant([FromBody]Tenant tenant, HttpPostedFile certificateFile)
        {
            CloudStorageAccount storageAccount = CloudStorageAccount.Parse(ConfigurationManager.AppSettings["AzureStorageKey"].ToString());
            // Create the blob client.
            CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient();

            // Retrieve reference to a previously created container.
            CloudBlobContainer container = blobClient.GetContainerReference(ConfigurationManager.AppSettings["certificatesContainer"].ToString());

            // Retrieve reference to a blob named "myblob".
            CloudBlockBlob blockBlob = container.GetBlockBlobReference("myblob");

            // Create or overwrite the "myblob" blob with contents from a local file.
            blockBlob.Properties.ContentType = certificateFile.ContentType;
            blockBlob.UploadFromStream(certificateFile.InputStream);

            var tenantStore = CosmosStoreFactory.CreateForEntity<Tenant>();
            tenant.CertificatePath = blockBlob.Uri;

            if (!ModelState.IsValid)
            {
                return BadRequest(ModelState);
            }
            //if (id != tenant.TenantId)
            //{
            //    return BadRequest();
            //}

            var added = await tenantStore.AddAsync(tenant);
            return StatusCode(HttpStatusCode.NoContent); 
        }

        public async Task<IHttpActionResult> PostTenant(string id, Tenant tenant)
        {
            var tenantStore = CosmosStoreFactory.CreateForEntity<Tenant>();

            if (!ModelState.IsValid)
            {
                return BadRequest(ModelState);
            }

            var result = await tenantStore.UpdateAsync(tenant);
            return Ok(result);
        }

        public async Task<IHttpActionResult> DeleteTenant(string tenantId)
        {
            var tenantStore = CosmosStoreFactory.CreateForEntity<Tenant>();
            await tenantStore.RemoveByIdAsync(tenantId);// Removes an entity with the specified ID
            return Ok(tenantId);
        }
    }

问题正是异常消息中指定的问题。您正在将多个参数绑定到操作主体,您不能使用 Web API 做到这一点。 请阅读 Parameter binding in Web API.

使用以下代码读取上传的文件。

    using System.Net.Http;
    using System.IO;
    public async Task<IHttpActionResult>  PutTenant([FromBody]Tenant tenant)
    {
            var provider = new MultipartMemoryStreamProvider();
            var contentType = "";
            Stream content = new MemoryStream();
            await base.Request.Content.ReadAsMultipartAsync(provider);
            if(provider.Contents.Count > 0)
            {
                contentType = provider.Contents[0].Headers.ContentType.MediaType;
                content = await provider.Contents[0].ReadAsStreamAsync();
            }

        CloudStorageAccount storageAccount = CloudStorageAccount.Parse(ConfigurationManager.AppSettings["AzureStorageKey"].ToString());
        // Create the blob client.
        CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient();

        // Retrieve reference to a previously created container.
        CloudBlobContainer container = blobClient.GetContainerReference(ConfigurationManager.AppSettings["certificatesContainer"].ToString());

        // Retrieve reference to a blob named "myblob".
        CloudBlockBlob blockBlob = container.GetBlockBlobReference("myblob");

        // Create or overwrite the "myblob" blob with contents from a local file.
        blockBlob.Properties.ContentType = contentType;
        blockBlob.UploadFromStream(content);

        var tenantStore = CosmosStoreFactory.CreateForEntity<Tenant>();
        tenant.CertificatePath = blockBlob.Uri;

        if (!ModelState.IsValid)
        {
            return BadRequest(ModelState);
        }
        //if (id != tenant.TenantId)
        //{
        //    return BadRequest();
        //}

        var added = await tenantStore.AddAsync(tenant);
        return StatusCode(HttpStatusCode.NoContent); 
    }