【问题标题】:Web api rest service null reference exception thrown on first request第一次请求时抛出 Web api rest 服务空引用异常
【发布时间】: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


【解决方案1】:

从我阅读的所有内容来看,在 ASP.NET 应用程序中使用单线程 ApartmentState (STA) 似乎存在一些问题。查看文章here,它描述了作者如何解决这些问题。

我无法确定为什么只有在当天的初始请求时才会发生这种行为。

可以在herehere 找到其他资源。我希望这会有所帮助!

尝试将您的 generate 方法更改为(假设 m_Bitmap 已在该类的其他地方构建):

public Bitmap Generate()
{
    return m_Bitmap;
}

【讨论】:

  • 它似乎与Generate() 方法无关,我在调用周围添加了try/catch,它没有抛出异常?另外,为什么它只会在第一个请求时失败?你会认为每个请求都会发生这个问题,如果它与线程有关,不是吗?
  • 抛出异常的行是websiteImage.Save(websiteStream, System.Drawing.Imaging.ImageFormat.Jpeg);,它在EmailController.cs
  • @Zengineer,这很奇怪。你能提供捕获的异常的堆栈跟踪吗?
  • 检查我对 StackTrace 的编辑。我相信它与原始异常的堆栈跟踪相同。
  • 如果我从Bitmap Generate() 中删除线程,服务会在每个请求的同一行代码中抛出相同的异常。基本上,没有线程 m_Bitmap 始终为空。
猜你喜欢
  • 1970-01-01
  • 2018-01-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-10-12
  • 2012-03-06
相关资源
最近更新 更多