Set the HTTP status code from an IOperationInterceptor in OpenRasta
I've written an ExceptionInterceptor
that wraps the operation invocation in a try
/catch
to have centralized exception handling. What I also want to do is have centralized HTTP status code handling, but I seem to be unable to set the HTTP status code from within my IOperationInterceptor
. My Interceptor takes ICommunicationContext
as a dependency and se开发者_JAVA技巧ts its OperationResult
to e.g. OperationResult.BadRequest
, but OpenRasta still writes the following to the log:
Step into: Stepping over non-user code 'OpenRasta.Pipeline.PipelineRunner.RunCallGraph'
38-[2011-07-08 09:11:37Z] Start(1) Entering PipelineRunner: Executing contributor OperationResultInvokerContributor.RunOperationResult
38-[2011-07-08 09:11:37Z] Information(0) Executing OperationResult OperationResult: type=OK, statusCode=200.
I've also tried setting the IResponse.StatusCode
explicitly, but it has no effect. It seems that because the ResponseResource I want to respond with is a valid resource registered in ResourceSpace
(though without a URI), the status code is ignored and the regular rendering pipeline thrusts forward thinking "this looks okay".
Why is OpenRasta ignoring my StatusCode?
After some more digging I figured out that you can't override the HTTP status code directly from an IOperationInterceptor
. Instead, you need to store whatever data you need in the ICommunicationContext.PipelineData
collection from wherever makes sense in your IOperationInterceptor
implementation. I, for instance, have the following BeforeExecute
implementation:
public bool BeforeExecute(IOperation operation)
{
if (operation.Inputs.Count() > 0)
this.inputMember = operation.Inputs.First();
return true;
}
Setting the inputMember in BeforeExecute
allows me to retrieve the entity (received via HTTP POST or PUT) via this.inputMember.Binder.BuildObject().Instance
as well as yielding a new OutputMember
in the RewriteOperation
method.
When you have stored all the data you want inside ICommunicationContext.PipelineData
you can then go on to handle stored data and respond accordingly inside an IPipelineContributor
implementation. You do this by subscribing to the After<KnownStages.IOperationExecution>()
event like so:
public void Initialize(IPipeline pipelineRunner)
{
pipelineRunner
.Notify(RenderOnException)
.After<KnownStages.IOperationExecution>();
}
The RenderOnException
method looks like this:
private static PipelineContinuation RenderOnException(ICommunicationContext context)
{
if (!context.PipelineData.ContainsKey(ExceptionInterceptor.Key))
return PipelineContinuation.Continue;
var interestingDataStoredByTheOperationInterceptorImplementation =
context.PipelineData["SomeKey"];
// Set context.OperationResult to something meaningful.
// Instruct the pipeline to render now
return PipelineContinuation.RenderNow;
}
While this might not be the best way to implement this, it works and ends up making the individual Handler methods thin and focused on the problem they are solving. Instead of returning OperationResult
s left and right, Handler methods can now throw regular exceptions that my IOperationInterceptor
implementation will catch and then my IPipelineContributor
implementation can override the OperationResult
with an appropriate response.
精彩评论