Skip to content
blackcity edited this page Jul 2, 2013 · 3 revisions

###Handling exceptions Handling exceptions is an important task in any application. Backload provides you with a single point for handling exceptions. Just create an extension derived from IProcessPiplineException and your extension will be called no matter where the exception occurred during the execution of the processing pipeline. You can do whatever you want in your exception extension, for example you may want to gracefully handle and return an error message within your Json output, or you may want to stop any further processing and report a server error back. In this example you will learn 3 ways to handle the exception. Note: As in Example09, we use PlUpload from Moxiecode as our client side uploader. Both examples show you how easy you can write extensions to produce the Json output in a PlUpload friendly format (same with other uploaders). Note also, that we use JQuery 2.0 in our examples which does not support older browsers (use JQuery 1.9 if you need this support).

####Note on creating extensions Extensions are normal classes, decorated with an ExportAttribute, that are implementing a simple interface. When an extension point in the processing pipeline is reached, all extensions in the Extensions folder implementing the interface for this extension point are called. This is a very cheap process regarding system resources, because the extensions are instantiated as singletons and the system has only to read the metadata once. For Backload, these extensions are like helper classes. It loops through all extensions for a specific extension point, reads/writes values and finally incorporates retrieved data into its own environment. While executing, extension can do whatever you want to do: For example: Logging, additional authentication/authorization, retrieving/sending data from/to resources or web services, additional image processing, additional file handling, PDF or other file generation, etc. Extension usually have a very specific task to fulfill. If you have to do multiple different tasks for a specific extension point, just split them into seperate extensions (assemblies). In our MVC testing environment, we've set the output build path for the extension to the ~/bin/Extensions folder for a test MVC application, so we do not need to copy them by hand into the Extensions folder. You may need to clean and rebuild your extensions project when you made changes. If breakpoints are not reached in your extensions code or strange exceptions occur during debugging, this might be a sign that your MVC application does not use the current assembly.

####General overview Our extensions project has 3 extensions for incoming requests, outgoing responses and exception handling. The incoming and outging extensions are only for demo purposes. The incoming extension throws an exception if a special file (BadFile.jpg) is uploaded and the outgoing extension transforms the output into a PlUpload friendly format. So we only need to have a look into the ProcessPiplineException extension class and its corresponding client side JavaScript handlers. The extension class includes 3 ways of handling the exception: Two gracefully handled exceptions (Examples 1 and 2) and one none gracefully (Example 3). We start with Example 1.

####Example 1: Using a success property (HTTP Status 200) In the first example we simply set a success property to false. If a class implementing IFileUploadStatus not has been created yet (because the exception was too early in the processing pipeline, we create our own (A FileUploadStatus class internally holds the values to create the Json response) and return it back to the pipeline. Note, that after we finished our exception handling the pipeline (resp. our OutgoingResponse extension) produces the Json output as usual along with a HTTP Status code 200. Now the client side FileUploaded event handler will be invoked, not the Error handler, because of the HTTP Status 200 (ok). So in this example we need a switch on the file.success==[true|false] condition and do the error handling ourselves. In this demo we now want to throw an exception. This is done in our IncomingRequest extension when you try to upload a file named BadFile.jpg (in this examples root folder). Set a breakpoint in the extensions code and in the client side FileUploaded event handler (~/Scripts/main.js). Now try to upload ~/BadFile.jpg.

#####ProcessPiplineException extension code (Example 1)

namespace Backload.Extension.Contoso
{
    [Export(typeof(IProcessPipelineException))]
    public class ProcessPipelineException : IProcessPipelineException
    {
        public IFileUploadStatus FileStatus { get; set; }
        public Exception Exception { get; set; }
        public List Logger { get; set; }
 
        public bool ProcessStep(System.Web.HttpRequestBase request, string httpMethod)
        {
            //
            // Example 1: Graceful exception. We send an error message in our response
            // Extensions responsible for the outgoing response (IOutgoingResponse) are executed as usual.
            // Note: In this case the error is not handled in our client side error handler as we do not 
            // send a HTTP status error code (see examples 2 and 3). Instead the response was send to our 
            // client side FilesAdded handler, so we need an if statement reading the file.success property there.
            // This could lead to a lot of messy code, because you have to notify the client uploader yourself that something 
            // went wrong. PlUpload isn't very good in changing the state from your code. Example 2 provides a cleaner solution.
            //
            // Do whatever you want, this code is only for demo. Example
            if (FileStatus != null)
            {
                foreach (var file in FileStatus.files)
                {
                    file.error = Exception.Message; // for simplicity we set the error to all FileUploadStatusItems
                    file.success = false; // for simplicity we set the error to all FileUploadStatusItems
                }
            }
            else
            {   // For demo only. We build our own FileStatus. FileStatusSimple has no logic implemented, its only a property bag.
                FileStatusSimple status = new FileStatusSimple();
                for (int i = 0; i < request.Files.Count; i++)
                {
                    var file = request.Files[i];
                    status.files.Add(new FileStatusSimpleItem(file.FileName, file.ContentType, file.ContentLength, "", Exception.Message, false));
                }
                // Now that we have a simple IFileUploadStatus object it can be transformed in our outgoing extension 
                // into a PlUpload friendly Json format. 
                this.FileStatus = status;
            }

            return true;  // Set to true, if the extension has manipulated the result.
        }
    }
}

