System.InvalidOperationException error when attaching files to System.Net.Mail.MailMessage - asp.net

I am getting an odd error when attaching one or more files to a System.Net.Mail.MailMessage object from an ASP.NET page.
I create a List(Of Attachment) and add new Attachment objects for each attached file requested by the user. These are files that reside on the web server's file system. For example, the code looks similar to the below, but rather than having hard-coded file paths it's getting them from the database. But while debugging I see that the file paths are valid, pointing to existing files that I can view from Explorer given the full file path or from the website using the virtual address (~/Documents/resume.pdf, for example).
Dim attachments As New List(Of Attachment)
attachments.Add(New Attachment("C:\Websites\Documents\resume.pdf"))
attachments.Add(New Attachment("C:\Websites\Documents\map.png"))
...
After constructing my attachments collection I send the email, adding each Attachment object to the Attachments collection like so:
Dim message As MailMessage = New MailMessage(from, toAddress, subject, body)
If attachments IsNot Nothing Then
For Each att As Attachment In attachments
message.Attachments.Add(att)
Next
End If
Dim mailClient As SmtpClient = New SmtpClient
mailClient.Send(message)
However, when I run the code I get the following error:
System.InvalidOperationException: One of the streams has already been used and can't be reset to the origin.
System.Web.HttpUnhandledException: Exception of type 'System.Web.HttpUnhandledException' was thrown. ---> System.Net.Mail.SmtpException: Failure sending mail. ---> System.InvalidOperationException: One of the streams has already been used and can't be reset to the origin.
at System.Net.Mime.MimePart.ResetStream()
at System.Net.Mail.Attachment.PrepareForSending()
at System.Net.Mail.MailMessage.SetContent()
at System.Net.Mail.MailMessage.Send(BaseWriter writer, Boolean sendEnvelope)
at System.Net.Mail.SmtpClient.Send(MailMessage message)
--- End of inner exception stack trace ---
...
I've tried replacing my logic that adds attachments based on a database query to one that adds a single file with a hard-coded file path. I've tried using different SMTP clients (my web host provider's SMTP server and GMail). I get the same error regardless.
Thanks

Found the answer... the problem was because I was trying to send two separate emails using the same attachments collection.
The email sending logic was in a function that was called like so:
SendEmail(from, to, subject, body, attachments)
If SomethingOrOther Then
SendEmail(from, someoneElse, subject, body, attachments)
End If
My (cheesy) workaround was to just create two attachments lists, one for the first call to SendEmail, another one for the second call.

I think better solution is to set your message object to nothing and your client option to nothing as well, before looping back around for the second message. Good luck!

Related

How to retrieve information from JIRA using webhooks in an ASP.Net MVC application?

I am using an MVC application that will send an email to JIRA with the information required to create an Issue in JIRA.
This is all works successfully but the next step is to retrieve the information in a page. Currently I am displaying the information that was sent by retrieving it from the database.
The problem is that I also need to retrieve the KEY and the status of the issue. This cannot be just entered as the user will not know what they are, it has to be done in JIRA.
Originally I was going to use an API to get the information from JIRA but because the JIRA site is not hosted online the API does not meet the Access-Control-Allow-Headers" Header.
I was told that I would have to use webhooks to get the information but I am unsure about how to go about this.
I know that I have to first register the webhook which I am doing Via the JIRA Administration UI. What I what to know is how can I retrieve that information in my application using webhooks, I know the webhooks must have a friendly name for the webhook created, the URL where the callback should be sent.
The scope of the webhook and the events to post to the URL, either "all events" or a specific set of events.
I also know that the Post function web hooks will not fire if added to the Create Issue workflow transition. We recommend that you configure your web hook to fire from the issue_created event instead.
So how can I successfully retrieve this information, I am currently trying this:
Public Function Webhook() As ActionResult
Dim status As String = "Status"
If Request("secret") <> status Then
Response.StatusCode = 403
Return Content("Invalid status secret")
End If
If Request("event") = "incoming_messages" Then
Dim Key As String = Request("Key")
Dim jiraStatus As String = Request("status")
Dim reply As Dictionary(Of String, Object) = New Dictionary(Of String, Object)()
reply("content") = "Thanks for your submission!"
Dim result As Dictionary(Of String, Object) = New Dictionary(Of String, Object)()
result("messages") = New Object() {reply}
Return Json(result)
Else
Response.StatusCode = 400
Return Content("Unknown event")
End If
Return View()
End Function
But I am pretty sure I am doing it wrong, what steps do I need to follow to do this correctly?
Update
Where should my Webhook URL fire to currently I am sending it to RequestB.in for testing which is working but where should I fire it to get the information in my MVC application? Should it fire to the MVC application, if so where should it fire to.
How can I process the Json payload currently in my MVC application, I am trying to deserialize it but I have not done this before and am unsure how to connect the Json Payload with the application. This is what I have tried so far:
Request.InputStream.Position = 0
Request.InputStream.Seek(0, SeekOrigin.Begin)
Using reader = New StreamReader(Request.InputStream)
Dim jiraJson = reader.ReadToEnd()
Dim contentType As String = Request.ContentType
Dim body = JsonConvert.DeserializeObject(jiraJson)
Try
Select Case DirectCast(body.key, String)
Case ""
Return Json(jiraJson)
Case Else
Return Json(jiraJson)
End Select
Catch ex As Exception
End Try
End Using
Return View()
I am unsure what to place in switch statement and also how to return the json and then display it, how can I do this?
If I'm understanding you correctly, a high level process of how this should work would be :
An email is sent to Jira.
An issue is created under the given project, which triggers the webhook.
The webhook will POST a JSON payload to the URL you've specified.
The URL should be a public route within your MVC application that consumes and processes the request.
So given where you're already at, all you need to consume the webhook is a publicly accessible MVC route. Let's say you have a controller named "JiraUpdateController", and on that controller exists a method called "ProcessWebhook" :
public class JiraUpdateController : Controller
{
/// <summary>
/// Set our default base logger for the update tasks
/// </summary>
private static readonly Logger logger = LogManager.GetCurrentClassLogger();
public static Logger Log
{
get { return logger; }
}
[HttpPost]
public ActionResult ProcessWebhook()
{
// Process the JSON payload here accordingly.
}
}
Now, you need to set this as the URL that the webhook should post to. Ultimately it should look something like :
http://internal.company.com/JiraUpdate/ProcessWebhook
I've done something very similar to this, so here's a couple things to keep in mind.
Make sure you're setting the webhook to a minimal scope. If this is something you want to trigger across all projects whenever an issue is created in any of them, you're going to have quite a few requests going to the ProcessWebhook endpoint. Try to limit the projects to just the ones you need, and the event to only the "issue created" event.
If you don't already have a public endpoint for testing, you should definitely check out RequestBin. What this will allow you to do, is setup a temporary URL for the webhook, so you can see exactly what the JSON payload from JIRA will look like. From there, build your business logic in the ProcessWebhook method, and you should be good to go. To use RequestBin, just go to the site, generate a URL, and set that as the Webhook URL. Create an issue in JIRA under the project you're working with, and you should see a large JSON payload go to that RequestBin URL - this will give you a feel for exactly what the JSON looks like and how to serialize it into a workable construct.

server error when reading in an xml file

I have some xml files which contain the source data for populating dropdown lists in my pages. These dropdowns are re-populated based on selected items from other dropdwons. I am getting a server error
Could not find a part of the path
X:\ASP.Net\CommodDisplay\DataFiles\dataXML.xml'.
Even though this is a valid path.
Here is the code im using to call it;
Dim doc As New XmlDocument()
'Load XML from the file into XmlDocument object
doc.Load(HttpContext.Current.Server.MapPath("~/DataFiles/dataXML.xml"))
Dim root As XmlNode = doc.DocumentElement
'Select all nodes with the tag Book
Dim nodeList As XmlNodeList = root.SelectNodes("futures")
For Each node As XmlNode In nodeList
ddlMainsub1.Items.Add(node.SelectSingleNode("product").InnerText)
Next
I also tried it just using Server.MapPath and I get the same error. These files are in a networked drive. Does that matter?
You asp.net service is running under a different user account then your own. You should be sure the service account has access to the path:
X:\ASP.Net\CommodDisplay\DataFiles\dataXML.xml
If part of the path is a network mapping it is possible that it is mapped for you but not for the service account running your website. Put all files local and try if that helps.

Mail Attachment in ASP.net

Am trying to add an attachment to mail in asp.net VB.
I could send mail fine until I added the attachment code,
Dim attch As Attachment = New Attachment("http://sitehere.com/Documents/file.jpg")
mail.Attachments.Add(attch)
I am getting the error URI formats are not supported.
Any ideas why that is and what I can do about it?
The Attachment class expects either a path to a file on the file system, or a Stream.
Try:
Dim data As Byte() = New WebClient().DownloadData("http://sitehere.com/Documents/file.jpg")
Dim attachment As New Attachment(New MemoryStream(data), "file.jpg")
That's me doing my best to translate from C# to VB.NET so the syntax might not be 100% correct, but that's the general idea. That will download the data into a byte array, then create a memory stream from those bytes and pass that to the Attachment constructor.
You can't add an attachment straight from a URL. You'll need to download the file first, then add it as an attachment.
you can use HttpWebRequest to get the file as a stream, then attach the stream. That saves having to store the file on disk.
If you have the file locally in your server and in a folder which you know the path, dont use the uri for that,
Dim eMessage As New MailMessage
Dim attachLabel As Attachment
Dim location As String
loction= Server.MapPath("Documents\\file.jpg")
attachLabel = New Attachment(loction)
eMessage .Attachments.Add(attachLabel);
If you really want to send a file from another url, you may use HttpWebRequest to download that first and send it as Colin and Davy8 metnioned.

How to capture IIS 500 server error detail when received in a WebException?

I have code that uses an HttpWebRequest to request a .ASP page (asp 3.0), with some parameters in the URL. That ASP page will generate a PDF by getting some data from a database. Due to error in the ASP code, I'm getting a WebException with IIS's 500 server error when I try to get the response from the request. And that's fine, but the exception's message doesn't really say anything other than 500 error occurred.
If I copy paste the URL that I'm requesting in IE, I do get some details since I have friendly errors disable and IIS configured to send the real error text to the user.
"Technical Information (for support personnel)
Error Type:
ADODB.Recordset (0x800A0CC1)
Item cannot be found in the collection corresponding to the requested name or ordinal.
"
I've done a QuickWatch on the Exception and visited all of the properties, but not one contained this data.
Is there a way to get this information on the WebException?
Thanks.
Is there a way to get this information on the WebException?
Yes, you can read the response body:
try
{
...
}
catch (WebException ex)
{
using (var stream = ex.Response.GetResponseStream())
using (var reader = new StreamReader(stream))
{
string text = reader.ReadToEnd();
// text will contain the response from the server
}
}

Error code or Exception - which is the best practice for an ASP.Net web service?

I've read this thread for WCF has inbuilt Custom Fault codes and stuff.
But what is the best practice for ASP.Net web services? Do I throw exceptions and let the client handle the exception or send an Error code (success, failure etc) that the client would rely upon to do its processing.
Update: Just to discuss further in case of SOAP, let's say the client makes a web svc call which is supposed to be a notification message (no return value expected), so everything goes smooth and no exceptions are thrown by the svc.
Now how will the client know if the notification call has gotten lost due to a communication/network problem or something in between the server and the client? compare this with not having any exception thrown. Client might assume it's a success. But it's not. The call got lost somewhere.
Does send a 'success' error code ensures to the client that the call went smooth? is there any other way to achieve this or is the scenario above even possible?
Jeff Atwood posted an interesting aerticle about this subject some time ago. Allthough a .NET exception is converted to a SoapFault, which is compatible with most other toolkits, the information in the faults isn't very good. Therefor, the conlusion of the article is that .NET webservices don't throw very good exception messages and you should add additional information:
Private Sub WebServiceExceptionHandler(ByVal ex As Exception)
Dim ueh As New AspUnhandledExceptionHandler
ueh.HandleException(ex)
'-- Build the detail element of the SOAP fault.
Dim doc As New System.Xml.XmlDocument
Dim node As System.Xml.XmlNode = doc.CreateNode(XmlNodeType.Element, _
SoapException.DetailElementName.Name, _
SoapException.DetailElementName.Namespace)
'-- append our error detail string to the SOAP detail element
Dim details As System.Xml.XmlNode = doc.CreateNode(XmlNodeType.Element, _
"ExceptionInfo", _
SoapException.DetailElementName.Namespace)
details.InnerText = ueh.ExceptionToString(ex)
node.AppendChild(details)
'-- re-throw the exception so we can package additional info
Throw New SoapException("Unhandled Exception: " & ex.Message, _
SoapException.ClientFaultCode, _
Context.Request.Url.ToString, node)
End Sub
More info why soapfaults are better in this question.
Depends on how you are going to consume the web service - i.e. which protocol are you going to use.
If it is GET or POST, better return error code, as the calling HttpWebRequest (.Net) or other code will receive server error, and have to deal with it to extract the exception code.
If it is SOAP - then it is perfectly ok to throw custom exceptions (you do not want to return internal framework exceptions, as they may reveal some stack trace, etc. to external parties).
As the SOAP web services are exactly meant to look to the calling code as a normal method call, the corresponding calling framework should be able to handle and propagate the exception just fine, thus making the calling code look and behave as it deals with internal calls.

Resources