我有以下react组件,显然它可以正常工作:
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 < 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 => response.json()) .then(responseJson => { if (!this.isCancelled) { this.setState({ data: responseJson }); } }) .catch(error => { 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 ( <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> ); } }
但是,我有一个这样的自定义模型活页夹:
public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext) { if (bindingContext.ModelType != typeof(Tenant)) { return false; } var task = Task.Run(async () => { 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 => n.Headers.ContentDisposition.Name.Equals("file")); if (fileContent == null) { bindingContext.ModelState.AddModelError(bindingContext.ModelName, "Section 'file' is missed"); } var modelContent = provider.Contents.FirstOrDefault(n => n.Headers.ContentDisposition.Name.Equals("model")); if (modelContent == null) { bindingContext.ModelState.AddModelError(bindingContext.ModelName, "Section 'model' is missed"); } if (fileContent != null && modelContent != null) { model = JsonConvert.DeserializeObject<Tenant>(await modelContent.ReadAsStringAsync()); //model.Text = "<NativeTranslation>"; 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; } }
但文件内容为null,模型也为null
更新1:
我没想到这一点,但是似乎您在检查的名称时必须加引号ContentDispositionHeader。
ContentDispositionHeader
对于file,而不是
file
var fileContent = provider.Contents.FirstOrDefault(n => n.Headers.ContentDisposition.Name.Equals("file"));
肯定是
var fileContent = provider.Contents.FirstOrDefault(n => n.Headers.ContentDisposition.Name.Equals(@"""file"""));
因为model它一定是
model
var modelContent = provider.Contents.FirstOrDefault(n => n.Headers.ContentDisposition.Name.Equals(@"""model"""));
如果file通过provider.Contents[0].Headers.ContentDisposition (可能是索引[1]) 在Visual Studio的“即时窗口” 中检查,则会看到以下输出。 注意DispositionType: "form-data"和 之间引号的区别Name: "\"file\"",尽管两者都是类型String。
provider.Contents[0].Headers.ContentDisposition
DispositionType: "form-data"
Name: "\"file\""
String
{form-data; name="file"; filename="upload.txt"} CreationDate: null DispositionType: "form-data" FileName: "\"upload.txt\"" FileNameStar: null ModificationDate: null Name: "\"file\"" Parameters: Count = 2 ReadDate: null Size: null
我还没有React开发人员,所以我无法Web API通过您的React脚本执行代码,但是我在下面使用了这个简单的html表单…类似的概念。 通过类似的工具发布时,我看到相同的行为。提琴手。
React
Web API
<html> <head> </head> <body> <form method="post" enctype="multipart/form-data" action="http://url-to-your-webapi-controller-method-here"> <input type="file" name="file" /> <input type="text" name="model" /> <input type="submit" /> </form> </body> </html>