#####Client side FileUploaded event handler

    // If the HTTP status code is < 400 the FileUploaded will be called
    uploader.bind('FileUploaded', function (up, files, result) {
            $.each(files, function (i, file) {
                if (remoteFiles[i].success) {
                    // Everything is ok
                    ...
                } else {
                    // Something went wrong
                    // We are here because in Example 1 of our server side exception extension the HTTP status code is set to 200 (default)
                    // In Example 2 and 3 we set the HTTP status code 500 within the extension, so this handler will not be called and we do not
                    // need to switch on the file.success condition.
                    ...
                }
            });
        }
    });

####Example 2: Sending a Http error status code This example differs only in one additional line of code. This will send an Http 500 Internal Server along with your Json data. All further extension will be called as usual. But now the client side Error handler will be invoked. This is a cleaner solution because we let the client side plugin do the hard work and we do not mess up the client FileUploaded handler. Comment in the line marked with "Example 2". Set a breakpoint and set an additional breakpoint in the client side Error event handler (main.js). Upload ~/BadFile.jpg. #####Added line in the erver side extension code (Example 29)

   //
   // Example 2: Set Response code to a valid http status code (e.g. 500 Internal server error.
   // Extensions responsible for the outgoing response (IOutgoingResponse) are executed as usual and can build a custom Json response
   // which will be sent with the HTTP status code. The client error handler can use the Json object.
   // Comment in the following line (Don't forget to clean and rebuild your solution):
   //
   request.RequestContext.HttpContext.Response.StatusCode = 500;

#####Client side Error handler

 // If the HTTP status code is >= 400 the Error handler will be called
 uploader.bind('Error', function (up, err) {
     var file = err.file;
     if (typeof err.response === "string") resp = $.parseJSON(err.response); // if string convert to an object
     else resp = err.response;                           // We have a response object, if we use graceful exception handling in our extension. Otherwise, if we throw an error, this is null
     $('.plupload_header .plupload_message').remove();   // remove existing error message
     $uploaderwidget.notify("error", "HTTP Error " + err.status + " 
An error has occured during the last operation" + ((file != null) ? file.name : "") + "."); });

####Example 3: Throwing an HttpException In the last example we throw an HttpException (500 Internal server error). This terminates the processing pipeline and the Server returns the HTTP status code. In this case no extensions (e.g. OutgoingResponse extensions) will be called and the response body is empty. But the client side Error handler will be invoked and can do its tasks. If you do not need any data from the response body and other extensions must not be executed, this maybe also is a solution for you (Client side code is the same as in example 2). Set the breakpoints and upload the file ~/BadFile.jpg. Note: Internally Backload uses standard MVC error handling procedures, so an exception will always be handled in a clean and controlled manner. #####Added line to throw a HttpException

   //
   // Example 3: We throw an exception, so ASP.MVC sends out an http status code.
   // Extensions responsible for the outgoing response (IOutgoingResponse) are not executed anymore.
   // The response body is null in this case, because no JsonResult has been created.
   // Comment in the following line (Don't forget to clean and rebuild your solution):
   //
   throw new HttpException(500, Exception.Message, Exception);

####Conclusion In this example you learned how to handle exceptions. Backload provides you with the means to handle an exception gracefully or terminate the further processing. It's up to you how to handle exceptions, but in most cases Example 2 is the best method. Don't forget, that you can have multiple exception handling extensions installed in the Extensions folder and every extension can do a specific task even in the case of exceptions.

Code

Code examples: Example10


License

Backload. (Standard version): Copyright 2013, Steffen Habermehl, License (Standard version): MIT license
Professional and Enterprise (source code) version are available under a commercial license.
Follow us on Twitter: @Backload_MVC

Clone this wiki locally