【发布时间】:2014-05-09 13:51:41
【问题描述】:
设置
我们有一个 .net web api rest 服务,它基本上会将网页呈现为图像并将该图像附加到电子邮件中并将其发送到一个地址。我们向服务传递一个 JSON 电子邮件对象,其中包含要呈现的 from、to、subject、body、mailServerName 和 html。该服务会被多次调用,但是,它是第一次调用会出现问题。
问题
当天第一个.net web api rest服务请求总是抛出以下异常:
消息:“发生错误。”,ExceptionMessage:“对象 引用未设置为对象的实例”,异常类型: “System.NullReferenceException”,堆栈跟踪:“在
EmailService.Controllers.EmailController.Post(电子邮件) at
lambda_method(Closure, Object, Object[]) at ...
注意:
在下面添加自定义异常后,现在的异常消息如下:
ExceptionMessage:“将图像保存到 Jpeg 流时发生异常”
堆栈跟踪:
在 EmailService.Controllers.EmailController.Post(电子邮件电子邮件)在
lambda_method(闭包,对象,对象[])在 System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ActionExecutor.c_DisplayClass10.b_9(对象 实例,对象 [] 方法参数)在 System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ExecuteAsync(HttpControllerContext controllerContext,IDictionary`2 个参数,CancellationToken 取消令牌)在 System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(任务任务) 在 System.RuntimeCompilerServices.TaskAwaiter.HandleNonSuccessAnsDebuggerNotification(任务 任务)....
与这行代码有关:
websiteImage.Save(websiteStream, System.Drawing.Imaging.ImageFormat.Jpeg);
在执行第一个请求后,该服务工作正常。同样,只有在漫长的等待之后,通常要等到第二天,我们才会看到上面抛出的异常。
问题
是什么导致服务抛出 NullReferenceException,我该如何解决?
控制器类的C#代码和执行实际请求的jQuery如下。
控制器类:
namespace EmailService.Controllers
{
public class EmailController : ApiController
{
// POST api/email
public HttpResponseMessage Post(Email email)
{
if (email == null)
{
throw new Exception("Email parameter is null");
}
Bitmap websiteImage;
try
{
websiteImage = WebsiteToImage.Generate();
}
catch (Exception)
{
// 500 Internal Server Error
response.StatusCode = HttpStatusCode.InternalServerError;
throw new Exception("Exception happened while generating Image");
}
// create memory stream from bitmap and save it as a jpeg, this allows us to attach the image from memory, without having to store it on the server
System.IO.Stream websiteStream = new System.IO.MemoryStream();
try
{
websiteImage.Save(websiteStream, System.Drawing.Imaging.ImageFormat.Jpeg);
}
catch (Exception)
{
// 500 Internal Server Error
response.StatusCode = HttpStatusCode.InternalServerError;
throw new Exception("Exception happened while saving Image to Jpeg stream");
}
try
{
websiteStream.Position = 0;
}
catch (Exception)
{
// 500 Internal Server Error
response.StatusCode = HttpStatusCode.InternalServerError;
throw new Exception("Exception happened while setting stream posiiton=0");
}
// create response with status code 200 OK, since we aren't actually creating anything
var response = this.Request.CreateResponse(HttpStatusCode.OK);
try
{
// MailMessage is used to represent the e-mail being sent
using (MailMessage message = new MailMessage(email.from, email.to, email.subject, email.body))
{
// attach jpeg from memory
message.Attachments.Add(new Attachment(websiteStream, "letter.jpg", "image/jpeg"));
// create mail client
SmtpClient mailClient = new SmtpClient(email.mailServerName);
// use the Windows credentials of the account (i.e. user account)
mailClient.UseDefaultCredentials = true;
// send the message
mailClient.Send(message);
}
}
catch (Exception)
{
// 500 Internal Server Error
response.StatusCode = HttpStatusCode.InternalServerError;
throw new Exception("Exception happened while creating and sending mail message");
}
//return new HttpResponseMessage() { Content = new StringContent(html) };
return response;
}
// PUT api/email/5
public HttpResponseMessage Put(int id, [FromBody]string value)
{
// return response status code of 501 Not Implemented
return this.Request.CreateResponse(HttpStatusCode.NotImplemented);
}
}
}
服务请求代码:
$.ajax ({
type: "POST",
url: "/api/email/",
contentType: "application/json; charset=utf-8",
data: JSON.stringify(email),
success: finish,
error: error
});
Global.asax
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
// add details to server errors
GlobalConfiguration.Configuration.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always;
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
}
}
WebApiConfig.cs
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
WebsiteToImage.cs
public class WebsiteToImage
{
private Bitmap m_Bitmap;
private string m_Url;
private string m_FileName = string.Empty;
private string m_html;
public WebsiteToImage(string html)
{
// Without file
//m_Url = url;
m_html = html;
}
public WebsiteToImage(string url, string fileName)
{
// With file
m_Url = url;
m_FileName = fileName;
}
public Bitmap Generate()
{
// Thread
var m_thread = new Thread(_Generate);
m_thread.SetApartmentState(ApartmentState.STA);
m_thread.Start();
m_thread.Join();
return m_Bitmap;
}
private void _Generate()
{
WebBrowser browser = new WebBrowser { ScrollBarsEnabled = false };
//browser.Navigate(m_Url);
browser.DocumentText = m_html;
browser.DocumentCompleted += WebBrowser_DocumentCompleted;
while (browser.ReadyState != WebBrowserReadyState.Complete)
{
Application.DoEvents();
}
browser.Dispose();
}
private void WebBrowser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
// Capture
var browser = (WebBrowser)sender;
browser.ClientSize = new Size(browser.Document.Body.ScrollRectangle.Width, browser.Document.Body.ScrollRectangle.Bottom);
browser.ScrollBarsEnabled = false;
m_Bitmap = new Bitmap(browser.Document.Body.ScrollRectangle.Width, browser.Document.Body.ScrollRectangle.Bottom);
browser.BringToFront();
browser.DrawToBitmap(m_Bitmap, browser.Bounds);
// Save as file?
if (m_FileName.Length > 0)
{
// Save
m_Bitmap.SaveJPG100(m_FileName);
}
}
}
public static class BitmapExtensions
{
public static void SaveJPG100(this Bitmap bmp, string filename)
{
var encoderParameters = new EncoderParameters(1);
encoderParameters.Param[0] = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, 100L);
bmp.Save(filename, GetEncoder(ImageFormat.Jpeg), encoderParameters);
}
public static void SaveJPG100(this Bitmap bmp, Stream stream)
{
var encoderParameters = new EncoderParameters(1);
encoderParameters.Param[0] = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, 100L);
bmp.Save(stream, GetEncoder(ImageFormat.Jpeg), encoderParameters);
}
public static ImageCodecInfo GetEncoder(ImageFormat format)
{
var codecs = ImageCodecInfo.GetImageDecoders();
foreach (var codec in codecs)
{
if (codec.FormatID == format.Guid)
{
return codec;
}
}
// Return
return null;
}
}
Email.cs
namespace EmailService.Models
{
public class Email
{
// email properties
public string mailServerName {get; set;}
public string from {get; set;}
public string to {get; set;}
public string subject {get; set;}
public string body {get; set;}
public string content { get; set; }
}
}
【问题讨论】:
-
你知道 nullref 在哪一行被抛出吗?您可以在两行之间添加一些日志记录,以查看哪一行是罪魁祸首。它将提供更多背景信息以了解正在发生的事情......
-
不知道是哪一行。该异常似乎导致其在 Post 方法调用中。我已经在整个代码中进行了检查,但它们从未被击中。几乎就像在执行进入 Post 方法之前抛出异常一样。
-
@Zengineer Exception 指出它在
Post内部,所以执行确实进入了。只是不清楚哪一行。您目前拥有哪些日志记录基础设施?您可以在行之间添加一些日志,以便稍后跟踪哪些消息被记录,哪些没有(从而显示有问题的行)?如果不是,另一种方法是你可以放一堆try/catch-es 并将异常包装在新的异常中,注意它是哪个。另一件事,Email类的后定义 - 我敢打赌那里的某些字段你期望非空可能会出现空? -
好的,我将添加更多
try/catch块,看看我是否可以追踪它。WebsiteToImage.Generate()看起来要创建一个线程,这可能是一个原因。我将发布电子邮件课程。另一个问题是无需等待一整天重新测试即可重新创建问题。
标签: c# rest asp.net-web-api