I'm using Elmah with ASP.NET and wondering how I would add custom data, such as a session variable, to an unhandled exception email.
I've tried several handlers in the Global.asax file but can't seem to find the right one.
For this, I'd think you would need to modify the Elmah source and recompile. It shouldn't be too difficult to achieve. If you have a look in the constructor of the Elmah.Error class, the HttpContext is passed in, from which you should be able to get the info you need, e.g. Session, Form variables etc. You could add custom fields to the Elmah.Error class for this data
I think the Elmah.ErrorMailHtmlFormatter class is where the email is constructed using a HtmlTextWriter, and here you could insert code in the RenderSummary() method to include the custom fields you added to Elmah.Error.
I know it may be a pain to start working with source, but personally I think it's the cleanest way as there currently is no facility for report/email templates, and it's better that bolting on something to change the output after it has been generated.
Andrew's answer helped a lot, thanks. I ended up doing the following:
Added a OnBuilding event to the ErrorMail http module. The event args for this event have a NameValueCollection property.
I handled the OnBuilding event in global.asax.
Since HttpModules don't always have access to sessionstate, esp. if the exception occurs before the session is loaded, I copied the data i wanted reported into the HttpApplication cache(indexed by sessionid).
When an exception occurs I grab the data i want out of the application cache via the sessionid stored in the request(specifically, in the cookie). I generate a NameValueCollection from this data and send it back to the httpmodule via the OnBuilding args.
The data is then rendered to email similarly to how the server variables section is rendered.
Related
Ill try my best to explain this. In my Hub code, i have instantiated the ConnectionMapping class. It was taken from here http://www.asp.net/signalr/overview/guide-to-the-api/mapping-users-to-connections#inmemory .
This ConnectionMapping requires a unique key for its dictionary. I have a webform code-behind called default.aspx.cs. In this code behind, I will get a unique Login Username inside the Page_Load event. How do i pass this username to the Hub to add it to the ConnectionMapping?
I tried.
IHubContext hubContext = GlobalHost.ConnectionManager.GetHubContext<MyHub>();
But i dont know what im doing after doing this. Help.
The short answer is, you don't. Not with the code you have. The issue is this sequence:
Client requests page
Server-side code runs (Page_Load)
Resulting HTML/JS is sent to client
Client connects to SignalR
At this point, you would presumably add to the ConnectionMapping. The issue being of course, that any data/variables in Page_Load are no longer available, and you'd have no way of mapping the connection you just received to that page request even if they were.
Your website needs to hold on to some piece of information, perhaps that username, and pass it to the hub on connection (possibly via the query string). The data could even be stored in the page as part of the Page_Load event (in a hidden div or through some other mechanism).
Now you have both pieces of information at the same time, and can add to the mapping normally.
I am a VB.net winforms programmer attempting to build an ASP.Net app. I use data classes(objects) through reflection in most of my vb projects and was trying to adapt it to ASP.net using the VB code behind. I have a webpage that serves as an add/edit page for contact info. I instatiate my class which grabs the contact data from the data base then I have a process that loops through the controls on the form and matches up with a property in the data class. I can display data no problem. When I edit data and click the submit button my code calls a then loops through the controls on the form again and matches the control to the property of the data class to update the property of the class. However, my data class is no longer valid. I know web programming is different then winforms but I can't seem to get over the hump on this one. Is this the wrong way to go about this? Is my data class only available on the server side? Do I just reinstantiate the initial class and then loop through the propeties and change what the user changed and then call the update method (see redundant)? How can I get data class into a session object (I made an attempt in the past but was under tight deadlines and had to abandon it, maybe I need to revisit it?)?
Thanks
If you decide to keep some of your data in Session, you owe it to yourself to look at this post. Your code will be much cleaner and easier to maintain.
Yes, you need to reload the data class from the database as one option, or use an alternative approach. The reason is web is stateless, so all local variables are destroyed then the server side page unload process occurs. This means that in between requests, you need something to store your data.
You can read/write an object via the Session colleciton, as so:
Session["A"] = myobj;
myobj = (ObjType)Session["A"];
And so session stores an object for a specific user. Alternatively, cache stores application level data, so one instance of an object is available to all users (where session is unique to each user). You can make cache unique to a user by appending a user ID to the cache string.
var o = Cache.Get("A");
if (o != null) { .. }
Cache.Add("A", o, ...);
And so these mechanisms help you temporarily retain data.
You need to save your data class somewhere, usually in a session variable, otherwise it goes away as soon as the page gets sent back the user. Or else you need to recreate the data class again upon posting.
Suppose I have a button in an aspx page that performs a save of the form data to the database. In the associated event handler, before sending the updates, I read something from a webservice, operation that might result in an exception. In case of an error I want to have an adequate message displayed on the page, and all the data in the form preserved. How can I achieve this? Also, all my pages inherit from a basepage, so I would like, if possible to have all the error handling code in the base class. I do not want, if possible, to surround any web service call with try-catch blocks, I would in case of any unhandled exception to call some method automatically, something like Page_error, but still preserve the data in my forms.
You can easily put a method that manages the display message (maybe setting the text of some errorMessageLabel) in a superclass called from any derived class (if you wanna use inheritance to setup a template for your pages) if an exception is thrown (you can put the call to the superclas method in a catch block if there's actually an exception being thrown or you can manage this manually if the webservice is unavailable depending on your programming style).
As far as preserving the data presented, if viewstate is on and you are not populating your page dynamically then you're ok - if not, you need to explicitly save state information in viewState or session entries and retrieve them back if something goes wrong.
This bit really depends on how your page is actually implemented.
I am using methods with the Attribute [WebMethod] in my aspx pages. I don't use any asp.net ajax but jQuery to call these methods and return objects in JSON. This all works fine.
Next I added an authorization check inside the webMethod, if the current user doesn't have access to the feature I need to let the calling JavaScript know.
So I am throwing an AccessViolationException exception which can then be parsed by the OnError callback function in JavaScript. This works too but the exception includes the full StackTrace and I don't want to make this available to the calling client.
What other ways I could use to return an "Access Denied" to the client when the WebMethod returns a business object?
I'm using ASP.Net 3.5SP1 and jQuery 1.32
You can also add a:
customErrors mode="On"/
in your web.config, this will cut away the stack trace and leave you only the exception message
Why propagate errors through the wire? why not use an error response ?
Just wrap your object in a response object wich can contain an error code for status and an error message to present to users.
As suggested by NunFur I changed my approach and rather than throwing an error, I return a 'richer' object.
There are at least two options, the first one would be to encapsulate my business object into a response object with some status properties. I tried this but it makes the JSON more complicated.
So rather than adding a new object I added two properties to my business object, something like ServiceStatus and ServiceMessage. By default these are 200 and '', but can be set by the WebMethod code if anything goes wrong (no access, proper error). In this case they business object will be 'empty' (no data). The JavaScript code then first checks for the ServiceStatus and reacts appropriately.
I add the two fields to all my objects that are returned by WebMethods, even a simple string. They have to implement an Interface with those two properties.
Now I have complete control over that goes over the wire in case something unexpected is happening.
Thanks for the input
I save exceptions for when things go really wrong. (e.g. can't connect to the database)
Either return nothing (null/nill/whatever), or return a false bool value.
Sorry that I don't have a better answer than that...I'll have to keep looking myself.
You could look at SoapException: http://msdn.microsoft.com/en-us/library/system.web.services.protocols.soapexception(VS.71).aspx
I'm just not sure, if it will work when it is called from JavaScript. Espeially if it's called with a get-request.
BTW AccessViolationException is to my best knowlegde ment to be thrown when the application is accessing memory it has no access to.
/Asger
All literature I see on creating custom handlers deals with associating an extension with a handler, e.g. if I wanted a handler for Ajax requests, I could implement the IHttpHandler interface in an AjaxHandler class.
Now, to have individual instances of AjaxHandlers, e.g. DocAjaxHander, PersonAjaxHandler etc. how would I derive the base AJAX handling of my AjaxHandler class without registering each individual *.ajax page?
It sounds like your assuming 1 HttpHandler = 1 Page or 1 Control, but as I understand, 1 HttpHandler can handle all pages of a certain file extension.
Your Question is not very clear, and your reponse to another answerer makes no sense...
"In fact, it seemed, to me, a lot like I was asking Http handlers, using a .ajax handler as an example."
But I shall assume you are thinking "DocAjaxHander" and "PersonAjaxHandler" should each be created for a "DocAjaxControl" and "PersonAjaxControl" respectively. I dont think that would be neccesary, 1 handler should be able to handle all your ajax requests if you choose to do it that way, but it doesn't feel like the most intuitive solution to me (using HttpHandlers), anyway, onto the details...
every IHttpHandler object needs to implement :
public void ProcessRequest(HttpContext context)
which allows :
context.Response.Write("Your JSON Response in here");
but at the level of 'ProcessRequest()', you have no access to the instance of the control which created the ajax call, or to the 'System.Web.UI.Page' object that holds the control, or anything.
context.Request
to the rescue! With the Request object above you can read QueryStrings, Sessions, an you can determine the path of the original HttpRequest (i.e. PersonAjaxObject may make an ajax call to 'myPersonobjPage.ajax' for its JSON data, but the '.ajax' extention lands the request at your custom http handler and it's ProcessRequest method.)
If I was you, and I was going to use an HttpHandler for my ajax calls, I'd use query string data to provide enough info for my handler to know 'what type of object am I responding too' as well as 'what data is that object requesting'.
Hope that helps.
You can automatically handle AJAX request in a number of ways. Here's how to do it with a web service:
http://www.asp.net/AJAX/Documentation/Live/Tutorials/ConsumingWebServicesWithAJAXTutorial.aspx
Well, one way to do it would be via query string params...