There are two main interfaces for implementing a middleware:
Methods ugrpc::server::MiddlewareBase::OnCallStart and ugrpc::server::MiddlewareBase::OnCallFinish are called once per grpc Call (RPC).
OnCallStart is called after the client metadata is received. OnCallFinish is called before the last message is sent or before error status is sent to a client.
OnCallStart hooks are called in the order of middlewares. OnCallFinish hooks are called in the reverse order of middlewares
Register the component.
The static YAML config.
You can add some behavior on each request/response. Especially, it can be important for grpc-stream. See about streams in gRPC.
PostRecvMessage hooks are called in the direct middlewares order. PreSendMessage hooks are called in the reversed order.
For more information about the middlewares order:
The static YAML config.
Register the middleware component in the component system.
We use a simple short-cut ugrpc::server::SimpleMiddlewareFactoryComponent in the example above. To declare static config options of your middleware see gRPC middlewares configuration.
To fully understand what happens when middlewares hooks fail, you should understand the middlewares order:
If you want to interrupt a Call (RPC) in middlewares, you should use ugrpc::server::MiddlewareCallContext::SetError (see examples above on this page).
If you throw an exception in middlewares hooks, that exception will be translated to grpc::Status (by default grpc::StatusCode::UNKNOWN) and next hooks won't be called. server::handlers::CustomHandlerException is translated to a relevant grpc::Status.
All errors will be logged just like an exception or error status from the user handler:
error_msg tag.SetError.ugrpc::server::MiddlewareBase::OnCallFinish will be called despite of any errors.
The actual status is passed to OnCallFinish hooks. Each OnCallFinish hook gets the status from a previous OnCallFinish call and can change that by SetError (or exception). An error status from a handler will be passed to a first OnCallFinish and that hook can change that status, next hooks will get the new status. If all OnCallFinish hooks don't change the status, that status will be the final status for a client.
There are 3 middlewares A, B, C. Cases:
A::OnCallStart and B::OnCallStart succeed, but C::OnCallStart fails (by SetError or exception) ⇒ B::OnCallFinish and A::OnCallFinish will be called (remember that OnCallFinish order is reversed).OnCallStart succeed and C::OnCallFinish fails, B::OnCallFinish and A::OnCallFinish will be called and these hooks get an error status from C::OnCallFinish.OnCallFinish will be called.PostRecvMessage/PreSendMessage ⇒ RPC is failed ⇒ all OnCallFinish hooks will be called.There are two ways to implement a middleware component. You can see above ugrpc::server::SimpleMiddlewareFactoryComponent. This component is needed for simple cases without static config options of a middleware.
kName and kDependency (middlewares::MiddlewareDependencyBuilder) must be in a middleware class (as shown above).If you want to use static config options for your middleware, use ugrpc::server::MiddlewareFactoryComponentBase.
To override static config options of a middleware per a server see grpc_middlewares_config_override.
Before starting to read specifics of server middlewares ordering:
There are simple cases above: we just set Auth group for one middleware and use a default constructor of MiddlewareDependencyBuilder in other middleware. Here we say that all server middlewares are located in these groups.
PreCore group is called firstly, then Logging and so forth...