简体   繁体   中英

Why is model-binding not occurring for IFormFile in my Asp.Net controller?

I've written some code (MetadataProcessor) that locates an xml file and sends it to an API ( http://localhost:5000/api/Events/UploadAzureEncodedMetadata ) to be parsed. However, when the API is called, the IFormFile parameter is always null. I've examined the resulting http request with fiddler to determine what is causing the model-binding problem (if that's what it is), but I see nothing wrong.

Metadata processor:

 // Build a path to the directory containing the metadata file
            string folderName = string.Format("OUTPUT_ENCODER_{0}_{1}", eventId, id);
            string folderPath = Path.Combine(
                this._config.MediaBasePath,
                clientId.ToString(),
                eventId.ToString(),
                "results",
                folderName);

            // Find the metadata file
            string fileName = null;
            byte[] data = null;
            bool exists = false;
            string[] files = Directory.GetFiles(folderPath);

            foreach (var file in files)
            {
                if (file.EndsWith("_metadata.xml"))
                {
                    data = File.ReadAllBytes(file);
                    exists = true;
                    fileName = file;
                    break;
                }
            }

            // Generate token for access to Events controller
            TokenProvider tokens = new TokenProvider(this._config, this._logger);
            var authValue = new AuthenticationHeaderValue("Bearer", tokens.GetTokenObject(clientId));


            // Build the HttpClient
            HttpClient client = new HttpClient()
            {
                DefaultRequestHeaders =
                                            {
                                                Authorization = authValue
                                            }
            };

            // Bundle the xml data into the request body
            ByteArrayContent formFile = new ByteArrayContent(data, 0, data.Length);

            MultipartFormDataContent multiContent = new MultipartFormDataContent();
            multiContent.Add(formFile, "formFile", fileName);

            // Post to EventsController
            var baseUri = new Uri(this._config.BaseUrl);

            var url = new Uri(baseUri, string.Format("{0}api/Events/UploadAzureEncodedMetadata/{1}", baseUri, videoId));
            var result = await client.PostAsync(url.ToString(), multiContent);

The MVC controller in the API:

[HttpPost("UploadAzureEncodedMetadata/{id}")]
        [ProducesResponseType(201)]
        [ProducesResponseType(400)]
        [ProducesResponseType(401)]
        [ProducesResponseType(404)]
        public async Task<IActionResult> UpdateDuration([FromRoute]Guid id, [FromForm]IFormFile formFile)
        {
            // Find the video to update
            var video = await this.context.Videos.Where(v => v.Id == id).SingleOrDefaultAsync();
            if(formFile == null)
            {
                return this.BadRequest();
            }

            // Ensure that the service account has authorization
            var eventId = video.EventId;
            if (!await this.securityProvider.HasEventEditAccessAsync(eventId))
            {
                return this.Unauthorized();
            }

            // Attempt to deserialize the xml 
            int videoDuration = 0;

            try
            {
                var serializer = new XmlSerializer(typeof(AssetFiles));

                Stream stream = formFile.OpenReadStream();

                var assetFiles = (AssetFiles)serializer.Deserialize(stream);

                // Find duration and convert to seconds
                string durationString = assetFiles.AssetFile.Duration;
                TimeSpan durationTimeSpan = XmlConvert.ToTimeSpan(durationString);
                videoDuration = (int)durationTimeSpan.TotalSeconds;
            }
            catch(Exception ex)
            {
                return this.BadRequest();
            }

            // Update the video entry
            video.Duration = videoDuration;
            await this.context.SaveChangesAsync();

            return this.NoContent();

        }

Lastly, here's a raw readout of the http request produced by the MetadataProcessor, that resulted in a null value for formFile in the controller:

POST http://localhost:5000/api/Events/UploadAzureEncodedMetadata/dc14829c-3d1b-4379-615b-08d5f8d07e16 HTTP/1.1
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI1OWNiODg5YS01YjRjLTRlMzUtOWMyOS0zYWQ3MGU4NTJhYTgiLCJqdGkiOiI5OTU3MWIyMy0xYTQwLTQ0OTUtOTE2Zi03NDY5YjYwYzI1N2MiLCJpYXQiOjE1MzM4MzM4NzAsIkV2ZW50UmVnVXNlciI6ZmFsc2UspOgHjFVudElkIjoiMTdhMjc4NDYtOTc1My00OGEzLTRlYzEtMDhkNGUxMjgwZjVhIiwibmJmIjoxNTMzODMzODcwLCJleHAiOjE1MzM4MzM5MzAsImlzcyI6IjYzYmVmM2NkYTM1OTRmZjBhOTdiYWFiYWJjYTQzODhmIiwiYXVkIjoiSW5mZXJub0NvcmUifQ.rhIjYRUtFjHWrrgd9XnmW4kMXaZ5UFyr2ApNK1EBJRI
Content-Type: multipart/form-data; boundary="1b6c2d6e-53d6-414c-bb1b-681ff9c766f0"
Content-Length: 2951
Host: localhost:5000

--1b6c2d6e-53d6-414c-bb1b-681ff9c766f0
Content-Disposition: form-data; name=formFile; filename="C:\tmp\17a27846-9753-48a3-4ec1-08d4e1280f5a\213e65ee-fff9-4668-b123-7d0746bb4b05\results\OUTPUT_ENCODER_213e65ee-fff9-4668-b123-7d0746bb4b05_00000000-0000-0000-0000-000000000000\a35f0612-7a07-4bec-a9fd-a61d2a2f71bb_metadata.xml"; filename*=utf-8''C%3A%5Ctmp%5C17a27846-9753-48a3-4ec1-08d4e1280f5a%5C213e65ee-fff9-4668-b123-7d0746bb4b05%5Cresults%5COUTPUT_ENCODER_213e65ee-fff9-4668-b123-7d0746bb4b05_00000000-0000-0000-0000-000000000000%5Ca35f0612-7a07-4bec-a9fd-a61d2a2f71bb_metadata.xml

<?xml version="1.0" encoding="utf-8"?>
<AssetFiles xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.microsoft.com/windowsazure/mediaservices/2014/07/mediaencoder/inputmetadata">
  <AssetFile Name="bcff8e9a-7cb7-4d09-abd2-f04a83df1be1.mp4" Size="383631" Duration="PT5.568S" NumberOfStreams="2" FormatNames="mov,mp4,m4a,3gp,3g2,mj2" FormatVerboseName="QuickTime / MOV" StartTime="PT0S" OverallBitRate="551">
    <VideoTracks>
      <VideoTrack Id="1" Codec="h264" CodecLongName="H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10" TimeBase="1/90000" NumberOfFrames="166" StartTime="PT0S" Duration="PT5.533S" FourCC="avc1" Profile="Constrained Baseline" Level="3.0" PixelFormat="yuv420p" Width="560" Height="320" DisplayAspectRatioNumerator="0" DisplayAspectRatioDenominator="1" SampleAspectRatioNumerator="0" SampleAspectRatioDenominator="1" FrameRate="30" Bitrate="465" HasBFrames="0">
        <Disposition Default="1" Dub="0" Original="0" Comment="0" Lyrics="0" Karaoke="0" Forced="0" HearingImpaired="0" VisualImpaired="0" CleanEffects="0" AttachedPic="0" />
        <Metadata key="creation_time" value="2010-03-20T21:29:11.000000Z" />
        <Metadata key="language" value="und" />
        <Metadata key="encoder" value="JVT/AVC Coding" />
      </VideoTrack>
    </VideoTracks>
    <AudioTracks>
      <AudioTrack Id="2" Codec="aac" CodecLongName="AAC (Advanced Audio Coding)" TimeBase="1/48000" NumberOfFrames="261" StartTime="PT0S" Duration="PT5.568S" SampleFormat="fltp" ChannelLayout="mono" Channels="1" SamplingRate="48000" Bitrate="83" BitsPerSample="0">
        <Disposition Default="1" Dub="0" Original="0" Comment="0" Lyrics="0" Karaoke="0" Forced="0" HearingImpaired="0" VisualImpaired="0" CleanEffects="0" AttachedPic="0" />
        <Metadata key="creation_time" value="2010-03-20T21:29:11.000000Z" />
        <Metadata key="language" value="eng" />
      </AudioTrack>
    </AudioTracks>
    <Metadata key="major_brand" value="mp42" />
    <Metadata key="minor_version" value="0" />
    <Metadata key="compatible_brands" value="mp42isomavc1" />
    <Metadata key="creation_time" value="2010-03-20T21:29:11.000000Z" />
    <Metadata key="encoder" value="HandBrake 0.9.4 2009112300" />
  </AssetFile>
</AssetFiles>
--1b6c2d6e-53d6-414c-bb1b-681ff9c766f0--

I'm aware that there are other ways to upload xml data, but I'd prefer to keep the xml parsing on the api side of things, if possible. Thanks!

Problem was solved when I removed the [FromRoute] attribute from the IFormFile parameter and the [ApiController] attribute (not shown in code) from the MVC controller. So as not to disrupt functionality on the controller, I'll be creating a new controller specifically for the UpdateDuration() action.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM