views:

447

answers:

2

Hello everyone,

I have a web service in which I am manipulating POST and GET methods to facilitate upload / download functionality for some files in a client/server style architecture. Basically the user is able to click a button to download a specific file, make some changes in the app, then click upload button to send it back.

Problem I am having is with the download. Say the user expects 3 files 1.txt, 2.txt and 3.txt. Except 2.txt does not exist on the server.

So I have code like (on server side):

public class HttpHandler : IHttpHandler
{

    public void ProcessRequest
    {
       if (context.Request.HttpMethod == "GET")
       {
          GoGetIt(context)
       }
    }

private static void GoGetIt(HttpContext context)
{
     var fileInfoOfWhereTheFileShouldBe = new FileInfo(......);

     if (!fileInfoOfWhereTheFileShouldBe.RefreshExists())
     {
          //Remove this line below
          //throw new Exception("Oh dear the file doesn't exist");

          //Replace with a force return of whichever code I chose e.g. 200
          ??...
     }

    ...

So the problem I have is that when I run the application, and I use a WebClient on client side to use DownloadFile method which then uses the code I have above, I get:

WebException was unhandled: The remote server returned an error: (500) Internal Server Error.

(While debugging) If I attach to the browser and use http://localhost:xxx/1.txt I can step through server side code and throw the exception as intended. So I guess I'm wondering how I can handle the internal server error on the client side properly so I can return something meaningful like "File doesn't exist". One thought was to use a try catch around the WebClient.DownloadFile(address, filename) method but i'm not sure thats the only error that will occur i.e. the file doesn't exist.

edit: following the solution using HttpResponse

So if I were to use HttpResponse, could I get some suggestions as how to start?

I remove the exception throw from the client side, and replace with custom HttpResponse? So basically I guess I would chose a code to use, say 200, and force return code 200 in that if statement above. See comment.

Then on client side just use If (Response.StatusCode == 200) and do whatever I want to do (inform user file doesn't exist )

I'm along the right lines?

edit 2:

I've been trying using a try catch around my file copy methods, then in the catch, setting the status code or status description but this throws exceptions when setting the status description.. like this:

context.Response.StatusDescription = ex.ToString();
context.Response.Status = ex.ToString();

ArgumentOutOfRangeException - specified argument was out of the range of valid values.

+1  A: 

If you're programming the IHttpHandler interface you shouldn't throw exceptions on that code. Never!

Instead use the Response.StatusCode and Response.StatusDescription to return meaningful information to the client.

Let the throwing exception only to the system because then, it will REALLY be an exception to YOUR code.

Edited to add

Answering to your edit, the way I'd do it would be to in case of a file not found on the server side would return a 404 Status Code. And let the client handle this.

However, as you have put you're dealing with an web service, so, I'd simply add some additional response in the headers in order to better specify what really is going on the server side to your client application.

Edited to Add

Response.Status is and integer. That's why you're getting the ArgumentOutOfRangeException.

Make sure that Status is one of the valid HTTP return codes.

Paulo Santos
is this for security reasons? what is wrong with exceptions in IHttpHandler?
baron
@baron: they won't be propagated to the client in any meaningful way.
John Saunders
@Paulo Santos - could you respond to my edit?
baron
I don't understand how the headers are going to help when it breaks (WebException unhandled) on (clientside) `WebClient.DownloadFile(URLofTxt);` also can you show some code how to set the response, I couldn't work this out or find any good examples in your links.
baron
While reviewing your answer I managed to accidentally vote down your answer (After I already had it voted up). Now stackoverflow retardedly won't let me fix that because "Vote too old to be changed, unless this answer is edited" despite the fact I tried to change it back immediately after accidentally changing to vote down in the first place. So what - I was half a second too late? Somehow I find that hard to believe. Grrr. Sorry.
baron
@baron.. No problem...
Paulo Santos
A: 

Instead of throwing the exception, Log the exception in a text file or event log so that you can see exactly what happened when the error occur.

Here is sample code for event logging http://support.microsoft.com/kb/307024. For saving in the text file

    public void WriteExceptionToDisk(Exception exceptionLog)
    {

        string loggingname = @"c:\Exception-" + DateTime.Today.Month.ToString()
                             + "-" + DateTime.Today.Day.ToString() + "-" +
                             DateTime.Today.Year.ToString() + ".txt";
        // Put the exception some where in the server but
        // make sure Read/Write permission is allowed.
        StringBuilder message = new StringBuilder();
        if (exceptionLog != null)
        {
            message.Append("Exception Date and Time ");
            message.AppendLine(); 
            message.Append("   ");
            message.Append(DateTime.Today.ToLongDateString() + " at ");
            message.Append(DateTime.Now.ToLongTimeString());
            message.AppendLine();
            message.Append("Exception Message ");
            message.AppendLine(); message.Append("   ");
            message.Append(exceptionLog.Message);
            message.AppendLine();
            message.AppendLine("Exception Detail ");
            message.AppendLine();
            message.Append(exceptionLog.StackTrace);
            message.AppendLine();
        }
        else if (message == null || exceptionLog == null)
        {
            message.Append("Exception is not provided or is set as null.Please pass the exception.");
        }

        if (File.Exists(loggingname))// If logging name exists, then append the exception message
        {

            File.AppendAllText(loggingname, message.ToString());
        }
        else
        {
            // Then create the file name
            using (StreamWriter streamwriter = File.CreateText(loggingname))
            {
                streamwriter.AutoFlush = true;
                streamwriter.Write(message.ToString());
                streamwriter.Close();
            }                 
        }
    }
Wonde
-1: 1) StreamWriter needs to be in a `using` block, otherwise if an exception is thrown, the resources will be leaked. 2) Building the message through string concatenation.
John Saunders
@ John Saunders. Why it StreamWriter should be put in using block.I just closed explicitly ?
Wonde
@wonde because if an exception is thrown **WHILE** the `StreamWriter` is being used then the resource will be leaked. By wrapping it with an `using` clause you ensure that, even if an exception is thrown, the resource will be cleared properly.
Paulo Santos
@Paulo Santos - Thank you for detail explanation.
Wonde