如何在ASP.NET Core控制器中接收“多部分/混合" [英] How to receive `multipart/mixed` in an ASP.NET Core controller

查看:121
本文介绍了如何在ASP.NET Core控制器中接收“多部分/混合"的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

旧版系统会向我发送此信息:

  POST/xml HTTP/1.1主机:localhost:9000用户代理:curl/7.64.1接受: */*内容长度:321内容类型:多部分/混合;边界= ------------------------ a9dd0ab37a224967-------------------------- a9dd0ab37a224967内容处置:附件;name ="part1"内容类型:text/xml< foo> bar</foo>-------------------------- a9dd0ab37a224967内容处置:附件;名称=第2部分"内容类型:application/json{'foo':'bar'}-------------------------- a9dd0ab37a224967-- 

我需要将第一部分解释为原始的 XElement ;在第二部分中,我需要常规的模型绑定.

我尝试:

  class Part2 {公共字符串foo {get;放;}}[HttpPost][Route("/xml"))公共字符串Post1([FromBody] XElement part1,[FromBody] Part2 part2){返回part1.ToString()+,"+ part2.foo;} 

但是ASP.NET不允许使用以 [FromBody] 装饰的参数.

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

解决方案

没有内置机制来处理这种类型的帖子数据( multipart/mixed 实际上具有无限的可能性,并且它将很难将其绑定到一般意义上),但是,您可以使用 使用Microsoft.AspNetCore.Http;使用Microsoft.AspNetCore.Http.Features;使用Microsoft.AspNetCore.WebUtilities;使用Microsoft.Net.Http.Headers;使用系统;使用System.Collections.Generic;使用System.IO;使用System.Net.Mime;使用System.Text;命名空间YourNamespace.Utilities{公共静态类MutipartMixedHelper{公共静态异步IAsyncEnumerable< ParsedSection>ParseMultipartMixedRequestAsync(HttpRequest请求){//提取,清理和验证边界var boundary = HeaderUtilities.RemoveQuotes(MediaTypeHeaderValue.Parse(request.ContentType).Boundary).Value;如果(string.IsNullOrWhiteSpace(boundary)||(boundary.Length> new FormOptions().MultipartBoundaryLengthLimit)){抛出新的InvalidDataException(边界被射击");}//根据边界创建一个新的阅读器var reader = new MultipartReader(boundary,request.Body);//从MultipartReader开始阅读各节,直到没有更多内容为止MultipartSection部分;while((section =等待reader.ReadNextSectionAsync())!= null){//解析内容类型var contentType = new ContentType(section.ContentType);//创建一个新的ParsedSecion并开始填写详细信息var parsedSection =新的ParsedSection{IsJson = contentType.MediaType.Equals("application/json",StringComparison.OrdinalIgnoreCase),IsXml = contentType.MediaType.Equals("text/xml",StringComparison.OrdinalIgnoreCase),编码= Encoding.GetEncoding(contentType.CharSet)};//必须为XML或JSONif(!parsedSection.IsXml&!parsedSection.IsJson){抛出新的InvalidDataException(仅处理json/xml");}//解析内容如果(ContentDispositionHeaderValue.TryParse(section.ContentDisposition,输出为var contentDisposition)&&contentDisposition.DispositionType.Equals("attachment")){//保存名称parsedSection.Name = contentDisposition.Name.Value;//使用正确的编码创建一个新的StreamReader//将基础流保持打开状态使用(var streamReader = new StreamReader(section.Body,parsedSection.Encoding,leaveOpen:true)){parsedSection.Data =等待streamReader.ReadToEndAsync();收益回报parsedSection;}}}}}公共密封类ParsedSection{公共布尔IsJson {get;放;}公共布尔IsXml {get;放;}公共字符串名称{get;放;}公用字串Data {get;放;}公共编码编码{get;放;}}}

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

  [HttpPost,Route("TestMultipartMixedPost"))]公共异步Task< IActionResult>考验我(){等待foreach(MutipartMixedHelper中的var parsedSection.ParseMultipartMixedRequestAsync(Request)){Debug.WriteLine($名称:{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("-----------------------");}返回Ok();} 

您的端点将输出:

 名称:part1编码:Unicode(UTF-8)IsJson:错IsXml:正确数据:< foo> bar</foo>-----------------------名称:part2编码:Unicode(UTF-8)IsJson:正确IsXml:错误数据:{"foo":"bar"}----------------------- 

这时,您必须根据返回对象的属性反序列化.

Legacy systems will send me this:

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--

The first part I need to interpret as raw XElement; for the second part I would like the usual model binding.

I try this:

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

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

But ASP.NET does not allow more than one parameter decorated with [FromBody].

How do I make my ASP.NET Core service receive http requests with content-type multipart/mixed?

解决方案

There is no built-in mechanism to handle this type of post data (multipart/mixed has virtually unlimited possibilities, and it would be hard to bind to it in a generic sense), but, you can easily parse the data yourself using the MultipartReader object.

I am going to assume that all the data that is coming in has a disposition of attachment and that only JSON and XML content-types are valid. But this should be open-ended enough for you to modify as you see fit.

Take a look at this static helper:

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; }
    }
}

You can call this method from your endpoint, like so:

[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();
}

Your endpoint would output:

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"}
-----------------------

At this point, you'd have to deserialize based on the properties of the returned objects.

这篇关于如何在ASP.NET Core控制器中接收“多部分/混合"的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