How to catch exceptions from codec and pass message out via OperationResult.

149 views
Skip to first unread message

Rob

unread,
Jan 10, 2011, 2:14:20 AM1/10/11
to OpenRasta
I have posts of lists of resources basically working, thanks to the
help in previous thread.
I would like to capture exceptions thrown by the newtonsoft codec.
When de-serialising I might get an exception along the lines of "The
string was not recognized as a valid DateTime."
This sort of error currently causes the OpenRasta to return 405 Method
not Allowed.
I dont mind that its a 405, but to assist clients I would like to be
able to include the message from the exception in the output.

I allready have a non URI resource registered for certain error cases.
ResourceSpace.Has.ResourcesOfType<ResourceError>().WithoutUri.TranscodedBy<NewtonsoftJsonCodec>();
public class ResourceError { public string Message { get; set; } }

I would like to be able to modify the 405 result to include this or
some other resource with the exception message.

As an initial test I created
public class CustomLogger : OpenRasta.Diagnostics.ILogger
ResourceSpace.Uses.CustomDependency<ILogger, CustomLogger>

This logger does not even see the exception, so I am assuming the
caller of codecs does not treat codec exceptions the same way [which
did not surprise me].
In addition this logger class is not useful to me in modifying the
operation result I believe a the moment.

I then created
class ExceptionInterceptor : OperationInterceptor {}
ResourceSpace.Uses.CustomDependency<IOperationInterceptor,
ExceptionInterceptor>(DependencyLifetime.Transient);

Which I stumbled on via google.
However the method RewriteOperation appears to never be called in my
context.

I am still searching other avenues, like a pipeline contributor or
something to wrap around OperationHydrator, however I am not there
yet, and thought I would post this query before proceeding.

Sebastien Lambla

unread,
Jan 10, 2011, 3:21:21 AM1/10/11
to open...@googlegroups.com
That'd be because the operation interceptor only gets called when an operation has been selected. If inputs have not been processed, the method won't be found.

You can register a pipeline contributor after the request decoding. If the codec throws at that point, normally a client error should be triggered. You can then just rewrite the operation context afterwards, puts a 400 with your error type as the ResponseResource and put the pipeline in RenderNow mode.

Rob

unread,
Jan 10, 2011, 5:12:38 AM1/10/11
to OpenRasta
Wow fast answer thanks.
I think I follow what your saying and it makes sense so far.

I have created the following which I believe follows your advice.
I am still quite new to this aspect so forgive me if i have just plain
missed the point.

In Configuration.
ResourceSpace.Uses.PipelineContributor<OperationDecodingValidator>();


public class OperationDecodingValidator : IPipelineContributor
{
public void Initialize(IPipeline pipelineRunner)
{

pipelineRunner.Notify(CheckRequestDecoding).After<KnownStages.IRequestDecoding>();
}
private static PipelineContinuation
CheckRequestDecoding(ICommunicationContext context) {}
}

It appears at the moment to me that CheckRequestDecoding() is only
reached if there was no exception from the Json Decoder which means I
dont get the opportunity to do something in the error case.

Assuming I can get this contiributor to run in the exception case, so
far I have not figured out how to recover the exception that was
thrown in the KnownStages.IRequestDecoding to be able to use to modify
the context.OperationResult in CheckRequestDecoding()

Rob

unread,
Jan 10, 2011, 5:21:27 AM1/10/11
to OpenRasta
Additional information...
Just looking through the log.

It appears that OperationHydratorContributor when it gets an exception
from json decoder allready uses RenderNow mode and the subsequent
pipeline contributor is not used. I might be missinterpretting this.


