在 IIS 上发布包含 jpg 和 json 的多部分请求会导致内部服务器错误和 win32 状态 64
Posted
技术标签:
【中文标题】在 IIS 上发布包含 jpg 和 json 的多部分请求会导致内部服务器错误和 win32 状态 64【英文标题】:Posting multipart request containing jpg and json causes interal server error and win32 status 64 on IIS 【发布时间】:2020-12-26 19:38:07 【问题描述】:我的 winforms 应用程序向 asp.net Web api 服务发送 PUT/POST 请求。大多数情况下,它将 json 对象作为请求的内容发送,并且这些请求运行良好。有时,当它需要将 jpg 与 json 对象一起发送时,它会创建 multiPart 请求,其中 jpg 是内容,json 在 url 中传递,如下所示:
example.com/EditPart?id=193&PartJson=<serialized json object>
这里是发送请求的方法的完整定义:
public async void Edit(string attachmentPath)
using (var client = new HttpClient())
var serializedProduct = JsonConvert.SerializeObject(this, new JsonSerializerSettings DateFormatString = "yyyy-MM-ddTHH:mm:ss.fff" );
string url = Secrets.ApiAddress + $"Edittypeof(T).Name?token=" + Secrets.TenantToken + $"&id=this.Id&UserId=RuntimeSettings.UserId" + $"&typeof(T).NameJson=serializedProduct";
MultipartFormDataContent content = new MultipartFormDataContent();
try
using (var fileStream = System.IO.File.OpenRead(attachmentPath))
var fileInfo = new FileInfo(attachmentPath);
StreamContent fcontent = new StreamContent(fileStream);
fcontent.Headers.Add("Content-Type", "application/octet-stream");
fcontent.Headers.Add("Content-Disposition", "form-data; name=\"file\"; filename=\"" + fileInfo.Name + "\"");
content.Add(fcontent, "file", fileInfo.Name);
System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12;
var result = await client.PutAsync(url, content);//<--stops here
if (result.IsSuccessStatusCode)
MessageBox.Show("Edycja zakończona powodzeniem!");
else
MessageBox.Show("Serwer zwrócił błąd przy próbie edycji. Wiadomość: " + result.ReasonPhrase);
catch (Exception ex)
MessageBox.Show("Problem z wysyłką żądania do serwera. Wiadomość: " + ex.Message + ". " + ex.InnerException.Message, "Błąd żądania", MessageBoxButtons.OK, MessageBoxIcon.Error);
它转到await client.PutAsync(url, content);
,然后直接进入异常状态:发送请求时发生错误。底层连接已关闭。接收时出现意外错误。
当我检查 IIS 日志时,我看到请求正确到达服务器,但以状态 500 和 win32 状态 64 结束。我什至将使用 NLog 的日志记录到 EditPart 方法,但它从不触发。看起来像该方法根本没有被调用,但当然从 IIS 日志中我知道它是。
这是关于 asp.net web api 的完整 EditPart 定义:
[HttpPut]
[Route("EditPart")]
[ResponseType(typeof(void))]
public HttpResponseMessage EditPart(string token, int id, int UserId, string PartJson)
try
javascriptSerializer jss = new JavaScriptSerializer();
JDE_Parts item = jss.Deserialize<JDE_Parts>(PartJson);
try
var items = db.JDE_Parts.Where(u => u.PartId == id);
if (items.Any())
Logger.Info("EditPart: Znalazłem odpowiednią część. Przystępuję do edycji Id=id, UserId=UserId", id, UserId);
JDE_Parts orgItem = items.FirstOrDefault();
//handle image
var httpRequest = HttpContext.Current.Request;
if (httpRequest.ContentLength > 0)
//there's a new content
if (httpRequest.ContentLength > Static.RuntimeSettings.MaxFileContentLength)
return Request.CreateResponse(HttpStatusCode.BadRequest, $"item.Name przekracza dopuszczalną wielość pliku (Static.RuntimeSettings.MaxFileContentLength MB) i został odrzucony");
var postedFile = httpRequest.Files[0];
string filePath = "";
if (postedFile != null && postedFile.ContentLength > 0)
Logger.Info("EditPart: Znaleziono nowe pliki. Przystępuję do zapisu na dysku. Id=id, UserId=UserId", id, UserId);
var ext = postedFile.FileName.Substring(postedFile.FileName.LastIndexOf('.'));
filePath = $"Static.RuntimeSettings.Path2Filesitem.Token + ext.ToLower()";
string oFileName = db.JDE_Parts.Where(p => p.PartId == id).FirstOrDefault().Image;
if (!string.IsNullOrEmpty(oFileName))
// There was a file, must delete it first
Logger.Info("EditPart: Istnieją poprzednie pliki pod tą nazwą. Przystępuję do usuwania. Id=id, UserId=UserId", id, UserId);
System.IO.File.Delete(Path.Combine(RuntimeSettings.Path2Files, oFileName));
System.IO.File.Delete(Path.Combine(RuntimeSettings.Path2Thumbs, oFileName));
postedFile.SaveAs(filePath);
Logger.Info("EditPart: Zapisano pliki. Przystępuję do utworzenia miniatury.. Id=id, UserId=UserId", id, UserId);
Static.Utilities.ProduceThumbnail(filePath);
item.Image = item.Token + ext.ToLower();
try
Logger.Info("EditPart: Przystępuję do zapisu zmian w bazie danych. Id=id, UserId=UserId", id, UserId);
db.Entry(orgItem).CurrentValues.SetValues(item);
db.Entry(orgItem).State = EntityState.Modified;
db.SaveChanges();
Logger.Info("EditPart: Zapisano zmiany w bazie. Id=id, UserId=UserId", id, UserId);
catch (Exception ex)
Logger.Error("Błąd w EditPart. Id=id, UserId=UserId. Szczegóły: Message, nowa wartość: item", id, UserId, ex.ToString(), item);
return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, ex);
catch (Exception ex)
Logger.Error("Błąd w EditPart. Id=id, UserId=UserId. Szczegóły: Message, nowa wartość: item", id, UserId, ex.ToString(), item);
return Request.CreateResponse(HttpStatusCode.NoContent);
catch (Exception ex)
Logger.Error("Błąd w EditPart. Id=id, UserId=UserId. Szczegóły: Message", id, UserId, ex.ToString());
return Request.CreateResponse(HttpStatusCode.InternalServerError, ex.Message);
return Request.CreateResponse(HttpStatusCode.NoContent);
奇怪的是,这一切都运行了好几个月,直到前一段时间才停止。另外,当我在我的机器上调试 asp.net 应用程序时,请求运行没有任何问题。我还能做什么追踪这个问题?
【问题讨论】:
HTTP 500 错误是一般错误,表明服务器出现问题。 windows 错误 64 表示网络名称不再可用。我怀疑该错误是由于在请求开始时使用 TCP 发生的 TLS 身份验证造成的。五年前,由于安全漏洞,业界决定消除 TLS 1.0/1.1。今年 6 月,微软推送了一项安全更新,禁用 TLS 1.0/1.1 服务器并强制客户端使用 TLS 1.2 或更高版本。您的请求可能仍在使用默认的 TLS 版本。 尝试:SecurityProtocolType.Tls12。请参阅:inthetechpit.com/2018/12/15/… 感谢 jdweng,但事实并非如此。我已经放置了 System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;在发出请求之前,但它并没有改变任何事情。这是有道理的,因为这个方法的另一个版本向同一个 API 发送请求但没有文件,并且它一直在正常工作 可能是证书不支持 TLS 1.2 的加密模式/密钥。您可以使用诸如wireshark 或fiddler 之类的嗅探器来查看TSL 是否完成。 我使用了 Fiddler 但它只说 504 ReadResponse() failed: 服务器没有为此请求返回完整的响应。服务器返回 0 个字节。我想我会在表单字段中更改包含文件和 json 的多部分的服务器方法并测试它是如何进行的 【参考方案1】:当您调试asp.net api应用程序时,它可以毫无问题地运行。这说明api应用没有问题。
但是IIS日志中的status是500,大于500的error一般是服务器造成的,客户端正常。这和上面那个api没问题的结论相矛盾。
由于我不知道您使用的 .net 版本,我不确定是使用 TLS 1.0、1.1 还是 1.2。不同版本的 .net 针对不同的 TLS,最新的 4.6 目前支持 1.2。所以最安全有效的方法就是设置所有的TLS和SSL。
System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12| SecurityProtocolType.Ssl3;
【讨论】:
它是带有 IIS 10 和 .Net 框架 4.7.03062 的 Windows Server 2016。我认为它与 TLS/SSL 无关,同一个 Asp.net web api 服务上的所有其他方法都可以正常工作,问题只涉及附加文件的请求。我尝试在提出请求之前输入System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12| SecurityProtocolType.Ssl3;
,但它并没有解决问题
你检查失败请求跟踪了吗?可能会显示更多关于应用程序的过程。【参考方案2】:
原来问题出在客户端应用程序中我的 Edit 方法中的单行。将fcontent.Headers.Add("Content-Type", "application/octet-stream")
更改为fcontent.Headers.ContentType = new MediaTypeHeaderValue(MimeMapping.GetMimeMapping(fileInfo.Name))
后,它可以完美运行。换句话说,我的请求甚至没有发送到服务器。然而,令人费解的是,同样的代码已经运行了几个月然后停止了......
public async void Edit(string attachmentPath)
using (var client = new HttpClient())
var serializedProduct = JsonConvert.SerializeObject(this, new JsonSerializerSettings DateFormatString = "yyyy-MM-ddTHH:mm:ss.fff" );
string url = Secrets.ApiAddress + $"Edittypeof(T).Name?token=" + Secrets.TenantToken + $"&id=this.Id&UserId=RuntimeSettings.UserId" + $"&typeof(T).NameJson=serializedProduct";
MultipartFormDataContent content = new MultipartFormDataContent();
try
using (var fileStream = System.IO.File.OpenRead(attachmentPath))
var fileInfo = new FileInfo(attachmentPath);
StreamContent fcontent = new StreamContent(fileStream);
fcontent.Headers.ContentType = new MediaTypeHeaderValue(MimeMapping.GetMimeMapping(fileInfo.Name)); //fcontent.Headers.Add("Content-Type", "application/octet-stream");
fcontent.Headers.Add("Content-Disposition", "form-data; name=\"file\"; filename=\"" + fileInfo.Name + "\"");
content.Add(fcontent, "file", fileInfo.Name);
System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12;
var result = await client.PutAsync(url, content);//<--stops here
if (result.IsSuccessStatusCode)
MessageBox.Show("Edycja zakończona powodzeniem!");
else
MessageBox.Show("Serwer zwrócił błąd przy próbie edycji. Wiadomość: " + result.ReasonPhrase);
catch (Exception ex)
MessageBox.Show("Problem z wysyłką żądania do serwera. Wiadomość: " + ex.Message + ". " + ex.InnerException.Message, "Błąd żądania", MessageBoxButtons.OK, MessageBoxIcon.Error);
【讨论】:
以上是关于在 IIS 上发布包含 jpg 和 json 的多部分请求会导致内部服务器错误和 win32 状态 64的主要内容,如果未能解决你的问题,请参考以下文章
Gatsby:在页面上组合两个 graphql 源(.json 和 .jpg 源)