openrasta

In OpenRasta, how should you handle codec errors or exceptions?


My scenario is this:

  1. A client application executes a HTTP POST against an endpoint exposed by OpenRasta.
  2. The body of the request contains an error that causes a problem in the codec - which is a custom implementation of OpenRasta.Codecs.IMediaTypeReader. This converts a JSON payload to the POCO expected by the handler.
  3. The codec throws an exception that describes the error in a useful way. For example: Newtonsoft.Json.JsonReaderException: After parsing a value an unexpected character was encountered: ". Line 4, position 5.
  4. The client application receives a HTTP 405 - MethodNotAllowed. The client doesn't see any of the exception details.

If the codec is modified to catch a JsonReaderException and return Missing.Value, similar to the Implementing a codec wiki, then the client receives a HTTP 500 - Internal Server Error. The body of the response also describes the following exception:

System.InvalidOperationException: The operation is not ready for invocation.
   at OpenRasta.OperationModel.MethodBased.MethodBasedOperation.Invoke()
   at OpenRasta.OperationModel.Interceptors.OperationWithInterceptors.<Invoke>b__0()
   at OpenRasta.OperationModel.Interceptors.OperationWithInterceptors.Invoke()
   at OpenRasta.OperationModel.OperationExecutor.Execute(IEnumerable`1 operations)
   at OpenRasta.Pipeline.Contributors.OperationInvokerContributor.ExecuteOperations(ICommunicationContext context)
   at OpenRasta.Pipeline.PipelineRunner.ExecuteContributor(ICommunicationContext context, ContributorCall call)

How should I modify my application so that:


Solution

  • Having found this thread on Google Groups which contains all the answers, my current implementation looks something like this.

    Within my implementation of IConfigurationSource:

    using (OpenRastaConfiguration.Manual)
    {
        ResourceSpace.Uses.PipelineContributor<ErrorCheckingContributor>(); 
    
        // Other configuration here
    }
    

    Then ErrorCheckingContributor looks something like this:

    public class ErrorCheckingContributor : IPipelineContributor
    {
        public void Initialize(IPipeline pipelineRunner)
        {
            pipelineRunner
                .Notify(CheckRequestDecoding)
                .After<KnownStages.IOperationResultInvocation>()
                .And.Before<KnownStages.ICodecResponseSelection>(); 
        }
    
        private static PipelineContinuation CheckRequestDecoding(ICommunicationContext context)
        {
            if (context.ServerErrors.Count == 0)
            {
                return PipelineContinuation.Continue;
            }
    
            var first = context.ServerErrors[0];
            if (first.Exception is Newtonsoft.Json.JsonReaderException)
            {
                context.Response.Entity.ContentType = MediaType.TextPlain;
                context.Response.Entity.ContentLength = first.Exception.Message.Length;
                using (var sw = new StreamWriter(context.Response.Entity.Stream))
                {
                    sw.Write(first.Exception.Message);
                }
            }
    
            return PipelineContinuation.Continue;
        } 
    }
    

    There's some things to be aware of with the above: