问题描述
我正在将 Azure 功能从 .net 3.1 升级到 5,但遇到了一些困难.许多事情从 3.1 更改为 5,我正在尝试找出细微差别.我有一个接受多部分/表单数据帖子的函数,它是这样创建的:
I'm upgrading an Azure function from .net 3.1 to 5 and am having some difficulties. Many things changed from 3.1 to 5 and I'm trying to figure out the nuances. I have a function that accepts a multipart/form-data post which is created like this:
public class EmailService
{
private EmailPost _post;
public EmailService() { }
public EmailService(EmailPost post)
{
_post = post;
}
public EmailPost Post => _post;
public async Task<EmailServiceResult> SendAsync()
{
try
{
var httpClient = new HttpClient();
var form = new MultipartFormDataContent();
form.Add(new StringContent(string.Join(",", _post.To)), "To");
form.Add(new StringContent(string.Join(",", _post.Cc)), "Cc");
form.Add(new StringContent(string.Join(",", _post.Bcc)), "Bcc");
form.Add(new StringContent(_post.From), "From");
form.Add(new StringContent(_post.Subject), "Subject");
form.Add(new StringContent(_post.Body), "Body");
form.Add(new StringContent(_post.IsHtml.ToString()), "IsHtml");
form.Add(new StringContent(_post.IsPriority.ToString()), "IsPriority");
// any files?
foreach (var i in _post.Files)
{
if (!File.Exists(i))
{
throw new Exception("File does not exist. Make sure it's a full path.");
}
var filename = i.Substring(i.LastIndexOf("\\", StringComparison.InvariantCultureIgnoreCase) + 1);
var fs = new FileStream(i, FileMode.Open, FileAccess.Read);
var fileByteArray = new byte[fs.Length];
fs.Read(fileByteArray, 0, fileByteArray.Length);
form.Add(new ByteArrayContent(fileByteArray, 0, fileByteArray.Length), "filename", filename);
fs.Close();
}
var endPointUrl = "my az function endpoint url";
// if it's local, then use the test version
if (Debugger.IsAttached)
endPointUrl = "local url here";
var response = await httpClient.PostAsync(endPointUrl, form);
response.EnsureSuccessStatusCode();
httpClient.Dispose();
}
catch (Exception ex)
{
return new EmailServiceResult
{
Success = false,
Message = ex.Message
};
}
return new EmailServiceResult {Success = true};
}
}
然后是 Azure 函数:
Then the Azure function:
public class Email
{
private ILogger _log;
private IConfiguration _config;
public Email(IConfiguration config, ILogger<Email> logger)
{
_config = config;
_log = logger;
}
[Function("Email")]
public async Task<HttpResponseData> Run([HttpTrigger(AuthorizationLevel.Function, "post", Route = "email")]HttpRequestData req, FunctionContext context)
{
try
{
// Suck the request body into a stream
var ms = new MemoryStream();
await req.Body.CopyToAsync(ms);
ms.Position = 0;
// look through all the keys
foreach (var h in req.Headers)
_log.LogInformation(h.Key + ":" + string.Join(',', h.Value));
var streamContent = new StreamContent(ms);
// This line was used with 3.1 but isn't available any more because the req object is different (HttpRequestData in v5 vs HttpRequest in v3)
// streamContent.Headers.ContentType = req.Content.Headers.ContentType;
// so I did it this way instead
var ct = req.Headers.FirstOrDefault(o => o.Key == "Content-Type").Value.FirstOrDefault().Split(';');
// this line is the kicker. it doesn't accept a value with a boundary, which is needed later
// multipart/form-data; boundary="b0ff40b2-8b47-4746-b85b-a876924e4e4c"
// so I just pass in: multipart/form-data
var mthv = new System.Net.Http.Headers.MediaTypeHeaderValue(ct[0]);
streamContent.Headers.ContentType = mthv;
// this line needs the boundary, which I can't figure out how to enter.
var provider = await streamContent.ReadAsMultipartAsync();
// ... more processing of fields
return req.CreateResponse(System.Net.HttpStatusCode.OK);
}
catch (Exception ex)
{
///... error handling
}
}
}
然后为了完整起见,这是我的程序文件:
And then for completeness, here's my Program file:
public class Program
{
public static async Task Main(string[] args)
{
var host = new HostBuilder()
.ConfigureAppConfiguration(c =>
{
c.SetBasePath(Environment.CurrentDirectory);
c.AddJsonFile("local.settings.json", optional: true, reloadOnChange: true);
// Add Environment Variables since we need to get the App Configuration connection string from settings.
c.AddEnvironmentVariables();
// Call Build() so we can get values from the IConfiguration.
var config = c.Build();
})
.ConfigureFunctionsWorkerDefaults()
.ConfigureServices(s =>
{
// s.AddSingleton<IHttpResponderService, DefaultHttpResponderService>();
})
.Build();
await host.RunAsync();
}
我很难弄清楚如何使用:var provider = await streamContent.ReadAsMultipartAsync();
需要边界但是:var mthv = new System.Net.Http.Headers.MediaTypeHeaderValue(ct[0]);
不允许有边界.
I'm having a tough time figuring out how to use:var provider = await streamContent.ReadAsMultipartAsync();
which needs the boundary but:var mthv = new System.Net.Http.Headers.MediaTypeHeaderValue(ct[0]);
doesn't allow for a boundary.
我得到的错误是:提供的HttpContent"实例无效.它没有带有边界"参数的多部分"内容类型标头.(参数'内容')
The error I get is: Invalid 'HttpContent' instance provided. It does not have a 'multipart' content-type header with a 'boundary' parameter. (Parameter 'content')
如果有人对这个特定问题有任何建议,我将不胜感激.或者,如果有另一种方法可以完全做到这一点,那也没关系.谢谢!
If anyone has some advice on this specific issue I would appreciate it. Or if there's another way to do it completely, that would also be fine. Thanks!
推荐答案
Media-type headers 由带有可选参数的 media-type 组成.MediaTypeHeaderValue
的构造函数只接受媒体类型;参数应通过 Parameters
属性:
Media-type headers are made up of a media-type with optional parameters. The constructor for MediaTypeHeaderValue
only takes a media-type; the parameters should be set via the Parameters
property:
var mthv = new System.Net.Http.Headers.MediaTypeHeaderValue("multipart/form-data");
mthv.Parameters.Add(new NameValueHeaderValue("boundary", "b0ff40b2-8b47-4746-b85b-a876924e4e4c"));
streamContent.Headers.ContentType = mthv;
如果您需要解析传入请求中的 boundary
值并使用相同的值,那么我建议您查看 MediaTypeHeaderValue.Parse
.
If you need to, say, parse the boundary
value on the incoming request and use the same value, then I'd recommend looking into MediaTypeHeaderValue.Parse
.
这篇关于.net 5 Azure Function 分段文件上传问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!