38-[2011-01-10 05:18:15Z] Start(1) Entering PipelineRunner:
Executing contributor OperationHydratorContributor.ProcessOperations
38-[2011-01-10 05:18:15Z] Information(0) Operation
Handler`1::Post(Int32 siteID, IEnumerable`1 resources) was selected
with a codec score of 1
38-[2011-01-10 05:18:15Z] Information(0) Loaded codec
TrakkaShell.Codecs.NewtonsoftJsonCodec
38-[2011-01-10 05:18:15Z] Verbose(0) Switching to full
object media type reading.
A first chance exception of type 'System.FormatException' occurred in
mscorlib.dll
A first chance exception of type 'System.FormatException' occurred in
Newtonsoft.Json.DLL
A first chance exception of type 'System.FormatException' occurred in
Newtonsoft.Json.DLL
A first chance exception of type 'System.FormatException' occurred in
TrakkaShell.DLL
38-[2011-01-10 05:18:15Z] Error(0) An error of type
System.FormatException has been thrown
38-[2011-01-10 05:18:15Z] Error(0) System.FormatException:
The string was not recognized as a valid DateTime. There is an unknown
word starting at index 0.
38-[2011-01-10 05:18:15Z] Error(0) at
TrakkaShell.Codecs.NewtonsoftJsonCodec.ReadFrom(IHttpEntity request,
IType destinationType, String destinationName) in C:\Projects\TrakkaWeb
\service\TrakkaShell\TrakkaShell\TrakkaShell\Codecs
\NewtonsoftJsonCodec.cs:line 31
38-[2011-01-10 05:18:15Z] Error(0) at
OpenRasta.OperationModel.Hydrators.RequestEntityReaderHydrator.TryReadPayloadAsObject(IHttpEntity
requestEntity, IMediaTypeReader reader, IOperation operation) in c:
\Projects\Sandpit\openrasta\src\core\OpenRasta\OperationModel\Hydrators
\RequestEntityReaderHydrator.cs:line 101
38-[2011-01-10 05:18:15Z] Stop(1) Exiting PipelineRunner
38-[2011-01-10 05:18:15Z] Verbose(0) Pipeline is in RenderNow
mode.

Sebastien Lambla

unread,
Jan 10, 2011, 5:45:20 AM1/10/11
to open...@googlegroups.com
Ah, yes, if there's an error you may well find that the pipeline is in rendernow mode, as all contributors before OperationResultInvocation are ignored.

At this stage it's probably easier to hook your contrib after result invocation and before response codec selection, that way you still have a chance to update the OperationResult with your custom type.

We should make codec error handling a bit better in future versions :)

Sebastien Lambla

unread,
Jan 10, 2011, 5:45:38 AM1/10/11
to open...@googlegroups.com
No you got it just reight :)

-----Original Message-----
From: open...@googlegroups.com [mailto:open...@googlegroups.com] On Behalf Of Rob

Sent: 10 January 2011 05:21
To: OpenRasta

Rob

unread,
Jan 10, 2011, 6:16:44 AM1/10/11
to OpenRasta

Could you illuminate me on how i can get the exception thrown
OperationHydrator available in OperationResultInvocation ?
Is there existing structure for me to find it ?
Or do i have to catch what I want of it it in my Json codec to be
picked up in OperationResultInvocation ?

Sebastien Lambla

unread,
Jan 10, 2011, 6:26:32 AM1/10/11
to open...@googlegroups.com
It should be in ICommunicationContext.ServerErrors?

-----Original Message-----
From: open...@googlegroups.com [mailto:open...@googlegroups.com] On Behalf Of Rob
Sent: 10 January 2011 06:17
To: OpenRasta
Subject: [openrasta] Re: How to catch exceptions from codec and pass message out via OperationResult.

Rob

unread,
Jan 10, 2011, 6:39:53 AM1/10/11
to OpenRasta
Think i found it in context.ServerErrors.

Andrés G. Aragoneses

unread,
Jan 12, 2011, 4:54:49 PM1/12/11
to OpenRasta, and...@7digital.com
Hello. I'm new in the list and new user of OpenRasta too, sorry if I
make no sense.

I was going to post a message asking for help but suddenly realised I
have a similar problem as the OP of this thread (exception from codec
which I want to capture, although my exception has a slightly
different stacktrace, but still comes from the
RequestEntityReaderHydrator [1]). So I think the approach you mention
here below is the one I need to use (because I already tried with
others [2] without success), could you provide an example of how to do
what you say about hooking a contributor after result invocation
please?

Thanks!

[1] My exception is:
2011-01-12 15:01:08,049 [12] ERROR openrasta - An error has occurred
and the processing of the request has stopped.

