简体   繁体   English

在第一个请求上抛出Web API Rest Service空引用异常

[英]Web api rest service null reference exception thrown on first request

The Setup 设置

We have a .net web api rest service that basically will render a web page as an image and attach that image to an email and send it to an address. 我们有一个.net Web api rest服务,该服务基本上将网页呈现为图像并将该图像附加到电子邮件中并将其发送到地址。 We pass the service a JSON email object that contains the from, to, subject, body, mailServerName, and html to render. 我们向服务传递一个JSON电子邮件对象,该对象包含要呈现的from,to,subject,body,mailServerName和html。 This service will be called aa lot, however, its the first call that gives the problem. 该服务将被称为很多,但是它是第一个带来问题的调用。

The problem 问题

The first .net web api rest service request of the day always throws the following exception: 当天的第一个.net Web api REST服务请求始终会引发以下异常:

Message: "An error has occurred.", ExceptionMessage: "Object Reference not set to an instance of an object", ExceptionType: "System.NullReferenceException", Stacktrace: "at 消息:“发生错误。”,ExceptionMessage:“对象引用未设置为对象的实例”,ExceptionType:“ System.NullReferenceException”,Stacktrace:“ at
EmailService.Controllers.EmailController.Post(Email email) at EmailService.Controllers.EmailController.Post(电子邮件电子邮件)位于
lambda_method(Closure, Object, Object[]) at ... lambda_method(Closure,Object,Object [])在...

Note: 注意:

After adding the custom exceptions below, the exception message is now the following: 在下面添加了自定义例外之后,现在出现以下例外消息:

ExceptionMessage: "Exception happened while saving Image to Jpeg stream" ExceptionMessage:“将图像保存到Jpeg流时发生异常”

Stacktrace: 堆栈跟踪:

at EmailService.Controllers.EmailController.Post(Email email) at 在EmailService.Controllers.EmailController.Post(电子邮件电子邮件)位于
lambda_method(Closure, Object, Object[]) at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ActionExecutor.<>c_DisplayClass10.b_9(Object instance,Object[] methodParameters) at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ExecuteAsync(HttpControllerContext controllerContext,IDictionary`2 arguments, CancellationToken cancellationToken) at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Tasktask) at System.RuntimeCompilerServices.TaskAwaiter.HandleNonSuccessAnsDebuggerNotification(Task task).... System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ActionExecutor。<> c_DisplayClass10.b_9(Object instance,Object [] methodParameters)在System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ExecuteAsync( HttpControllerContext controllerContext,IDictionary`2个参数,System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Tasktask)的System.RuntimeCompilerServices.TaskAwaiter.HandleNonSuccessAnsDebuggerNotification(Task任务)....

which is related to this line of code: 与这行代码有关:

websiteImage.Save(websiteStream, System.Drawing.Imaging.ImageFormat.Jpeg);

The service works fine after the first request in performed. 在执行第一个请求后,该服务运行正常。 Again, its only after a long wait, typically until the next day, do we see the above exception thrown. 同样,只有经过长时间的等待(通常直到第二天),我们才能看到上述异常。

The Question 问题

What is causing the service to throw the NullReferenceException and how can I fix it? 是什么导致服务引发NullReferenceException,我该如何解决?

The C# code for the controller class and the jQuery that performs the actual requst are below. 控制器类的C#代码和执行实际要求的jQuery如下。

Controller class: 控制器类:

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);
        }

    }
}

Service request code: 服务请求代码:

$.ajax ({
        type: "POST",
        url: "/api/email/",
        contentType: "application/json; charset=utf-8",
        data: JSON.stringify(email),
        success: finish,
        error: error

    });

Global.asax 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 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 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 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; }

    }
}

From everything I have been reading it looks like there are some issues using the Single Threaded ApartmentState (STA) in an ASP.NET application. 从我阅读的所有内容来看,在ASP.NET应用程序中使用单线程ApartmentState(STA)似乎存在一些问题。 Take a look at the article here which describes how the author was able to work around these issues. 这里看一下这篇文章它描述了作者如何解决这些问题。

I have not been able to determine why exactly this behavior is only happening for you on the initial request of the day. 我无法确定为什么这种行为仅在当天的最初请求下才为您发生。

Additional resources for this can be found here and here . 可以在此处此处找到其他资源。 I hope this helps! 我希望这有帮助!

Try changing your generate method to (assuming that m_Bitmap has been constructed elsewhere in that class): 尝试将您的generate方法更改为(假设m_Bitmap已在该类的其他地方构造):

public Bitmap Generate()
{
    return m_Bitmap;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM