Why no developer exception page in asp.net 5 razor pages? - c#

I have a simple asp.net 5 razor pages app which does not show developer exception page but shows this in the browser developer tools
The character encoding of the plain text document was not declared. The document will render with garbled text in some browser configurations if the document contains characters from outside the US-ASCII range. The character encoding of the file needs to be declared in the transfer protocol or file needs to use a byte order mark as an encoding signature.
After many iterations and debugging it turns out there was a simple typo in the sql query and instead of showing the developer error page, it was showing blank with the aforementioned error in the browser console !
Questions -
Is this normal/expected ?
any way to turn on "more" debugging to identify such errors rather than trial and error ?
environment -
Visual studio 2019, .net 5
db access using dapper v2.0.78
configure excerpts below !
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILogger<Startup> logg)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseMigrationsEndPoint();
}
else
{
app.UseDeveloperExceptionPage();
//app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
...
there are no try/catch handlers, the code is rather basic
in the razor page
public IEnumerable<gEmployee> ListRows { get; private set; }
DAL mDAL;
public string Message;
public void OnGet(int dID)
{
ListRows = mDAL.GetEmployees(dID);
Message = $"Got {ListRows.Count()} Rows";
}
this is how i figured out the error when the OnGet() would get called but 2nd line with Message = ListRows.Count would not get executed !!
in GetEmployees
public List<gEmployee> GetEmployees(int dID)
{
using (var conn = new SqlConnection(cx.DefaultConnection))
{
var sql = #"SELECT * from gEmployee ";
if (dID > 0)
sql += " WHERE dID = #dID ";
var ListRows = conn.Query<gEmployee>(sql, new { dID = dID}).ToList();
return ListRows;
}
}

Usually in these cases, the first approach should be trying to reproduce the behaviour on a small/clean project so you can rule out various scenarios.
As you saw, in your case it's the
services.AddDatabaseDeveloperPageExceptionFilter()
that is causing the problem.
As Microsoft says in the documentation
This should only be enabled in the Development environment.

The developer error page will display in the event of an exception being raised in your server-side code. Based on the console message, it is likely that your SQL typo did not cause an exception to be raised. Or, if it did, you might be hiding it in a try - catch block. You haven't actually shown the relevant code so this is pure speculation. Either way, it resulted in your view page trying to render something that the browser did not understand, so the browser let you know in the console.
If you are writing SQL, it is always useful to test it in SQL Server Management Studio prior to handing it over to your code to execute.

Is this normal/expected
You're working with an advanced framework (.net) for the web development, so, debugging is not as simple as other platforms (like PHP frameworks) unless you know enough about the the project dependencies and underlying details/behaviors. In this way, you may sometimes see none relevant or ambiguous errors. In my opinion, working with high level frameworks requires a greater level of knowledge and experience. In your case, primarily double check methods and attributes to be used deliberately as they can disable/disturb the built-in Exception Handler behavior where you said instead of showing the developer error page, it was showing blank with the aforementioned error in the browser console
any way to turn on "more" debugging to identify such errors rather
than trial and error ?
You can write a HandleError method containing one of the approaches available at Handle errors in ASP.NET Core to consume where you need it like this :
private void HandleError()
{
...
var exceptionHandlerPathFeature = HttpContext.Features.Get<IExceptionHandlerPathFeature>();
....
}
public void OnGet(string code)
{
//do stuff
HandleError();
//return
}
Please read the entire of MS Doc page in order to find out which one well fits in your case to unravel the main problem. After clearing the issue, you no more need to use above technique.

Related

How do I Display a Message across projects?

We have one Visual Studio 2019 ASP.NET solution with 22 projects, one of which is called BusinessLogic and one which is called Web.
In BusinessLogic, we have code to log errors, and it used by all projects in the solution.
public static Guid? StartDebugLog(String module, String method, String message)
{
if (ApplicationSettings.IsDebugLogActive)
{
using (BPIContext context = SessionManager.CreateNewBPIContext)
{
DebugLog debugLogObject = new DebugLog();
debugLogObject.ID = Guid.NewGuid();
debugLogObject.Module = module;
debugLogObject.Method = method;
debugLogObject.StartTime = DateTime.Now;
if (message == null)
debugLogObject.Message = "(NULL)";
else
debugLogObject.Message = message;
context.AddToDebugLog(debugLogObject);
context.SaveChanges();
return debugLogObject.ID;
}
}
return null;
}
The problem is that this routine is hiding important errors from our customers. Yes, they can go into the application logs, but simply starting the application creates over 100 entries in the application log. Finding errors would be a daunting task to ask of our customers.
I need a way for this routine to be able to display a basic message so that customers do not continue on, thinking everything is OK. Something like alert("An error has occurred."), but the *BusinessLogic project does not have access to the Web project.
Is there a recommended way to create a CALLBACK or something? Most of my background is in Windows Forms where this would be simple. Something like this:
...
context.AddToDebugLog(debugLogObject);
context.SaveChanges();
if (WebCallback != null)
{
WebCallback(debugLogObject);
}
return debugLogObject.ID;
...
I'm not even sure if this is possible in a Web environment. If it is not possible, that's an answer too.
For a "Part 2", some of our newer modules are displayed by dropping them into an iFrame. Is there a way to create something that will allow information from the iFrame to pass to the parent? Currently, all messages that are sent in the iFrame appear to get lost.

Json Web API binding to Model Sometimes Throws Exception

I have a WebAPI (not sure what version but it was created in VS2017) that receives a Json string and binds it to a model in the controller. Most of the time this works just fine. Occasionally it will throw an exception the first time I try to access the class instance. It is clear the binding routines are failing but the exception message is no help: Object reference not set to an instance of an object. I am guessing that the binder is failing and not even creating an empty instance of my object.
I log the Json string before I call the API so I can review the string for issues. I have identified that certain unicode symbols (such as the Trademark TM) will cause a failure, so I am managing those.
But I have a couple of recent Json strings that are throwing the exception and I cannot figure out why. There are no unicode symbols that I can locate. Now my users are asking why this certain style of job is failing.
It seems that once the InputStream is read by the inner MVC binding routines, it can never be read again. The trick of:
HttpContext.Current.Request.InputStream.Position = 0;
string streamresult = new System.IO.StreamReader(HttpContext.Current.Request.GetBufferedInputStream()).ReadToEnd();
throws it own exception, saying call was made before "the internal storage was filled by the caller of HttpRequest.GetBufferedInputStream".
Is there a way to peek inside the binder and see what it is choking
on?
or Is there a way to get a better exception message?
or to capture and log the incoming data stream?
If you want to read the Request Body, you will need to enable rewind on its stream, otherwise the request body can be read only once since by default it's a forward-only stream.
If you are using ASP.NET Core MVC, you could enable the rewind on your startup code with:
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.Use(async (context, next) => { // <----
context.Request.EnableRewind();
await next();
});
app.UseMvc();
}
For your last point I can recommend the Audit.NET library with its Audit.WebApi extension.