Exception:
System.Exception: someStringThatIsNotANumber is not a valid value for
Int32. ---> System.FormatException: Input string was not in a correct
format.
at System.Number.StringToNumber(String str, NumberStyles options,
NumberBuffer& number, NumberFormatInfo info, Boolean parseDecimal)
at System.Number.ParseInt32(String s, NumberStyles style,
NumberFormatInfo info)
at System.ComponentModel.Int32Converter.FromString(String value,
NumberFormatInfo formatInfo)
at
System.ComponentModel.BaseNumberConverter.ConvertFrom(ITypeDescriptorContext
context, CultureInfo culture, Object value)
--- End of inner exception stack trace ---
at
System.ComponentModel.BaseNumberConverter.ConvertFrom(ITypeDescriptorContext
context, CultureInfo culture, Object value)
at
OpenRasta.TypeSystem.ReflectionBased.ReflectionExtensions.CreateInstanceFromString(Type
type, String propertyValue, Stack`1 recursionDefender) in c:\BuildAgent
\work\636e974733804e57\src\core\OpenRasta\TypeSystem\ReflectionBased
\ReflectionExtensions.cs:line 470
at
OpenRasta.Codecs.AbstractApplicationXWwwFormUrlencodedCodec.ConvertValuesByString(String
strings, Type entityType) in c:\BuildAgent\work\636e974733804e57\src
\core\OpenRasta\Codecs\application\x-www-form-urlencoded
\AbstractApplicationXWwwFormUrlencodedCodec.cs:line 153
at
OpenRasta.TypeSystem.ReflectionBased.ReflectionExtensions.CreateInstanceFrom[T]
(Type type, IEnumerable`1 propertyValues, ValueConverter`1 converter)
in c:\BuildAgent\work\636e974733804e57\src\core\OpenRasta\TypeSystem
\ReflectionBased\ReflectionExtensions.cs:line 143
at
OpenRasta.TypeSystem.ReflectionBased.ReflectionBasedType.TryCreateInstance[T]
(IEnumerable`1 values, ValueConverter`1 converter, Object& result) in
c:\BuildAgent\work\636e974733804e57\src\core\OpenRasta\TypeSystem
\ReflectionBased\ReflectionBasedType.cs:line 82
at OpenRasta.TypeSystem.PropertyBuilder.TrySetValue[T]
(IEnumerable`1 values, ValueConverter`1 converter) in c:\BuildAgent
\work\636e974733804e57\src\core\OpenRasta\TypeSystem
\PropertyBuilder.cs:line 88
at OpenRasta.Binding.KeyedValuesBinder.SetPropertyValue[TValue]
(String key, String property, IEnumerable`1 values, ValueConverter`1
converter) in c:\BuildAgent\work\636e974733804e57\src\core\OpenRasta
\Binding\KeyedValuesBinder.cs:line 98
at OpenRasta.Binding.KeyedValuesBinder.SetProperty[TValue](String
key, IEnumerable`1 values, ValueConverter`1 converter) in c:\BuildAgent
\work\636e974733804e57\src\core\OpenRasta\Binding
\KeyedValuesBinder.cs:line 80
at OpenRasta.Binding.KeyedValues`1.SetProperty(IObjectBinder
binder) in c:\BuildAgent\work\636e974733804e57\src\core\OpenRasta
\Binding\KeyedValues`1.cs:line 37
at
OpenRasta.Codecs.CodecExtensions.<>c__DisplayClass8.<TryAssignKeyValues>b__7(KeyedValues
kv) in c:\BuildAgent\work\636e974733804e57\src\core\OpenRasta\Codecs
\CodecExtensions.cs:line 38
at System.Linq.Enumerable.WhereEnumerableIterator`1.MoveNext()
at
OpenRasta.Collections.ObservableIterator`1.<GetEnumerator>d__0.MoveNext()
in c:\BuildAgent\work\636e974733804e57\src\core\OpenRasta\Collections
\ObservableIterator.cs:line 42
at System.Linq.Enumerable.Count[TSource](IEnumerable`1 source)
at OpenRasta.Codecs.CodecExtensions.TryAssignKeyValues(ICodec
codec, IHttpEntity entity, IObjectBinder binder, Action`1 assigned,
Action`1 failed) in c:\BuildAgent\work\636e974733804e57\src\core
\OpenRasta\Codecs\CodecExtensions.cs:line 36
at
OpenRasta.Codecs.CodecExtensions.<>c__DisplayClassb.<TryAssignKeyValues>b__a(Boolean
result, IObjectBinder binder) in c:\BuildAgent\work
\636e974733804e57\src\core\OpenRasta\Codecs\CodecExtensions.cs:line 52
at System.Linq.Enumerable.Aggregate[TSource,TAccumulate]
(IEnumerable`1 source, TAccumulate seed, Func`3 func)
at OpenRasta.Codecs.CodecExtensions.TryAssignKeyValues(ICodec
codec, IHttpEntity entity, IEnumerable`1 binders, Action`1 assigned,
Action`1 failed) in c:\BuildAgent\work\636e974733804e57\src\core
\OpenRasta\Codecs\CodecExtensions.cs:line 52
at
OpenRasta.OperationModel.Hydrators.RequestEntityReaderHydrator.TryAssignKeyedValues(IHttpEntity
requestEntity, ICodec codec, Type codecType, IOperation operation) in
c:\BuildAgent\work\636e974733804e57\src\core\OpenRasta\OperationModel
\Hydrators\RequestEntityReaderHydrator.cs:line 88
at
OpenRasta.OperationModel.Hydrators.RequestEntityReaderHydrator.<Process>d__8.MoveNext()
in c:\BuildAgent\work\636e974733804e57\src\core\OpenRasta
\OperationModel\Hydrators\RequestEntityReaderHydrator.cs:line 53
at System.Collections.Generic.List`1..ctor(IEnumerable`1
collection)
at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
at
OpenRasta.Pipeline.Contributors.AbstractOperationProcessing`2.ProcessOperations(ICommunicationContext
context) in c:\BuildAgent\work\636e974733804e57\src\core\OpenRasta
\Pipeline\Contributors\AbstractOperationProcessing.cs:line 24
at
OpenRasta.Pipeline.PipelineRunner.ExecuteContributor(ICommunicationContext
context, ContributorCall call) in c:\BuildAgent\work
\636e974733804e57\src\core\OpenRasta\Pipeline\PipelineRunner.cs:line
187

[2] Tried a ValidationOperationInterceptor class that inherits from
OperationInterceptor and overrides BeforeExecute(IOperation), but the
exception happens before the call to BeforeExecute happens. Tried also
hooking an IPipelineContributor, if I do it After<IRequestDecoding>
(like the OP was doing) obviously it happens the same as when I tried
with an OperationInterceptor, but if I do it Before<IRequestDecoding>
I cannot do a pre-binding to figure out if the binding worked, because
context.PipelineData.Operations.First().Inputs.First().Binder.BuildObject()
always returns a non-successful result, even in the cases where this
exception doesn't happen.


On Jan 10, 5:45 am, Sebastien Lambla <s...@serialseb.com> wrote:
> Ah, yes, if there's an error you may well find that the pipeline is in rendernow mode, as all contributors before OperationResultInvocation are ignored.
>
> At this stage it's probably easier to hook your contrib after result invocation and before response codec selection, that way you still have a chance to update the OperationResult with your custom type.
>
> We should make codec error handling a bit better in future versions :)
>
>
>
>
>
>
>
> -----Original Message-----
> From: open...@googlegroups.com [mailto:open...@googlegroups.com] On Behalf Of Rob
> Sent: 10 January 2011 05:13
> To: OpenRasta
> Subject: [openrasta] Re: How to catch exceptions from codec and pass message out via OperationResult.
>
> Wow fast answer thanks.
> I think I follow what your saying and it makes sense so far.
>
> I have created the following which I believe follows your advice.
> I am still quite new to this aspect so forgive me if i have just plain missed the point.
>
> In Configuration.
> ResourceSpace.Uses.PipelineContributor<OperationDecodingValidator>();
>
> public class OperationDecodingValidator : IPipelineContributor {  public void Initialize(IPipeline pipelineRunner)  {
>
> pipelineRunner.Notify(CheckRequestDecoding).After<KnownStages.IRequestDecod ing>();

Andrés G. Aragoneses

unread,
Jan 12, 2011, 5:27:37 PM1/12/11
to OpenRasta
Nevermind! I think I found how:

pipelineRunner.Notify(CheckRequestDecoding).After<KnownStages.IOperationResultInvocation>();

Now I feel stupid hehe.

Cheers
> OpenRasta.TypeSystem.ReflectionBased.ReflectionExtensions.CreateInstanceFro m[T]
> ...
>
> read more »

Andrés G. Aragoneses

unread,
Jan 12, 2011, 6:12:11 PM1/12/11
to OpenRasta
Replying to myself again... I can intercept the exception in
ServerErrors, but I cannot revert the situation by emptying
ServerErrors and resetting context.OperationResult to the thing I want
to return, because the client keeps getting
"<html><head><title>OpenRasta encountered an error.</title></
head><body><div class="errorList"><dl></dl></div></body></html>", any
ideas?

Thanks again...

On Jan 12, 5:27 pm, Andrés G. Aragoneses <kno...@gmail.com> wrote:
> Nevermind! I think I found how:
>
> pipelineRunner.Notify(CheckRequestDecoding).After<KnownStages.IOperationRes ultInvocation>();
> ...
>
> read more »

Sebastien Lambla

unread,
Jan 12, 2011, 6:12:20 PM1/12/11
to open...@googlegroups.com
That'd be it yes. You may want to make sure you give a full boundary, so After<T>().And.Before<U>() to make sure you get executed in the right place.

Andrés G. Aragoneses

unread,
Jan 13, 2011, 6:36:55 PM1/13/11
to OpenRasta, services...@7digital.com
Yup, now I have the problem that I cannot clean the status of
openRasta after this exception has happened, too complex to restore
everything as if the Pipeline.Abort had never happened.

So I'm trying the following approach (may be too hacky right now but
it may be the start of a negotiation for a better patch on how to
catch and ignore exceptions in the execution of the pipeline):

1) Have this in my configuration code:

{
PipelineRunner.PipelineExceptionInterceptor =
TreatContributorException;
}

bool TreatContributorException(string contributorName, Error error) {
if (contributorName == "OperationHydratorContributor")
return false;
return true;
}

2) Use this patch:

