Middleware

3 min read

3 min read

3 min read

3 min read

2 min read

2 min read

2 min read

2 min read

3 min read

A middleware is a piece of code that intercepts an incoming request before this request reaches the target servant. The same code also intercepts the outgoing response before it's sent back to the caller.

At a more technical level, a middleware is a dispatcher that holds another dispatcher ("next") and calls dispatch on this next dispatcher as part of the implementation of its own dispatch method.

A middleware can include logic before calling dispatch on the next dispatcher (before the request is processed) and after calling dispatch on the next dispatcher (after it receives the response). A middleware can also short-circuit the dispatch processing by returning a cached response or by returning an error.

In C++, a middleware is a concrete class that implements Ice::Object and delegates to another dispatcher called “next”. For example:

C++
// A typical C++ middleware class
class AuthorizationMiddleware final : public Ice::Object
{
public:
AuthorizationMiddleware(Ice::ObjectPtr next, std::string validToken);
void dispatch(
Ice::IncomingRequest& request,
std::function<void(Ice::OutgoingResponse)> sendResponse) final;
};

The constructor accepts the “next” dispatcher and other data, and dispatch dispatches incoming requests by delegating to “next”.

You install a middleware on an object adapter by calling use:

C++
class ObjectAdapter
{
public:
ObjectAdapterPtr use(std::function<ObjectPtr(ObjectPtr)> middlewareFactory);
};

use accepts a middleware factory – not a middleware. This allows the object adapter to create and connect the middleware into its dispatch pipeline when it receives its first request.

For example, you can call use as follows:

C++
adapter->use(
[](Ice::ObjectPtr next)
{
return make_shared<Server::AuthorizationMiddleware>(
std::move(next),
"iced tea");
});

The middleware, once created and woven into the dispatch pipeline, intercept requests in the order of their registration through use.

In C#, a middleware is a concrete class that implements Ice.Object and delegates to another dispatcher called “next”. For example:

C#
// A typical C# middleware class
internal class AuthorizationMiddleware : Ice.Object
{
public ValueTask<Ice.OutgoingResponse> dispatchAsync(
Ice.IncomingRequest request)
{
...
}
internal AuthorizationMiddleware(Ice.Object next, string validToken)
{
...
}
}

The constructor accepts the “next” dispatcher and other data, and dispatch dispatches incoming requests by delegating to “next”.

You install a middleware on an object adapter by calling use:

C#
public sealed class ObjectAdapter
{
public ObjectAdapter use(Func<Object, Object> middleware)
{
...
}
}

use accepts a middleware factory – not a middleware. This allows the object adapter to create and connect the middleware into its dispatch pipeline when it receives its first request.

For example, you can call use as follows:

C#
adapter.use(
next => new Server.AuthorizationMiddleware(next, validToken: "iced tea"));

The middleware, once created and woven into the dispatch pipeline, intercept requests in the order of their registration through use.

In Java, a middleware is a concrete class that implements Ice.Object and delegates to another dispatcher called “next”. For example:

Java
// A typical Java middleware class
class AuthorizationMiddleware implements com.zeroc.Ice.Object
{
public CompletionStage<OutgoingResponse> dispatch(
IncomingRequest request) throws UserException {
...
}
AuthorizationMiddleware(com.zeroc.Ice.Object next, String validToken) {
...
}
}

The constructor accepts the “next” dispatcher and other data, and dispatch dispatches incoming requests by delegating to “next”.

You install a middleware on an object adapter by calling use:

Java
public final class ObjectAdapter {
public ObjectAdapter use(Function<Object, Object> middleware) {
}
}

use accepts a middleware factory – not a middleware. This allows the object adapter to create and connect the middleware into its dispatch pipeline when it receives its first request.

For example, you can call use as follows:

Java
adapter.use(next -> new AuthorizationMiddleware(next, "iced tea"));

The middleware, once created and woven into the dispatch pipeline, intercept requests in the order of their registration through use.

In JavaScript, a middleware is a concrete class that implements Ice.Object and delegates to another dispatcher called “next”. For example:

JavaScript
// A typical JavaScript middleware class
class AuthorizationMiddleware extends Ice.Object
{
async dispatch(request) {
...
}
constructor(next, validToken) {
...
}
}
TypeScript
// A typical TypeScript middleware class
class AuthorizationMiddleware extends Ice.Object
{
override async dispatch(
request: Ice.IncomingRequest): Promise<Ice.OutgoingResponse> {
...
}
constructor(next: Ice.Object, validToken: string) {
...
}
}

The constructor accepts the “next” dispatcher and other data, and dispatch dispatches incoming requests by delegating to “next”.

You install a middleware on an object adapter by calling use:

JavaScript
export class ObjectAdapter {
use(middleware) {
...
}
}
TypeScript
interface ObjectAdapter {
use(middleware: (next: Ice.Object) => Ice.Object): ObjectAdapter;
}

use accepts a middleware factory – not a middleware. This allows the object adapter to create and connect the middleware into its dispatch pipeline when it receives its first request.

For example, you can call use as follows:

JavaScript
adapter.use(next => new AuthorizationMiddleware(next, "iced tea"));
TypeScript
adapter.use((next: Ice.Object) => new AuthorizationMiddleware(next, "iced tea"));

The middleware, once created and woven into the dispatch pipeline, intercept requests in the order of their registration through use.

In Swift, a middleware is a type that implements Ice.Dispatcher protocol and delegates to another dispatcher called “next”. For example:

Swift
// A typical Swift middleware class
final class AuthorizationMiddleware: Dispatcher {
func dispatch(
_ request: sending IncomingRequest) async throws -> OutgoingResponse {
...
}
init(next: Dispatcher, validToken: String) {
...
}
}

The constructor accepts the “next” dispatcher and other data, and dispatch dispatches incoming requests by delegating to “next”.

You install a middleware on an object adapter by calling use:

Swift
public protocol ObjectAdapter: AnyObject, Sendable {
@discardableResult
func use(_ middlewareFactory: @escaping (
_ next: Dispatcher) -> Dispatcher) -> Self
}

use accepts a middleware factory – not a middleware. This allows the object adapter to create and connect the middleware into its dispatch pipeline when it receives its first request.

For example, you can call use as follows:

Swift
adapter.use {
next in AuthorizationMiddleware(next: next, validToken: "iced tea")
}

The middleware, once created and woven into the dispatch pipeline, intercept requests in the order of their registration through use.

Ice installs automatically the following middleware at the beginning of the dispatch pipeline:

  • the logger middleware, unless both Ice.Warn.Dispatch and Ice.Trace.Dispatch are set to 0.
  • the observer middleware, when the Metrics admin facet is enabled.