Server.Transfer causing Session exception

In my global I have the following code to handle when an error occurs
//[..] code goes here
Server.Transfer("~/Error.aspx?ErrorID=" + errorId);
It used to be a Response.Redirect which worked perfectly except that it changed the url (which is why I want to use Server.Transfer)
Unfortunately, now when it tries to load the Error page, it crashes on the Masterpage when it tries to refer to the Session
HttpException:
Session state can only be used when enableSessionState is set to true,
either in a configuration file or in the Page directive. Please also
make sure that System.Web.SessionStateModule or a custom session state
module is included in the \\
section in the application configuration.
I do have enableSessionState in both my config and my page.
I also found some links which suggest using Context.RewritePath - that just causes a blank page to load for me.
Using Response.Redirect works perfectly and as expected, so I assume Server.Transfer is the issue here. What is it?
EDIT Code:
protected void Application_Error(object sender, EventArgs e)
{
lock (_lockMe)
{
Exception ex = Server.GetLastError();
if (ex != null)
{
if (ex.InnerException != null)
ex = ex.InnerException;
ErrorLoggingManager.AddError(ex, new MembershipData(), ...); //etc
}
Server.ClearError();
//Some other database code for cleaning up some stuff when an error happens
}
try
{
if (Response != null)
{
//Get the last error logged
MyDataContext db = new MyDataContext();
int errorId = db.LoggedErrors.OrderByDescending(le => le.ErrorId).Select(le => le.ErrorId).FirstOrDefault();
Server.Transfer("~/Error.aspx?ErrorID=" + errorId);
}
}
catch (Exception)
{
}
}
As you have not posted much code. So without seeing the actual implementation you have done. I could suggest you below points.
Point 1. First of all, you need to check if SessionState is enabled for pages. You could set them globally in web.config file. Try the snippet given below in web.config
<configuration>
<system.web>
<pages enableSessionState="true" />
</system.web>
</configuration>
Point 2. And put your Redirection in Application_Error in Global.asax.
public void Application_Error(object sender, EventArgs e)
{
HttpApplication app = (HttpApplication)sender;
app.Server.Transfer("~/Error.aspx?ErrorID=" + errorId,true);
}
Point 3. Also check if your SessionStateis set properly in IIS too.
Details are on MSDN to enable sessionstate
Hope this helps..!!!
From what I understand, Server.Transfer sends the content of another page to the client rather than the requested content. If that is the case, then I am wondering if it does not have something to do with applying a master page to the error page? I had a similar error years ago with earlier technology and it turned out that the master page did not like what I was trying to do.
I hope this helps at least point to a solution.
Here's what the problem is:
If there is a page render exception (ex. "File Not Found") then Server.Transfer screws up the session. This has something to do with it being called during the page render.
As long as you are not appending headers before the error occurs, Response.Redirect will work just fine; if you are, however, using Response.AppendHeader then Response.Redirect will not work during a page render.
Try using HttpContext.Current.RewritePath instead. That should fix all these problems. For whatever reason, RewritePath() does not care that the page hasn't finished rendering.
why not just use customErrors in web.config to do the redirect?
<customErrors mode="Off" defaultRedirect="~/Common/Error.aspx">
<error statusCode="403" redirect="~/SM_AccessDenied.aspx" />
<error statusCode="404" redirect="~/Common/FileNotFound.aspx" />
</customErrors>
I had the same problem in a different context a while ago. I don't know if it is your case, but if you're using IIS7 on Windows 2008, in addition to setting enableSessionState=true in your web.config, you have to put your modules inside the <system.webServer> section, instead of <system.web>. Changing this little thing solved it for me.
Why don't you try like this:
The Server.Transfer method also has a second parameter—"preserveForm". If you set this to True, using a statement such as Server.Transfer("WebForm2.aspx", True), the existing query string and any form variables will still be available to the page you are transferring to.
So I think doing like this your session will not expire.
Server.Transfer("~/Error.aspx?ErrorID=" + errorId,True);
The error you are encountering is because you are using a query string parameter. Per the msdn docs
However, the path parameter must not contain a query string, or ASP returns an error.
http://msdn.microsoft.com/en-us/library/ms525800%28v=vs.90%29.aspx
Its about 3/4 of the way down the page just above Requirements.
Even though the docs here are mentioning asp. and not asp.net, keep in mind the session state is a feature of IIS and is handled before asp.net is ever called.
#user2110845 : I had faced similar problem few months ago. the problem was with having an underscore in the website name. We were deploying a website in IIS with two different host names(adding two entries through the 'Edit Bindings' option on the website). The host names provided were abc_ts, abc_is. When the underscore was removed then the session problem got resolved.
It seems there are certain characters not allowed in a website host name. Check if that is your problem.
I found the answer here : link (check 'update 2' in the article)
You don't mention what version of ASP.NET you are using, but there were some changes between 2.0 and 3.5 in how unhandled exceptions bubbled their way up through an ASP.NET web app and then IIS.
Among some other possibles, while you are clearing the error you are not setting Context.Response.TrySkipIisCustomErrors = true; While this particular flag could have nothing to do with your issue (and is only available for 3.5+), it also could help deal with what is potentially two error pages behind the scenes that are obscuring the real issue. Regardless, it'll save you a lot of grief (at least if you are running 3.5+) with other potential issues. Check out two posts I wrote several years back that may be helpful: while they don't cover session handling, they do cover the multiple song-and-dance routines I had to follow to get proper 500 and 404 handling in various versions of ASP.NET. It's possible you will run into something that will get you further ahead, if not all the way there.
http://www.andornot.com/blog/post/Errors-Sending-the-Right-Message-(Redux-Covering-ASPNET-3540).aspx
http://www.andornot.com/blog/post/Errors-Sending-the-Right-Message.aspx