diff --git a/src/core/OpenRasta/Pipeline/PipelineRunner.cs b/src/core/
OpenRasta/Pipeline/PipelineRunner.cs
index 71f8713..4467a79 100644
--- a/src/core/OpenRasta/Pipeline/PipelineRunner.cs
+++ b/src/core/OpenRasta/Pipeline/PipelineRunner.cs
@@ -193,18 +193,27 @@ namespace OpenRasta.Pipeline
}
catch (Exception e)
{
- context.ServerErrors.Add(new Error
- {
- Title = "Fatal error",
- Message = "An exception was thrown while
processing a pipeline contributor",
- Exception = e
- });
- nextStep = PipelineContinuation.Abort;
- }
+ var error = new Error
+ {
+ Title = "Fatal error",
+ Message = "An exception was thrown while processing
a pipeline contributor",
+ Exception = e
+ };
+ if (PipelineExceptionInterceptor == null ||
PipelineExceptionInterceptor (call.ContributorTypeName, error))
+ {
+ context.ServerErrors.Add(error);
+ nextStep = PipelineContinuation.Abort;
+ } else
+ {
+ nextStep = PipelineContinuation.Continue;
+ }
+ }
return nextStep;
}
}

+ public static Func<string, Error, bool>
PipelineExceptionInterceptor { get; set; }
+
protected virtual void FinishPipeline(ICommunicationContext
context)
{
PipelineLog.WriteInfo("Pipeline finished.");


The problem at this point is that, for the parameter of my handler I
don't get a null but an element with default values (if it has an int
property, this property is 0). How could I follow with this approach
to get a null argument for the handler in this case?

