How to troubleshoot "System.Web.HttpException (0x80004005): File does not exist"? - asp.net

Apologies if this has already been answered on this site, I searched but did not find this exact scenario.
I'm adding log4net to a WCF service. I added a handler in the Application_Error event, and it is catching a file not found error on every request.
I've seen this with web sites, and usually the error can be traced down to not having a "favicon" file in the root directory, or to a missing image referenced in a css stylesheet.
However, this is a WCF service, there is no CSS stylesheet, and adding a favicon to the root did not solve the problem.
Does anyone else have a good way to troubleshoot this?
Some clues:
I haven't deployed this yet to the real IIS server, I'm running it locally.
The error does not happen when I am running in DEBUG inside Visual Studio, only when I access the service from a web browser (IE or Chrome)
I added the url and file path to the error message, and this is what they are:
URL: http://localhost:3994/
FilePath: /
Error: System.Web.HttpException (0x80004005): File does not exist.
Edit: the above values are what show up in the logged exception:
protected void Application_Error(object sender, EventArgs e)
{
var objErr = Server.GetLastError().GetBaseException();
if (objErr is System.Web.HttpException)
{
var filePath = Context.Request.FilePath;
var url = ((HttpApplication) sender).Context.Request.Url;
Log.Error("URL: " + url + "; FilePath: " + filePath, objErr);
} else
Log.Error("Application Error", objErr);
}
Any help would be greatly appreciated.

The reason is likely that the service has not been specified. When a web server (the local dev one as well) recieve a request for a folder they look inside that folder for the default page (usually called: index.htm, index.html, default.asp, default.aspx, etc) and present that (unless you are using a REST based service description). When you run from VS the debug will take you straight to the actual service.
In this case because you have built a service you need to specify the location of the exact service i.e. http://localhost:3994/service.svc.
Also: If you start a debug session and then change the URL to http://localhost:3994/ you should be able to check this is the case with the debugger.

Related

IIS/ASP.NET receiving calls from external application to SOAP proxy server

