in my application (Windows Phone), I generate a connection to a WCF web service dynamically via code behind.
The user must specify the url of the Web Service then I create my EndPointAddress and Binding, and it works well.
But if the user enter a invalid url, an exception is thrown :
"System.ServiceModel.EndpointNotFoundException -> There Was no endpoint listening at [address of the service] That Could accept the message. This Is Often Caused By An incorrect address or SOAP action. See InnerException, if present, for more details."
The innerexception is quite classic : "The remote server Returned an error: NotFound at InnerException."
The problem: I can not handle this exception. I tried many things found here without success.
Is there any solution?
The only thing I could find is surronding some methods in the Reference.cs, which is definitly not a good idea ...!
Thank you in advance and sorry for my bad english :) !
My actual code looks like this, but it doesn't catch the exception =>
MyServiceClient client = new MyServiceClient(myBinding, myEndpointAddress);
try
{
client.RegisterUserCompleted += new EventHandler<RegisterUserCompletedEventArgs>(client_RegisterUserCompleted);
client.RegisterUserAsync();
}
catch (TimeoutException ex)
{
MessageBox.Show(ex.Message);
client.Abort();
}
catch (FaultException ex)
{
MessageBox.Show(ex.Message);
client.Abort();
}
catch (CommunicationException ex)
{
MessageBox.Show(ex.Message);
client.Abort();
}
catch
{
MessageBox.Show("Error");
client.Abort();
}
finally
{
client.CloseAsync();
}
the problem is solved.
The solution is : the uri have to be test before creating the client.
To do it, I make a WebRequest and catch the exception in the WebResponse :
public void ValidateUri(String uri)
{
if (!Uri.IsWellFormedUriString(uri, UriKind.RelativeOrAbsolute)) return;
request = WebRequest.Create(uri);
request.BeginGetResponse(new AsyncCallback(ValidateUriCallback), null);
}
private WebRequest request;
private void ValidateUriCallback(IAsyncResult result)
{
try
{
WebResponse httpResponse = (WebResponse)request.EndGetResponse(result);
// Create the client here
ServiceClient client = new MyServiceClient(myBinding, myEndpointAddress);
client.RegisterUserCompleted += new EventHandler<RegisterUserCompletedEventArgs>(client_RegisterUserCompleted);
client.RegisterUserAsync();
}
catch (WebException ex)
{
var response = ex.Response as System.Net.HttpWebResponse;
if (response!=null
&& response.StatusCode == HttpStatusCode.NotFound)
{
Dispatcher.BeginInvoke(delegate()
{
MessageBox.Show("The web service address is not valid", "Sorry", MessageBoxButton.OK);
});
}
}
You need move your try catches to the client_RegisterUserCompleted method and wrap around the var x = e.Result;
EDIT: Since my first approach was wrong...
First you should start by inspecting your URL (myEndpointAddress in your code). Can you browse to it in a browser (you can set a breakpoint on that line, then copy the value and paste it into your browser).
If that works or if there is an error shown when you browse to it, then add this to your web.config of the web app hosting the WCF service inside of the
<system.diagnostics> <sources> <source name="System.ServiceModel" switchValue="Information, ActivityTracing" propagateActivity="true"> <listeners> <add name="traceListener" type="System.Diagnostics.XmlWriterTraceListener" initializeData= "Trace.svclog" /> </listeners> </source> </sources> </system.diagnostics>
Then run your application or browse to it. It will create a Trace.svclog file in the same directory as your web application. Open up that file and look for the highlighted in red entry. That will give you more information on the exception. WCF doesn't return the full information by default to slow down malicious users.
Related
I am developing an API using C# and .net 4.5.2; The API methods can return a handled BadRequest error or OK with an object response as per the below example:
[Authorize]
[RoutePrefix("api/Test")]
public class TestController : ApiController
{
[HttpGet]
[Route("TestMethod")]
public IHttpActionResult TestMethod()
{
MyProvider op = new MyProvider();
var lstResults = new List<Result>();
try
{
lstResults = op.TestMethod();
}
catch (Exception ex)
{
return BadRequest(ParseErrorMessage(ex.Message.ToString()));
}
return Ok(lstResults);
}
}
All errors are returned in a message object as below JSON:
{
Message: "Username or password is incorrect!"
}
The above was working perfectly until we added the below new configuration to redirect all 404 errors to a custom page for security issues. Now anybody (more specifically a hacker) who tries to call the API randomly, will be redirected to a custom 404 error page instead of the original .NET 404 page.
Web.config:
<httpErrors errorMode="Custom" existingResponse="Replace">
<remove statusCode="404" />
<error statusCode="404" path="404.html" responseMode="File"/>
</httpErrors>
The problem is:
BadRequest errors are not handled anymore as it was mentioned in the beginning, the custom JSON structure is not returned anymore, custom messages like "Username or password is incorrect!" are not taken into consideration, just the same simple text is always returned: Bad Request as per below screenshot:
The solution should be running on windows server 2016 IIS version 10.
How to solve the issue by keeping both working?
Update:
If I remove existingResponse="Replace", the badrequest message is returned, but the 404 custom error is not working anymore as per below screenshot
If I set errorMode="Detailed" the 404 custom error won't work anymore, and HTML description is returned for a bad request as you can see here:
I ended up using the below to solve the issue; Thus I won't mark it as the perfect answer since it didn't solve the above issue and I didn't know yet why the configuration did not work properly as excepted. So any answer that can solve the issue is more than welcomed, below is what worked as excepted for me:
Remove the httpErrors configuration from web.config
Use Global.asax file and add the below method to handle any error not handled in the API solution:
protected void Application_Error(object sender, EventArgs e)
{
try
{
try
{
Response.Filter = null;
}
catch { }
Exception serverException = Server.GetLastError();
//WebErrorHandler errorHandler = null;
//Try to log the inner Exception since that's what
//contains the 'real' error.
if (serverException.InnerException != null)
serverException = serverException.InnerException;
// Custom logging and notification for this application
//AppUtils.LogAndNotify(new WebErrorHandler(serverException));
Response.TrySkipIisCustomErrors = true;
string StockMessage =
"The Server Administrator has been notified and the error logged.<p>" +
"Please continue on by either clicking the back button or by returning to the home page.<p>";
// Handle some stock errors that may require special error pages
var HEx = serverException as HttpException;
if (HEx != null)
{
int HttpCode = HEx.GetHttpCode();
Server.ClearError();
if (HttpCode == 404) // Page Not Found
{
Response.StatusCode = 404;
//Response.Write("Page not found; You've accessed an invalid page on this Web server. " + StockMessage);
Response.Redirect("404.html");
return;
}
}
Server.ClearError();
Response.StatusCode = 500;
// generate a custom error page
Response.Write("Application Error; We're sorry, but an unhandled error occurred on the server." + StockMessage);
return;
}
catch (Exception ex)
{
Server.ClearError();
Response.TrySkipIisCustomErrors = true;
Response.StatusCode = 500;
Response.Write("Application Error Handler Failed; The application Error Handler failed with an exception.");
}
}
It worked like a charm:
user is redirected to 404.html custom page
Any 400 error is being thrown properly with the JSON format and proper message
The msdn page documenting the enumeration WebExceptionStatus says:
This API supports the product infrastructure and is not intended to be
used directly from your code.
Why? It is public and documented and an obvious way to determine why the webrequest failed.
Example:
I am using the class WebRequest get data from a webserver. Of course, I'll have to do exception handling if an error occurs during the request. So if something goes wrong, I want to seperate protocol errors (example: server returned a response with http status 400) from network errors (example: timout). For that I wanted the property Status:
try {
var request = WebRequest.CreateHttp(QueryUri);
//...
}
catch (WebException exception) {
if (exception.Status == WebExceptionStatus.ProtocolError) {
throw new MyCustomServerErrorException(...)
}
// ...
}
When a ajax call is made by my application I throw an error and wish to capture it at my client side. Which approach will be the best.
My server side code is:
try
{
...
}
catch (Exception ex)
{
throw new HttpException("This is my error", ex);
}
My client side code is:
var url = $(this).attr('href');
var dialog = $('<div style="display:none"></div>').appendTo('body');
dialog.load(url, {},
function (responseText, status, XMLHttpRequest) {
if (status == "error") {
alert("Sorry but there was an error: " + XMLHttpRequest.status + " " + XMLHttpRequest.statusText);
return false;
}
....
At runtime, when debugging, I don't get my error details as you can see on the screenshot below:
I get a generic error:
status: 500
statusText: Internal Server Error
How can I get the detail I sent : "This is my error" ?
Finally I use this method:
Web.config:
<system.web>
<customErrors mode="On" defaultRedirect="~/error/Global">
<error statusCode="404" redirect="~/error/FileNotFound"/>
</customErrors>
</system.web>
ErrorController:
public class ErrorController : Controller
{
public ActionResult Global()
{
return View("Global", ViewData.Model);
}
public ActionResult FileNotFound()
{
return View("FileNotFound", ViewData.Model);
}
}
Don't forget to create 2 specific views.
Finally when I need to throw specific error code & description, I proceed like this:
public ActionResult MyAction()
{
try
{
...
}
catch
{
ControllerContext.RequestContext.HttpContext.Response.StatusCode = 500;
ControllerContext.RequestContext.HttpContext.Response.StatusDescription = "My error message here";
return null;
}
}
Then client side I receive such error information.
Do something like this
Server side:
try{
//to try
}catch(Exception ex)
{
return this.Request.CreateErrorResponse(HttpStatusCode.InternalServerError, "Error :"+ ex.Message);
}
Then the request will return an error 500 to javascript but with your exception message.
You can control the detailed error messages being sent to the clients. By default, the detailed error messages can be viewed only by browsing the site from the server itself.
To display custom error in this way from the server side, you need add the rule in your IIS configuration.
You can read this thread, maybe can help you:
HOW TO enable the detailed error messages for the website while browsed from for the client browsers?
I have an HTTP Module that I installed on our server. What's weird is that it works but every once in awhile it's not being executed. I have logging and during the times that it doesn't work it doesn't reach the code that logs. I don't see anything in the IIS logs or the event viewer either.
namespace RedirectModule
{
public class RedirectModule : System.Web.IHttpModule
{
private const string MobileUserAgent = "MobileUserAgentCacheKey";
private const string
STRING_TO_FIND = "info/lps";
private const string STRING_TO_ADD = "/mobile";
public void Dispose()
{
//clean-up code here.
}
public void Init(HttpApplication context)
{
context.BeginRequest += context_BeginRequest;
}
private static object sync = new object();
private void context_BeginRequest(object sender, EventArgs e)
{
try
{
HttpContext context = HttpContext.Current;
string url = context.Request.Url.ToString().ToLower();
if (!url.Contains(STRING_TO_FIND) || url.Contains(STRING_TO_FIND + STRING_TO_ADD))
return;
Logger.Current.Log("Starting Redirect Phase");
if (XmlToValues.IsMobile(context.Request.ServerVariables["HTTP_USER_AGENT"],
GetCachedFile(context, "Settings.xml")))
{
var mobileRedirect = GetRedirectUrl(url, STRING_TO_FIND, STRING_TO_ADD);
if (mobileRedirect != null)
{
Logger.Current.Log("Redirect to Mobile page");
context.Response.Redirect(mobileRedirect);
}
}
Logger.Current.Log("Web Page");
Logger.Current.Log("End Begin Request");
}
catch (Exception ex)
{
if (ex is ThreadAbortException)
return;
Logger.Current.LogError(ex);
}
}
public static string GetRedirectUrl(string url, string strToFind, string strToAdd)
{
try
{
Logger.Current.Log("Get Redirect Url ");
int idx = url.IndexOf(strToFind) + strToFind.Length;
return url.Substring(0, idx) + strToAdd + url.Substring(idx);
}
catch (Exception ex)
{
Logger.Current.LogError(ex);
return null;
}
}
private XmlNodeList GetCachedFile(HttpContext context, string filePath)
{
try
{
Logger.Current.Log("GetCachedFile START");
if (context.Cache[MobileUserAgent] == null)
{
context.Cache[MobileUserAgent] = XmlToValues.GetMobileUserAgents(filePath);
Logger.Current.Log("Add Mobile File to Cache");
}
return (XmlNodeList)context.Cache[MobileUserAgent];
}
catch (Exception ex)
{
Logger.Current.LogError(ex);
return null;
}
}
}
}
and my Web.Config:
<?xml version="1.0" encoding="UTF-8"?>
<!--
For more information on how to configure your ASP.NET application, please visit
http://go.microsoft.com/fwlink/?LinkId=169433
-->
<configuration>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true">
<remove name="RedirectModule" />
<add name="RedirectModule" type="RedirectModule.RedirectModule, RedirectModule" />
</modules>
<handlers>
<remove name="Redirect" />
</handlers>
<validation validateIntegratedModeConfiguration="false"/>
</system.webServer>
<system.web>
<httpModules>
<add name="RedirectModule" type="RedirectModule.RedirectModule, RedirectModule" />
</httpModules>
<compilation debug="true">
</compilation>
</system.web>
</configuration>
p.s. I took out the log4net in the web.config as it's cumbersome.
Here's the link to the project: http://www.sendspace.com/file/w42me5
This is the markup of the page being requested, it's in a file called index.htmnl:
<html xmlns="http://www.w3.org/1999/xhtml" >
<head>
<!-- no cache headers -->
<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="no-cache">
<meta http-equiv="Expires" content="-1">
<meta http-equiv="Cache-Control" content="no-cache">
<!-- end no cache headers -->
</head>
<body>
MOBILE
</body>
</html>
I had a similar prblem... Try to turn of caching in your webbrowser and try again. In order to turn off caching for this requests youll need to modify response header. Example on modifying caching option
It sounds like you're probably hitting a cache somewhere between the browser and the server. There's a lot of potential places for the cache to be, here's some general steps to try and find it:
Browser - The browser does not have to request what it has cached. This question lists tools for different browsers to confirm what requests are being sent.
Client Computer - If a request is being sent from the browser, you may have a proxy (like Squid) returning cached data. You can use Fiddler to check if a HTTP request ever leaves the client computer.
Network Proxy - Same idea as the client computer, just the proxy is located on a server somewhere on your network. You would have to analyze outgoing HTTP traffic at the proxy server to determine if a request is being sent or not.
Web Server - Your redirect module can serve cached content instead of rendering it new. In this case, it should show up in your logs. Add a log call to the very beginning of your BeginRequest handler to confirm if the request actually makes it to your server.
To prevent caching, you can add add no-cache headers to the request (article here):
private void Application_EndRequest(Object source, EventArgs e)
{
HttpApplication application = (HttpApplication)source;
HttpContext context = application.Context;
context.Response.ExpiresAbsolute = DateTime.Now.AddDays( -100 );
context.Response.AddHeader( “pragma”, “no-cache” );
context.Response.AddHeader( “cache-control”, “private” );
context.Response.CacheControl = “no-cache”;
}
EDIT
Debugging what request get to the server
HTTP 200 in the response suggests that it's very likely you do not have a cache issue. To confirm every request is actually getting to the server, try adding a log to your Application_BeginRequest handler (in Global.asax) to log every request and compare it to the log generated by your context_BeginRequest in your HttpModule.
//In Global.asax
private void Application_BeginRequest(Object source, EventArgs e) {
Logger.Current.Log("Request made to Application_BeginRequest")
}
//In your HttpModule
private void context_BeginRequest(object sender, EventArgs e)
{
//Log before any of your other code
Logger.Current.Log("Request made to context_BeginRequest")
try
{
// your code
}
catch (Exception ex)
{
// log the exception first in case this is the problem
Logger.Current.LogError(ex);
if (ex is ThreadAbortException)
return;
}
}
Interpreting the results
Check your log after a request that is not getting to your module.
If it shows nothing, the request never made it to your server. See above about caches
If your log end at Request made to Application_BeginRequest, your module is not being called.
If it ends at Request made to context_BeginRequest, there's an error in the module code before your logger which is causing a crash on some requests. This could be a null reference HttpContext.Current, or Request, or a problem with overflows, or any number of other things. This should be logged by your finally statement.
I have this method which tries to catch an exception that might be thrown when supplied with invalid credentials when connecting through SSL. The credentials are supplied through SNMP. But I am not always able to interpret the exception as invalid credentials, as you can see I am only trying to read the message of exception thrown which differs from a device to device.
Is what am I doing correct? Are there any foolproof ways to check if the credentials needed to make the connection is valid? I am totally lost here as the exception message thrown changes from device to device.
Thanks for any pointers.
try
{
string ipSSL = string.Format(URL_CWIS_HELPSTRING, "s", SecurityBindingProxy.Destination.Address.Value.Host, "/");
System.Net.WebRequest https = System.Net.HttpWebRequest.Create(ipSSL);
System.Net.ServicePointManager.ServerCertificateValidationCallback = WsdlClient.ServiceRequirements.WsdlServiceRequirements.SSLCheckCallback;
https.Credentials = new System.Net.NetworkCredential(SNMP.ConnectionInfo.Instance.Username, SNMP.ConnectionInfo.Instance.Password); ;
System.Net.HttpWebResponse responseSSL = (System.Net.HttpWebResponse)https.GetResponse();
responseSSL.Close();
}
catch (Exception ex)
{
if (ex.Message.Contains("Unauthorized")
|| ex.Message.Contains("401"))
{
return -2;
}
}
You want to be catching a more specific type of exception rather than parsing exception messages. If you look at the docs for GetResponse you'll see documented the types of exceptions thrown. If you catch a WebException then this will allow you to find out the HTTP status without parsing strings manually e.g.:
catch (System.Net.WebException ex)
{
var errorResponse = (System.Net.HttpWebResponse)ex.Response;
if (errorResponse.StatusCode == System.Net.HttpStatusCode.Unauthorized)
{
//...
}
}