Webapi 控制器参数为空

Posted

技术标签:

【中文标题】Webapi 控制器参数为空【英文标题】:Webapi controller parameters are null 【发布时间】:2018-12-24 19:15:12 【问题描述】:

我有将 PUT 请求发送到 Web api 控制器的 reactjs 组件,但是接收到的对象(租户)为空。我可以附加远程调试器并注意到这一点。

我的反应组件:

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("TenantUrl", 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>
    );
  

我的租户控制器Put方法

[HttpPut]
        public async Task<IHttpActionResult> PutTenant([FromBody]Tenant tenant)
        
            var provider = new MultipartMemoryStreamProvider();
            var contentType = "";
            var content = new byte[0];
            await base.Request.Content.ReadAsMultipartAsync(provider);
            if (provider.Contents.Count > 0)
            
                contentType = provider.Contents[0].Headers.ContentType.MediaType;
                content = await provider.Contents[0].ReadAsByteArrayAsync();
            


            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;
            MemoryStream stream = new MemoryStream(content);
            blockBlob.UploadFromStream(stream);

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

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

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

和租户 POCO

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

        public byte[] content  get; set; 

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

堆栈跟踪:

"   at System.Net.Http.Formatting.Parsers.MimeMultipartBodyPartParser.ValidateArguments(HttpContent content, Int64 maxMessageSize, Boolean throwOnError)
   at System.Net.Http.Formatting.Parsers.MimeMultipartBodyPartParser..ctor(HttpContent content, MultipartStreamProvider streamProvider, Int64 maxMessageSize, Int32 maxBodyPartHeaderSize)
   at System.Net.Http.HttpContentMultipartExtensions.<ReadAsMultipartAsync>d__0`1.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
   at TenantManagementWebApi.Controllers.TenantController.<PutTenant>d__2.MoveNext() in C:\Users\levm3\source\repos\TenantManagementWebApi\Controllers\TenantController.cs:line 48
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Threading.Tasks.TaskHelpersExtensions.<CastToObject>d__3`1.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Web.Http.Controllers.ApiControllerActionInvoker.<InvokeActionAsyncCore>d__0.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Web.Http.Controllers.ActionFilterResult.<ExecuteAsync>d__2.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Web.Http.Filters.AuthorizationFilterAttribute.<ExecuteAuthorizationFilterAsyncCore>d__2.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Web.Http.Dispatcher.HttpControllerDispatcher.<SendAsync>d__1.MoveNext()"

我猜我的问题出在 react 而不是 webapi 中,如何更改我的 react 组件以使其正常工作?

【问题讨论】:

【参考方案1】:

您应该发送 JSON 对象,而不是发送多部分表单数据 来自反应组件的服务器

这将使您的生活更轻松。

示例

fetch('https://mywebsite.com/endpoint/', 
  method: 'POST',
  headers: 
    'Accept': 'application/json',
    'Content-Type': 'application/json',
  ,
  body: JSON.stringify(
    firstParam: 'yourValue',
    secondParam: 'yourOtherValue',
  )
)

参考:- React Native Docs

在 c# 中改变这个

public async Task<IHttpActionResult> PutTenant(Tenant tenant)

更新:- 如何将文件上传到服务器

根据this,你有3个选择

但如果您不经常交流,我更愿意转换为 base64。

所以你必须遵循这些步骤

    将文件转换为base64字符串参考this 将该 base64 字符串与其他数据一起发送到服务器 在服务器端接收文件作为base64字符串 在服务器上解码base64字符串,c#参考this

【讨论】:

您还没有解释最重要的部分,文件上传,请提供如何发送json数据的示例,同时根据我的需要处理文件上传。谢谢 不是一个坏主意,所以基本上文件本身就是一个字符串参数(base 64)。我喜欢这种方式

以上是关于Webapi 控制器参数为空的主要内容,如果未能解决你的问题,请参考以下文章

Protobuf : WebApi -> JS - 解码对象为空

将数据发布到 WebApi 时,DataTables.AspNet IDataTablesRequest 为空

调用具有 2 个参数的 WebAPI 控制器操作

如何在WebAPI的控制器的GET请求中传递参数

WebAPI 多个 Put/Post 参数

csharp c#在WebApi控制器中检查所有传入参数是否为null