This is a weird one, sorry :( I have a remote server (3rd party, not under my control) that calls a defined endpoint (http://myservice.com/service.asmx), but internally before calling, it appends '.wsdl' to the URL string (so I see http://myservice.com/service.asmx.wsdl) The original server waiting for this request is expecting this, but the original server is no longer in service and I'm hoping to replace it with a 'stub'.
Basically, I'm trying to put an ASP.NET application in place to receive the requests (all currently running locally with IIS). I've used wsdl.exe to create my stub code, and it's called service.asmx. Using POSTMAN against this running service, it all works great - I can debug, see the responses etc, but if I try to rename my project to service.asmx.wsdl to accomodate for the real server making the request, I see a 405 - HTTP Verb error. I've been unable to figure out how to make this work and was thinking it's IIS handers or something like that. I've looked at IIS handers, but I can't seem to find one that would work (i.e., copying the .asmx profiles into newly created .wsdl profiles)
So my question is "Can I make the endpoint at .wsdl behave like it's an .asmx or am I approaching this all wrong?
After much hairpulling, I had to add Global.asax file to my project and implement the following method therein...
protected void Application_BeginRequest(object sender, EventArgs e)
{
var path = Request.Path;
if (path.EndsWith(".asmx.wsdl"))
Context.RewritePath(path.Replace (".asmx.wsdl", ".asmx"));
This allowed for the default asmx handlers in IIS to remain as-is and process the request from the URL by simply rewriting the URL programmatically.

Issue with CompileAssemblyFromSource when called from a WCF app

I have a WCF service that exposes a method. When a client calls this method, the following occurs:
the method does some processing
it tries to load an assembly if its already there
if the above dll isn't there, it generates C# code, compiles it using CSharpCodeProvider's CompileAssemblyFromSource api
It then loads the just generated assembly
Now the problem. The first time the method it called, it generates the assembly at point 3), and when it tries to return the assembly reference via CompilerResults.CompiledAssembly it throws a file not found exception. However, I can clearly see that the assembly has been generated at the specified location and I can open it with other applications.
If I call the method again via a client, it is able to load the assembly (it was successfully generated as a result of the previous call) and goes ahead to do the remaining set of tasks. Only when the assembly isnt there and it generates it and goes ahead to load it immediately, I get that exception. Any ideas? I have tried playing around with the web.config, changing impersonation to true/false. I have a seperate app pool to run this web application and I tried changing the identity of the app pool from local service to local system, even gave my windows logon credentials which has admin rights but no luck.
Any help would be much appreciated.
Are you sure it's generating the assembly? I have the same problem, except I can't find the generated .dll. I first suspected it was unable to write to the folder, so it now calls CreateDirectory and drops a text file to demonstrate that the folder is writeable.
Anyway, same problem, no success. Is it really the case that nobody else has had this issue??
I'm going to remote debug the server & see if I can step through Microsoft's PDBs...
-- EDIT --
No need to step through Microsoft's code. I had a look at the Errors collection of the CompilerResults and there was 1 item in there: "Metadata file 'c:\Windows\System32\aaclient.dll' could not be opened -- 'An attempt was made to load a program with an incorrect format. '"
When I get Directory.GetCurrentDirectory() to pick up the other DLLs it's usign the Windows System32 directory...
-- EDIT --
Resolved this by adding references from the executing assembly's folder:
CompilerParameters compilerParameters = new CompilerParameters
{
OutputAssembly = Path.Combine(GeneratedAssembliesFolder, string.Format("{0}.Generated.dll", typeName))
};
string executingDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
string[] dllFiles = Directory.GetFiles(executingDirectory, "*.dll");
compilerParameters.ReferencedAssemblies.AddRange(dllFiles.Select(f => Path.Combine(executingDirectory, f)).ToArray());
IEnumerable<string> exeFiles =Directory.GetFiles(executingDirectory, "*.exe").Where(f => !f.Contains(".vshost."));
compilerParameters.ReferencedAssemblies.AddRange(exeFiles.Select(f => Path.Combine(executingDirectory, f)).ToArray());
For greater robustness one aught to add checks for the binaries being valid managed code assemblies. This code could also be shortened by using a Linq .Union between the two GetFiles calls.
To find a suitable folder to write to:
private static string generatedAssembliesFolder;
private static string GeneratedAssembliesFolder
{
get
{
if (generatedAssembliesFolder == null)
{
string[] candidateFolders = new[]
{
Environment.GetEnvironmentVariable("TEMP", EnvironmentVariableTarget.Process),
Environment.GetEnvironmentVariable("TMP", EnvironmentVariableTarget.Process),
Environment.GetEnvironmentVariable("TEMP", EnvironmentVariableTarget.User),
Environment.GetEnvironmentVariable("TMP", EnvironmentVariableTarget.User),
Environment.GetEnvironmentVariable("TEMP", EnvironmentVariableTarget.Machine),
Environment.GetEnvironmentVariable("TMP", EnvironmentVariableTarget.Machine),
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData)
};
foreach (string candidateFolder in candidateFolders)
{
try
{
if (!Directory.Exists(candidateFolder)) Directory.CreateDirectory(candidateFolder);
string testFileName = Path.Combine(candidateFolder, Path.GetRandomFileName());
File.WriteAllBytes(testFileName, new byte[0]);
File.Delete(testFileName);
}
catch (Exception ex)
{
continue;
}
generatedAssembliesFolder = candidateFolder;
break;
}
}
return generatedAssembliesFolder;
}
}
Thanks user1796307 for your input. I had resolved this issue, but forgot to update it. Sharing it for everyone's benefit. It was the .NET Fusion at play. It caches assembly load path and won't even try to load an assembly if a previous attempt to load from same location had failed. In other words:
if (Assembly.Load("C:\xyz.dll") == null)
{
Compile("C:\xyz.dll"); // Now the dll exists
Assembly.Load("C:\xyz.dll"); // this will still fail
}
The solution is to change it as:
if (!File.Exists("C:\xyz.dll")
{
Compile("C:\xyz.dll"); // Now the dll exists
Assembly.Load("C:\xyz.dll"); // this will now work
}

ASP.NET where does throw new exception error get displayed

Code snippet..
if (regionalApprover == null)
{
throw new Exception(string.Format("The regional approver for {0} could not be found", companyData["Country"]));
}
How does the user actually see this error ?
The result of an unhandled exception depends on a variety of factors, including
where the web request is coming from,
the settings of the <customErrors> Element in your web.config and
the contents of Application_Error in your global.asax codebehind file.
In the default configuration, IIS will log the error into the Windows event log. In addition, it is shown in the browser by ASP.NET if the web request comes from localhost.
If you're trying to display an error message on the page (that the user is supposed to see), don't use Exceptions.
It's a much better idea to add an errors section to the page that you can add the messages to before showing the page to the user.

ASP.NET and 404 redirect problem

I have a page that reveives a lot of incoming traffic. Some of these fail, and i want to redirect them to my main page to avoid losing potentional customers. I have tried this in my global.asax
void Application_Error(object sender, EventArgs e)
{
// Code that runs when an unhandled error occurs
Exception ex = Server.GetLastError();
if (ex is HttpException)
{
if (((HttpException)(ex)).GetHttpCode() == 404)
Server.Transfer("~/Default.aspx");
}
// Code that runs when an unhandled error occurs
Server.Transfer("~/Default.aspx");
}
And i tried using custom errors, but my application keeps giving me the IIS 7.5 HTTP 404 error page, even though i should have handled it myself.... Everything is working as intended in my development environment, but on my hosted solution is isnt working... any solutions?
HTTP 404 is not application error. It is client error which means that requested web resource is not found / doesn't exist. It is returned by web server before it ever reach your application so your error code will never be executed.
Edit: Actually I'm not sure if this didn't change in IIS 7.x integrated mode but for IIS 6 and IIS 7.x in classic mode above statement is true.
Try the following:
throw new HttpException(404, "Not Found");
This should redirect user to the custom error page defined in web.config.
Also, it is usually considered as bad practice to redirect user to the main page instead of showing special "Not found" page. User should be aware of the error. Better way is to offer user some useful links on error page as well as quick search form.
I see in this code a potential dead loop on the same page (default.aspx).
Solved using a http handler and registering it in web.onfig

How to simulate an HTTP 500 error on my ASP.NET app?

I want to simulate this error so I can check a generic error page is displayed, not the HTTP 500 one, in light of the recent security vulnerability.
We include special processing in the site itself for 404 and 403 so I want to make sure that errors without special processing work too.
throw new Exception();
This will generate a HTTP 500
I think you can do this by overriding page init and adding the 500 status code to the response like the following:
protected void Page_Init(object sender, EventArgs e)
{
Response.Clear();
Response.StatusCode = 500;
Response.End();
}
Enjoy!
Here's a way to do this without modifying your site in any way:
From your web browser, open a page on your site that has a postback form.
Press F12 to open developer tools.
From the HTML tab, search for __VIEWSTATE and change the value in any way.
Post to the form
This will cause a "Validation of viewstate MAC failed" ASP.Net Exception, which returns a 500 internal server error HTTP response code.
Breaking the web.config with a malformed tag also works, but defeats the purpose if you are trying to test some settings in your web.config (like Failed Request Tracing).
Change the name of your dll file. It will crash the app if you ask for a route afterwards because it won't find the controller. I used this to test my logging.
you can break the web.config file. Put a malformed tag for tests
This generate a custom http error code in classic asp.
<%#language=Jscript%>
<%
Response.Status = "996 TerraNova GeoWeb Internal Server Error";
Response.End;
%>

Resources