如何在 ASP.NET Core 控制器中接收“multipart/mixed”

2024-04-28

旧系统会向我发送以下内容:

POST /xml HTTP/1.1
Host: localhost:9000
User-Agent: curl/7.64.1
Accept: */*
Content-Length: 321
Content-Type: multipart/mixed; boundary=------------------------a9dd0ab37a224967

--------------------------a9dd0ab37a224967
Content-Disposition: attachment; name="part1"
Content-Type: text/xml

<foo>bar</foo>
--------------------------a9dd0ab37a224967
Content-Disposition: attachment; name="part2"
Content-Type: application/json

{'foo': 'bar'}
--------------------------a9dd0ab37a224967--

我需要将第一部分解释为原始部分XElement;对于第二部分,我想要通常的模型绑定。

我试试这个:

class Part2 { 
    public string foo { get; set; }
}
    

[HttpPost]
[Route("/xml")]
public string Post1([FromBody] XElement part1, [FromBody] Part2 part2 )
{
    return part1.ToString() + ", " + part2.foo;
}

但 ASP.NET 不允许使用多个参数修饰[FromBody].

如何让我的 ASP.NET Core 服务接收具有内容类型的 http 请求multipart/mixed?


没有内置机制来处理这种类型的发布数据(multipart/mixed实际上具有无限的可能性,并且很难在一般意义上绑定到它),但是,您可以使用以下方法轻松地自己解析数据MultipartReader https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.webutilities.multipartreader object.

我假设所有传入的数据都有以下处理:attachment并且只有 JSON 和 XML 内容类型有效。但这应该是足够开放的,以便您可以根据需要进行修改。

看看这个静态助手:

using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.WebUtilities;
using Microsoft.Net.Http.Headers;
using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Mime;
using System.Text;

namespace YourNamespace.Utilities
{
    public static class MutipartMixedHelper
    {
        public static async IAsyncEnumerable<ParsedSection> ParseMultipartMixedRequestAsync(HttpRequest request)
        {
            // Extract, sanitize and validate boundry
            var boundary = HeaderUtilities.RemoveQuotes(
                MediaTypeHeaderValue.Parse(request.ContentType).Boundary).Value;

            if (string.IsNullOrWhiteSpace(boundary) ||
                (boundary.Length > new FormOptions().MultipartBoundaryLengthLimit))
            {
                throw new InvalidDataException("boundry is shot");
            }

            // Create a new reader based on that boundry
            var reader = new MultipartReader(boundary, request.Body);

            // Start reading sections from the MultipartReader until there are no more
            MultipartSection section;
            while ((section = await reader.ReadNextSectionAsync()) != null)
            {
                // parse the content type
                var contentType = new ContentType(section.ContentType);

                // create a new ParsedSecion and start filling in the details
                var parsedSection = new ParsedSection
                {
                    IsJson = contentType.MediaType.Equals("application/json",
                        StringComparison.OrdinalIgnoreCase),
                    IsXml = contentType.MediaType.Equals("text/xml",
                        StringComparison.OrdinalIgnoreCase),
                    Encoding = Encoding.GetEncoding(contentType.CharSet)
                };

                // Must be XML or JSON
                if (!parsedSection.IsXml && !parsedSection.IsJson)
                {
                    throw new InvalidDataException("only handling json/xml");
                }

                // Parse the content disosition
                if (ContentDispositionHeaderValue.TryParse(
                        section.ContentDisposition, out var contentDisposition) &&
                        contentDisposition.DispositionType.Equals("attachment"))
                {
                    // save the name
                    parsedSection.Name = contentDisposition.Name.Value;

                    // Create a new StreamReader using the proper encoding and
                    // leave the underlying stream open
                    using (var streamReader = new StreamReader(
                        section.Body, parsedSection.Encoding, leaveOpen: true))
                    {
                        parsedSection.Data = await streamReader.ReadToEndAsync();
                        yield return parsedSection;
                    }
                }
            }
        }
    }

    public sealed class ParsedSection
    {
        public bool IsJson { get; set; }
        public bool IsXml { get; set; }
        public string Name { get; set; }
        public string Data { get; set; }
        public Encoding Encoding { get; set; }
    }
}

您可以从端点调用此方法,如下所示:

[HttpPost, Route("TestMultipartMixedPost")]
public async Task<IActionResult> TestMe()
{
    await foreach (var parsedSection in MutipartMixedHelper
        .ParseMultipartMixedRequestAsync(Request))
    {
        Debug.WriteLine($"Name: {parsedSection.Name}");
        Debug.WriteLine($"Encoding: {parsedSection.Encoding.EncodingName}");
        Debug.WriteLine($"IsJson: {parsedSection.IsJson}");
        Debug.WriteLine($"IsXml: {parsedSection.IsXml}");
        Debug.WriteLine($"Data: {parsedSection.Data}");
        Debug.WriteLine("-----------------------");
    }

    return Ok();
}

您的端点将输出:

Name: part1
Encoding: Unicode (UTF-8)
IsJson: False
IsXml: True
Data: <foo>bar</foo>
-----------------------
Name: part2
Encoding: Unicode (UTF-8)
IsJson: True
IsXml: False
Data: {"foo": "bar"}
-----------------------

此时,您必须根据返回对象的属性进行反序列化。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何在 ASP.NET Core 控制器中接收“multipart/mixed” 的相关文章

随机推荐