Search code examples
rx-java2micronautrx-kotlin2

How to handle exception when doing request to external service and return status accordingly in Micronaut filter


So I'm completely new to rx-java/rx-kotlin and haven't heard about it until I where to write a HTTP filter in Micronaut (https://docs.micronaut.io/latest/guide/index.html#filters) which caught me off guard since I've only done regular JAX-RS filters.

So after doing some trial-and-error I came up with this.

 return Flowable.fromPublisher(remoteClient.getPermissions(userId))
        .subscribeOn(Schedulers.io())
        .onErrorReturn { e ->
            logger.error("Error when fetching users from remote service", e)
            emptyList()
        }
        .switchMap { permissions ->
            if (permissions.contains(somePermission)) {
                chain.proceed(request)
            } else {
                val response = HttpResponseFactory.INSTANCE.status(
                    HttpStatus.FORBIDDEN,
                    "No user found"
                )
                Flowable.just(response)
            }
        }

Code is rewritten but point still stands. There's some room for improvement here.

What I'd like to do is to improve the exception handling and return a 500 in the case an exception is thrown (like if the remote service called by remoteClient isn't reachable. How can I accomplish this? I haven't got anything else to work other than the solution above to return an empty list for the next observer.


Solution

  • You can move the onErrorReturn operator after the switchMap operator. If getPermissions emits an error, execution will skip your switchMap, and pick up inside of onErrorReturn:

     return Flowable.fromPublisher(remoteClient.getPermissions(userId))
            .subscribeOn(Schedulers.io())
            .switchMap { permissions ->
                if (permissions.contains(somePermission)) {
                    chain.proceed(request)
                } else {
                    val response = HttpResponseFactory.INSTANCE.status(
                        HttpStatus.FORBIDDEN,
                        "No user found"
                    )
                    Flowable.just(response)
                }
            }
            .onErrorReturn { e ->
                logger.error("Error when fetching users from remote service", e)
                val response = // whatever
                return response
            }
    

    Note that an exception occurring inside of switchMap will also result in onErrorReturn getting executed. So you either want to examine the Exception that is passed into onErrorReturn to decide what response to use, or embed an onErrorReturn inside of switchMap, chained to the chain.proceed(request) call, to prevent it from emitting an exception downstream.