c# - 发布包含 jpg 和 json 的多部分请求会导致内部服务器错误和 IIS 上的 win32 状态 64
问题描述
我的 winforms 应用程序将 PUT/POST 请求发送到 asp.net web api 服务。大多数情况下,它将 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 + $"Edit{typeof(T).Name}?token=" + Secrets.TenantToken + $"&id={this.Id}&UserId={RuntimeSettings.UserId}" + $"&{typeof(T).Name}Json={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.Path2Files}{item.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 应用程序时,请求运行没有任何问题。我还能做些什么来跟踪这个问题?
解决方案
事实证明,问题出在客户端应用程序中我的 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 + $"Edit{typeof(T).Name}?token=" + Secrets.TenantToken + $"&id={this.Id}&UserId={RuntimeSettings.UserId}" + $"&{typeof(T).Name}Json={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);
}
}
}