Thanks

On Jan 12, 6:12 pm, Sebastien Lambla <s...@serialseb.com> wrote:
> That'd be it yes. You may want to make sure you give a full boundary, so After<T>().And.Before<U>() to make sure you get executed in the right place.
>
>
>
>
>
>
>
> -----Original Message-----
> From: open...@googlegroups.com [mailto:open...@googlegroups.com] On Behalf Of Andrés G. Aragoneses
> Sent: 12 January 2011 17:28
> To: OpenRasta
> Subject: [openrasta] Re: How to catch exceptions from codec and pass message out via OperationResult.
>
> Nevermind! I think I found how:
>
> pipelineRunner.Notify(CheckRequestDecoding).After<KnownStages.IOperationRes ultInvocation>();
> ...
>
> read more »

Sebastien Lambla

unread,
Jan 13, 2011, 7:41:33 PM1/13/11
to open...@googlegroups.com
I'd recommend not messing with OperationHydrator too much, it's a hairy part of the framework. The point is to wrap the codec phase with exception handling so you can recover from that, if you can't from the data in ServerErrors and from the ICommunicationContext, happy to look at code that lets you achieve what you need and pull it in.

For code review, please do send a pull request on github, that way I can comment inline with the code or with the patch request rather than rely on text :)

If you want this included, it may be interesting to note the following. Originally, the plan was to have contributors be skipped when the pipeline was in RenderNow mode. I've suggested adding a marker interface for contributors that still want to be called when rendernow is in place, something along the lines of IMandatoryPipelineContributor or something better named, which the pipeline code would look at before deciding to skip a contributor. Would that solve your problem?

That'd definitely be a patch request I'd be quite glad to get and would fit nicely with the pipeline model without introducing special casing.

Reply all
Reply to author
Forward
0 new messages