Sitecore 500 Error Logging / Alerts

Problem
Currently, I'm looking to serve a custom 500 error page as well as log and alert a web master about the condition that was hit (What URL, stack trace, timestamp, etc.).
I tried defined custom http errors under system configuration, but the error pages were not hit.
I am able to handle 404s and their related errors (layoutnotfound).
Question
Should I intercept the context in global.asax to handle the 500 errors and return a custom page? Is there another way with Sitecore to achieve what I'm looking for?
Mainly, I'm looking for best practice of logging / alerts for 500 errors with Sitecore
Try using ELMAH.
Here is an article on using it with Sitecore.
Elmah is great and does a good job. You can also use log4net to log exceptions. All you do is add an application_error method in global.asax and you can track all the errors that occur. You can also add different appenders and can log messages in a log file, database and email them.
Here is the code that logs the error and includes some additional information like url and Current sitecore item:
private static readonly ILog log = LogManager.GetLogger(typeof(Global));
protected void Application_Error(object sender, EventArgs e)
{
if (Context != null)
{
Exception error = Context.Server.GetLastError().GetBaseException();
log.Fatal(
GetErrorMessage(), error);
}
}
private string GetErrorMessage()
{
StringBuilder sb = new StringBuilder();
sb.AppendLine("Application_Error: Unhandled exception has been occured.");
try
{
sb.AppendLine("Current Request: " + Context.Request.RawUrl);
Sitecore.Data.Items.Item currentItem = Sitecore.Context.Item;
if (currentItem != null)
sb.AppendLine(String.Format("Current Item ({0}): {1}", currentItem.ID, currentItem.Paths.FullPath));
if (Sitecore.Context.Database != null)
sb.AppendLine("Current Database: " + Sitecore.Context.Database.Name);
}
catch { } // in no way should we prevent the site from logging the error
return sb.ToString();
}
If you want an easy solution I would recommend going with Elmah. If you want to have more control and more logging options you should go with a custom log4net solution.
I tried defined custom http errors
under system configuration, but the
error pages were not hit. I am able to
handle 404s and their related errors
(layoutnotfound).
On that particular point...Be aware that custom errors behave differently when accessed locally. Also, by default you need to use physical files (not sitecore pages) for these pages. You need be aware of IIS behaviour when returning status codes over 200 and how it is dependant on the configuration within web.config and (if running in integrated mode) the section. In particular, see the flow chart half way down the first link below.
http://learn.iis.net/page.aspx/267/how-to-use-http-detailed-errors-in-iis-70/
http://www.iis.net/ConfigReference/system.webServer/httpErrors
http://msdn.microsoft.com/en-us/library/ms689487(v=VS.90).aspx
You might also want to change the RequestErrors.UseServerSideRedirect setting to "true" to provide better http responses without redirects.

CryptographicException: Padding is invalid and cannot be removed and Validation of viewstate MAC failed

Monitoring my global exception logs this error seems to be impossible to remove no matter what I do, I thought I finally got rid of it but it's back again. You can see a strack trace of the error on a similar post here.
Notes about the environment:
IIS 6.0, .NET 3.5 SP1 single server ASP.NET application
Steps already taken:
<system.web>
<machineKey validationKey="big encryption key"
decryptionKey="big decryption key"
validation="SHA1" decryption="AES" />
In my Page Base for all of my pages
protected override void OnInit(EventArgs e)
{
const string viewStateKey = "big key value";
Page.ViewStateUserKey = viewStateKey;
}
Also in the source of the page I can see that all of the ASP.NET generated hidden fields are correctly at the top of the page.
First of all lets start from the fact, that this error of view state happens on PostBack.
Also I must say that I have done all the things that every one suggest to do to avoid this problem. And I have single machine, but 2 pools that run the same Pages.
So someone do an action, ether a man, ether some other search machine by 'clicking' on your pages, or some hacker try to check your system for problems...
I have similar problems (rare but existing ones), and I finally found that people try to hack-test my pages. (from the same IP I have and dos attacks)
I modify the function LoadPageStateFromPersistenceMedium() that translate the viewstate, and see by logging what exactly was the input, and from what IPs... then I started monitor these results and see that the view state was changed by hand - or was totally empty.
On error I just redirect him to the same page...
Here is what I did...
public abstract class BasePage : System.Web.UI.Page
{
protected override object LoadPageStateFromPersistenceMedium()
{
try
{
.. return the base, or make here your decompress, or what ever...
return base.LoadPageStateFromPersistenceMedium();
}
catch (Exception x)
{
string vsString = Request.Form[__VIEWSTATE];
string cThePage = Request.RawUrl;
...log the x.ToString() error...
...log the vsString...
...log the ip coming from...
...log the cThePage...
// check by your self for local errors
Debug.Fail("Fail to load view state ! Reason:" + x.ToString());
}
// if reach here, then have fail, so I reload the page - maybe here you
// can place somthing like ?rnd=RandomNumber&ErrorId=1 and show a message
Responce.Redirect(Request.RawUrl, true);
// the return is not used after the redirect
return string.Empty;
}
}
Second Reason
Now there is one more reason why this can happen, and the reason is because some one click on your page before the __EVENTVALIDATION is loaded.
This eventValidation is placed on the last button-even that asp.net found, and if you have some of them on many place on the page, or near the button, then this go to the end of the page.
So even if you see the viewstate on the top of the page, where is the Validation ??? maybe this never loaded - page corrupt ?, too fast user click on page ?
<input type="hidden" name="__EVENTVALIDATION" id="__EVENTVALIDATION" ... >
To avoid this kind of problem I made a simple javascript that I do not let it press the button unless this input have been loaded !!!.
One more comment, the __EVENTVALIDATION is not always presents ! so is maybe safer not to search for this field if you make a general solution, but to make a javascript trick to just check if the full page is loaded, or something else that you think.
Here is my final solution with jQuery: (note that I check on PageLoad if eventvalidation exist !). I have this placed on my MasterPages.
<script language="javascript" type="text/javascript">
function AllowFormToRun()
{
var MyEventValidation = $("#__EVENTVALIDATION");
if(MyEventValidation.length == 0 || MyEventValidation.val() == ""){
alert("Please wait for the page to fully loaded.");
return false;
}
return true;
}
</script>
protected void Page_Load(object sender, EventArgs e)
{
// I do not know if Page can be null - just in case I place it.
if (Page != null && Page.EnableEventValidation)
{
Form.Attributes["onsubmit"] = "return AllowFormToRun();";
}
}
You can test by placing near the button of your page a delay.
<% System.Threading.Thread.Sleep(5000); %>
Update
Today I see in log this message again for WebResource and what I discover is that a bot getting the pages and make all the character on the links in lower case, including the parameters, so this was one more reason to not getting the correct encoded string, and throw a message like Padding is invalid and cannot be removed.
Hope this help you more.
A survey of the web pages found with several of the keywords from the error message indicate that this type of error is relatively common, usually random (at least in appearance) and unfortunately rarely inclusive of an explicit work-around or explanation...
The existence of many similar-yet-different situations is probably tied to the very many different architectures and underlying configurations which can somehow lead to the inability of the crypto layer to assert the authenticity of the MAC (Message Authentication Codes) in the request pages:
Server farm setup
Cross domain / syndicated pages
third party widget libraries and such
Actual ASP program logic (of course)
One relatively frequent "marker" around these bug reports is the mention of resource requests (eg. WebResource.axd).
Note that such requests are often not logged (lest they inflate the log files size with event of relative little interest). This absence from the log file and the fact they are often cached (and hence the relative random and infrequent occurrence of the bug) may explain how this possible origin of the bug go "under the radar". This also suggests that in trying to recreate the bug, (while tracking in the logs, in real time etc) it may be useful to prevent the web browser from caching (or for the least to clear it cache initially).
In short, here are a few ideas and things to look for:
start logging the *.axd requests
try and co-relate such axd requests with the error events in the exception log
look for pages with resource references
if in a Farm setting, ensure that all instances use the same key (apparently the snippet provided in the question hint at multiple IIS servers)
Be suspicious of pages with 3rd party tie-ins (search services, affiliate programs...)
Hope this helps ;-)
Are you sure your problem is cryptography related, and not caused by oversized ViewState?
If ViewState is the problem, you can chunk it - change the value of pages / MaxPageStateFieldLength in web.config

Categories

Resources