You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
@@ -266,14 +266,16 @@ public class B implements ServerInterceptor{
266
266
}
267
267
----
268
268
269
-
The starter uses built-in interceptors to implement Spring `Security`, `Validation` and `Metrics` integration.
269
+
The starter uses built-in interceptors to implement error handling, Spring `Security`, `Validation` and `Metrics` integration.
270
270
Their order can also be controlled by below properties :
271
271
272
-
* `grpc.security.auth.interceptor-order` ( defaults to `Ordered.HIGHEST_PRECEDENCE`)
272
+
* `grpc.security.auth.interceptor-order` ( defaults to `Ordered.HIGHEST_PRECEDENCE+1`)
273
273
* `grpc.validation.interceptor-order` ( defaults to `Ordered.HIGHEST_PRECEDENCE+10`)
274
274
* `grpc.metrics.interceptor-order` ( defaults to `Ordered.HIGHEST_PRECEDENCE+20`)
275
275
276
-
This gives you the ability to setup the desired order of built-in and your custom interceptors.
276
+
This gives you the ability to set up the desired order of built-in and your custom interceptors.
277
+
278
+
Error handling interceptor has the highest precedence.
277
279
278
280
*Keep on reading !!! There is more*
279
281
@@ -450,6 +452,112 @@ If you enable both `NettyServer` and `in-process` servers, the `configure` metho
450
452
If you need to differentiate between the passed `serverBuilder` s, you can check the type. +
451
453
This is the current limitation.
452
454
455
+
== Error handling
456
+
457
+
The starter registers the `GRpcExceptionHandlerInterceptor` which is responsible to propagate the service-thrown exception to the error handlers. +
458
+
The error handling method could be registered by having `@GRpcServiceAdvice` annotated bean with methods annotated with `@GRpcExceptionHandler` annotations. +
459
+
These are considered as `global` error handlers and the method with exception type parameter nearest by the type hierarchy to the thrown exception is invoked. +
460
+
The signature of the error handler has to follow the below pattern:
461
+
462
+
|===
463
+
|Return type |Parameter 1 |Parameter 2
464
+
465
+
|io.grpc.Status
466
+
|any `Exception` type
467
+
|GRpcExceptionScope
468
+
469
+
470
+
471
+
|===
472
+
473
+
474
+
475
+
[source,java]
476
+
.Sample
477
+
----
478
+
@GRpcServiceAdvice
479
+
class MyHandler1{
480
+
@GRpcExceptionHandler
481
+
public Status handle (MyCustomExcpetion exc, GRpcExceptionScope scope){
482
+
483
+
}
484
+
@GRpcExceptionHandler
485
+
public Status handle (IllegalArgumentException exc, GRpcExceptionScope scope){
486
+
487
+
}
488
+
489
+
}
490
+
@GRpcServiceAdvice
491
+
class MyHandler2 {
492
+
@GRpcExceptionHandler
493
+
public Status anotherHandler (NullPointerException npe,GRpcExceptionScope scope){
494
+
495
+
}
496
+
}
497
+
----
498
+
499
+
You can have as many `advice` beans and handler methods as you want as long as they don't interfere with each other and don't create handled exception type ambiguity.
500
+
501
+
The `grpc` service bean is also discovered for error handlers, having the higher precedence than global error handling methods discovered in `@GRpcServiceAdvice` beans. The service-level error handling methods are considered `private` and invoked only when the exception is thrown by *this* service:
502
+
503
+
[source,java]
504
+
.Sample
505
+
----
506
+
class SomeException extends Exception{
507
+
508
+
}
509
+
class SomeRuntimeException extends RuntimeException{
510
+
511
+
}
512
+
513
+
@GRpcService
514
+
public class HelloService extends GreeterGrpc.GreeterImplBase{
515
+
@Override
516
+
public void sayHello(GreeterOuterClass.HelloRequest request, StreamObserver<GreeterOuterClass.HelloReply> responseObserver) {
517
+
...
518
+
throw new GRpcRuntimeExceptionWrapper(new SomeException()) ; <1>
519
+
//or
520
+
throw new GRpcRuntimeExceptionWrapper(new SomeException(),"myHint") <2>
521
+
//or
522
+
throw new SomeRuntimeException() <3>
523
+
}
524
+
@GRpcExceptionHandler
525
+
public Status privateHandler (SomeException npe,GRpcExceptionScope scope){
public Status privateHandler (SomeRuntimeException npe,GRpcExceptionScope scope){
532
+
// INVOKED when thrown from HelloService service
533
+
534
+
}
535
+
}
536
+
@GRpcServiceAdvice
537
+
class MyHandler {
538
+
@GRpcExceptionHandler
539
+
public Status anotherHandler (SomeException npe,GRpcExceptionScope scope){
540
+
// NOT INVOKED when thrown from HelloService service
541
+
}
542
+
@GRpcExceptionHandler
543
+
public Status anotherHandler (SomeRuntimeException npe,GRpcExceptionScope scope){
544
+
// NOT INVOKED when thrown from HelloService service
545
+
}
546
+
547
+
}
548
+
----
549
+
<1> Because the nature of `grpc` service API that doesn't allow throwing checked exception, the special runtime exception type is provided to wrap the checked exception. It's then getting unwrapped when looking for the handler method.
550
+
<2> When throwing the `GRpcRuntimeExceptionWrapper` exception, you can also pass the `hint` object which is then accessible from the `scope` object in `handler` method.
551
+
<3> Runtime exception can be thrown as-is and doesn't need to be wrapped.
552
+
<4> Obtain the hint object.
553
+
<5> Send custom headers to the client.
554
+
555
+
Authentication failure is propagated via `AuthenticationException` and authorization failure - via `AccessDeniedException`.
556
+
557
+
Validation failure is propagated via `ConstraintViolationException`: for failed request - with `Status.INVALID_ARGUMENT` as a hint , and for failed response - with `Status.FAILED_PRECONDITION` as a hint.
558
+
559
+
The demo is link:grpc-spring-boot-starter-demo/src/test/java/org/lognet/springboot/grpc/recovery/GRpcRecoveryTest.java[here]
560
+
453
561
== Implementing message validation
454
562
455
563
Thanks to https://beanvalidation.org/2.0/spec/[Bean Validation] configuration support via https://beanvalidation.org/2.0/spec/#xml[XML deployment descriptor] , it's possible to
@@ -514,7 +622,6 @@ Demo is https://github.com/LogNet/grpc-spring-boot-starter/blob/master/grpc-spri
514
622
515
623
516
624
517
-
By adding `GRpcErrorHandler` bean to your application, you get a chance to send your custom response headers. The error handler will be called with `Status.INVALID_ARGUMENT` and incoming request message that is failed.
518
625
519
626
520
627
== GRPC response observer and Spring @Transactional caveats
@@ -577,6 +684,10 @@ class MyGrpcService extends ...{
577
684
By following this approach you also decouple the transport layer and business logic that now can be tested separately.
578
685
579
686
687
+
688
+
689
+
690
+
580
691
== Spring Security Integration
581
692
582
693
=== Setup
@@ -720,14 +831,6 @@ Starting from `4.5.6`, the `Authentication` object can also be obtained via stan
720
831
final Authentication auth = SecurityContextHolder.getContext().getAuthentication();
721
832
----
722
833
723
-
=== Custom authentication failure handling
724
-
725
-
By adding `GRpcErrorHandler` bean to your application, you get a chance to provide your custom response headers. The error handler will be called with `Status.PERMISSION_DENIED/Status.UNAUTHENTICATED` (and incoming request message , if you set `grpc.security.auth.fail-fast` property to `false`). +
726
-
The demo is link:grpc-spring-boot-starter-demo/src/test/java/org/lognet/springboot/grpc/auth/JwtRoleTest.java[here]
727
-
728
-
729
-
730
-
731
834
732
835
=== Client side configuration support
733
836
@@ -809,11 +912,11 @@ Starting from version `3.3.0`, the starter will auto-register the running grpc s
809
912
810
913
The registered service name will be prefixed with `grpc-` ,i.e. `grpc-${spring.application.name}` to not interfere with standard registered web-service name if you choose to run both embedded `Grpc` and `Web` servers. +
811
914
812
-
Setting `spring.cloud.consul.discovery.register-health-check` to true will register GRPC health check service in Consul.
915
+
Setting `spring.cloud.consul.discovery.register-health-check` to true will register GRPC health check service with Consul.
813
916
814
917
Tags could be set by defining `spring.cloud.consul.discovery.tags` property.
815
918
816
-
There are 3 supported registration modes :
919
+
There are 4 supported registration modes :
817
920
818
921
. `SINGLE_SERVER_WITH_GLOBAL_CHECK` (default) +
819
922
In this mode the running grpc server is registered as single service with single `grpc` check with empty `serviceId`. +
@@ -822,6 +925,7 @@ Please note that default implementation https://github.com/grpc/grpc-java/blob/b
822
925
In this mode the running grpc server is registered as single service with check per each discovered `grpc` service.
823
926
. `STANDALONE_SERVICES` +
824
927
In this mode each discovered grpc service is registered as single service with single check. Each registered service is tagged by its own service name.
928
+
. `NOOP` - no grpc services registered. This mode is usefull if you serve both `rest` and `grpc` services in your application, but for some reason, only `rest` services should be registered with Consul.
825
929
826
930
[source,yml]
827
931
.You can control the desired mode from application.properties
Copy file name to clipboardExpand all lines: grpc-spring-boot-starter/src/main/java/org/lognet/springboot/grpc/recovery/GRpcExceptionHandlerInterceptor.java
0 commit comments