How can I pass a moderately large volume of data t

2019-05-10 07:49发布

问题:

I've got this code in a Web API Controller:

[Route("{unit}/{begindate}/{enddate}")]
[HttpPost]
public void Post(string unit, string begindate, string enddate, 
    [FromBody] string stringifiedjsondata)
{
    List<ProduceUsageSPResults> _produceUsageList = JsonConvert.DeserializeObject<List<ProduceUsageSPResults>>(stringifiedjsondata);
    string _unit = unit;
    string _begindate = String.Format("{0}01", HyphenizeYYYYMM(begindate));
    string _enddate = GetEndOfMonthFor(enddate);
    string appDataFolder =  
HttpContext.Current.Server.MapPath("~/App_Data/");
    string htmlStr = ConvertProduceUsageListToHtml(_produceUsageList);
    string htmlFilename = string.Format("produceUsage_{0}_{1}_{2}.html", 
_unit, _begindate, _enddate); 
    string fullPath = Path.Combine(appDataFolder, htmlFilename);
    File.WriteAllText(fullPath, htmlStr);
}

It works fine when passing only a little bit of (contrived) data from the Client (Winforms app) via the "[FromBody]" arg, like so:

private async Task SaveProduceUsageFileOnServer(string beginMonth, string beginYear, string endMonth, string endYear, DataTable _dtUsage)
{
    string beginRange = String.Format("{0}{1}", beginYear, beginMonth);
    string endRange = String.Format("{0}{1}", endYear, endMonth);
    HttpClient client = new HttpClient {BaseAddress = new Uri("http://localhost:42176")};
    string dataAsJson = "[{\"ItemDescription\": \"DUCKBILLS, GRAMPS-EIER 70CT  42#\",\"PackagesMonth1\": 1467}]";
    //string dataAsJson = JsonConvert.SerializeObject(_dtUsage);
    String uriToCall = String.Format("/api/produceusage/{0}/{1}/{2}", _unit, beginRange, endRange);
    var content = new FormUrlEncodedContent(new[]
    {
        new KeyValuePair<string, string>("", dataAsJson)
    });
    HttpResponseMessage response = await client.PostAsync(uriToCall, content);
}

However, if I reverse the comments on the assignment to dataAsJson so that it sends the real data, like so:

//string dataAsJson = "[{\"ItemDescription\": \"DUCKBILLS, GRAMPS-EIER 70CT  42#\",\"PackagesMonth1\": 1467}]";
string dataAsJson = JsonConvert.SerializeObject(_dtUsage);

...it fails; there is no err msg; the DataTable (_dtUsage) is serialized to json just fine with the JsonConvert.SerializeObject() method; it's just that there's a "lot" of data (a few thousand records). The client's call to PostAsync() is never reached, and so the server's Post method is never reached.

Is there a workaround that will allow this to succeed when sending more than just a trivial amount of data? I don't really like passing that much data over the wire (currently the client and server are both running locally, but thinking about the situation once deployed), but the other option is to execute the same Stored Procedure from both the client (to generate Excel spreadsheet files there) and from the server (to convert the data to HTML). It seems to be one of those "Catch-22" situations ("impounded if I do, sequestered if I don't").

UPDATE

Testing out user3093073's idea, I added this to the system.webServer section of Web.config:

<security>
  <requestFiltering>
    <requestLimits>
      <headerLimits>
        <add header="Content-type" sizeLimit="10000000" />
      </headerLimits>
    </requestLimits>
  </requestFiltering>
</security>

(based on what I found here, but it's still not working...

UPDATE 2

Closer to user user3093073's answer, I also tried this:

<requestFiltering>
   <requestLimits maxAllowedContentLength="10000000">
     <!--<headerLimits>
       <add header="Content-type" sizeLimit="100" />
     </headerLimits>-->
   </requestLimits>
</requestFiltering>

...also to no avail.

UPDATE 3

Note that I put the code above in every Web.config file in the site, namely:

The Web.config file below the \[ProjectName]\Views folder
The Web.config file below the \[ProjectName] folder
The two Web.config files below the \[ProjectName]\Web.config file, namely "Web.Debug.config" and "Web.Release.config"

...or, another way of viewing their locations:

\PlatypusReports\Web.config
\PlatypusReports\Web.config\Web.Debug.config
\PlatypusReports\Web.config\Web.Release.config
\PlatypusReports\Views\Webconfig

UPDATE 4

After being away from this for several days and coming back to it, it now seems plain to me that the "real" data I was trying to pass is a different animal than the phony/test data, which worked.

The test data was a collection of a simple KeyValuePair. The real data, though, is more complex. So when I tried to convert that complex data to a simple KeyValuePair, trouble was sure to follow. So instead of this:

new KeyValuePair<string, string>("", dataAsJson)

...I need something like this:

new List<DeliveryPerformanceClass>("", dataAsJson)

...but that is not working either; I get several compile errors, such as, "'System.Collections.Generic.List' does not contain a constructor that takes 2 arguments'"

If I remove the first arg so that it's:

new List<DeliveryPerformanceClass>(dataAsJson)

...I get other compile errors, such as, "Argument 1: cannot convert from 'string' to 'int'"

回答1:

I believe that since you have already specified the <requestFiltering> settings, you then need to also set the maxRequestLength. As @brewsky mentions, this is defaulted it is only 4MB. Check your web.config. This is setup for 2GB

<configuration>
    <system.web>
        <httpRuntime maxRequestLength="2147483648" />
    </system.web>
</configuration>

This SO answer does seem to address a similar issue.



回答2:

You have to setup IIS to accept big files. By default it is only 4MB or so. Check your web.config. This is setup for 2GB:

        <requestLimits maxAllowedContentLength="2147483648" />