ServerInterceptor lifecycle for a bidirectional streaming API - grpc

I have ServerInterceptor implementation as follows:
public class MyInterceptor implements ServerInterceptor {
public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(
final ServerCall<ReqT, RespT> serverCall,
final Metadata headers,
final ServerCallHandler<ReqT, RespT> serverCallHandler) {
System.out.println("Call started");
return new ForwardingServerCallListener.SimpleForwardingServerCallListener<ReqT>(serverCallHandler.startCall(serverCall, headers)) {
public void onMessage(ReqT message) {
System.out.println("onMessage");
super.onMessage(message);
}
public void onCancel() {
System.out.println("onCancel");
super.onCancel();
}
public void onComplete() {
System.out.println("onComplete");
super.onComplete();
};
}
}
I am expecting that for a bidirectional streaming API call, I'll see one "Call Started", multiple "onMessage" (depending on the number of requests in the stream) and one of either "onComplete" or "onCancel".
Similarly for unary API call, I am expecting one each of "Call Started", "onMessage" and "onComplete/onCancel" per call.
Is that correct?

That's correct.
The event order of method you can reference this picture:
You can read this note for more detail: https://helloworlde.github.io/2021/02/20/gRPC-%E6%9C%8D%E5%8A%A1%E9%97%B4%E8%B0%83%E7%94%A8%E4%BA%8B%E4%BB%B6%E6%B5%81%E7%A8%8B/

Related

Use .net core middleware with .Map configured to a specific path

IApplicationBuilder has a .Map method that all the examples online show you how to use when you are inlining your middleware configuration and using .Run or .Use directly.
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/middleware/?view=aspnetcore-2.2#use-run-and-map
Most of the examples then go on to say how this is a bad idea (rightly so for maintenance reasons alone) and show you how to make a middle ware component which looks something like this:
public class CustomMiddleware: IMiddleware
{
public async Task InvokeAsync(HttpContext context, RequestDelegate next)
{
... do stuff.
await next(context);
}
}
// Extension method used to add the middleware to the HTTP request pipeline.
public static class CustomMiddlewareExtensions
{
public static IApplicationBuilder UseCustomMiddleware(this IApplicationBuilder builder)
{
return builder.UseMiddleware<CustomMiddleware>();
}
}
What I can't see is how to combine the two. One thing I tried was this:
public static class CustomMiddlewareExtensions
{
public static IApplicationBuilder UseCustomMiddleware(this IApplicationBuilder builder)
{
return builder.Map("/CustomPath", app => {
app.UseMiddleware<CustomMiddleware>(); });
}
}
But this is apparently not working as it is calling InvokeAsync for everything.
I could easily look at the HttpContext.Request.Path and skip my do stuff if it does not match but wanted to know if it was possible to use .Map before doing so.
The code you provided in working as it should and yielding the result you desire; InvokeAsync is called only when the current request path starts with the one provided to Map -I'm not sure if this small detail is what might be causing the issue i.e. /CustomPath/anything will match and cause Map to invoke the different pipeline-.
For example using the following code in a new template:
Map will match /home, /home/privacy, /home/values etc.
Map won't match /anything_not_starting_with_home, and InvokeAsync won't be called.
public static class CustomMiddlewareExtensions {
public static IApplicationBuilder UseCustomMiddleware(this IApplicationBuilder appBuilder) {
appBuilder.Map("/home", b => {
b.UseMiddleware<CustomMiddleware1>();
b.UseMiddleware<CustomMiddleware2>();
});
return appBuilder;
}
public class CustomMiddleware1 {
private readonly RequestDelegate _next;
public CustomMiddleware1(RequestDelegate next) {
_next = next;
}
public async Task InvokeAsync(HttpContext context) {
Debug.WriteLine($" ======== Response handled by {nameof(CustomMiddleware1)} ======== ");
await _next.Invoke(context);
}
}
public class CustomMiddleware2 {
private readonly RequestDelegate _next;
public CustomMiddleware2(RequestDelegate next) {
_next = next;
}
public async Task InvokeAsync(HttpContext context) {
Debug.WriteLine($" ======== Response handled by {nameof(CustomMiddleware2)} ======== ");
await context.Response.WriteAsync("Custom Middleware used");
}
}
}

get message from gRPC java server side interceptor

I am writing a gRPC server side interceptor in java. I simplely want to access the request message and read one field from the message. But after 1 day of googling and talking with others, I couldn't find how to do it.
Any idea how to do it?
import io.grpc.Metadata;
import io.grpc.ServerCall;
import io.grpc.ServerCallHandler;
import io.grpc.ServerInterceptor;
public class EventInterceptor implements ServerInterceptor {
#Override
public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(
ServerCall<ReqT, RespT> serverCall,
Metadata metadata,
ServerCallHandler<ReqT, RespT> serverCallHandler) {
// TODO: Get the request message here
return serverCallHandler.startCall(serverCall, metadata);
}
}
The request message arrives later. You need to return your own listener to observe the message.
import io.grpc.ForwardingServerCallListener.SimpleForwardingServerCallListener;
public class EventInterceptor implements ServerInterceptor {
#Override
public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(
ServerCall<ReqT, RespT> serverCall,
Metadata metadata,
ServerCallHandler<ReqT, RespT> serverCallHandler) {
ServerCall.Listener listener = serverCallHandler.startCall(serverCall, metadata);
return new SimpleForwardingServerCallListener<ReqT>(listener) {
#Override public void onMessage(ReqT req) {
// You now have access to the request(s)
doWork(req);
super.onMessage(req);
}
};
}
}

.NET Core, SignalR Hub's constructor IHubCallerClients is NULL

I'm trying to implement .NET Core 2.2/SignalR 1.1.0.
In startup:
public void ConfigureServices(IServiceCollection services)
services.AddSignalR();
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
app.UseSignalR(routes =>
{
routes.MapHub<ChatHub>("/chatHub");
});
It works smoothly when I apply a one-to-one example.
But I need an architectural change.
My example:
public class ChatHub : Hub
{
ResponseHandler ResponseHandler { get; set; }
public ChatHub()
{
IHubCallerClients hubCallerClients = this.Clients;
ResponseHandler = new ResponseHandler(hubCallerClients);
}
public async Task SendMessage(string user, string message)
{
IHubCallerClients hubCallerClients = this.Clients;
await ResponseHandler.R();
}
}
If I tried to get this.Clients in the constructor it is coming with null data. But if I try to take it in the method, it comes full as expected.
I should get IHubCallerClients in the contructor so that I can forward it to another Response context.
Thanks advance!
OK. I solved the problem by
public class RequestHandler : Hub
{
ResponseHandler ResponseHandler { get; set; }
public RequestHandler(IHubContext<RequestHandler> hubContext)
{
ResponseHandler = new ResponseHandler(hubContext);
}
public async Task SendMessage(string user, string message)
{
await ResponseHandler.R();
}
}
Due to the nature of .net core, context comes to constructor as dependency.
"services.AddSignalR();" we're sure to add it to Scope.
"IHubContext hubContext" In this way, we can collect the contructured object.

GRPC Interceptor - Modify message by Number indicator's

I'm learing about grpc service in java and I'm trying to figure out if it is possible to intercept and modify a request.
I thought of modify the grpc request based on the number specified in the protobuf.
Sample Protobuf:
message PersonRequest {
string name = 1;
int32 id = 2;
bool has_ponycopter = 3;
}
#Override
public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall
(ServerCall<ReqT, RespT> call, Metadata headers, ServerCallHandler<ReqT, RespT> next) {
// Get by number 1 (name from proto)
// change the name
// send the updated request
}
Is there any approach available to do this.
You can modify the request message in the following server interceptor. Then your serviceImpl will receive the modified request message and process it.
import io.grpc.ForwardingServerCallListener.SimpleForwardingServerCallListener;
...
#Override
public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(
ServerCall<ReqT, RespT> call, Metadata headers,
ServerCallHandler<ReqT, RespT> next) {
ServerCall.Listener<ReqT> listener = next.startCall(call);
return new SimpleForwardingServerCallListener<ReqT>(listener) {
#Override
public void onMessage(ReqT message) {
ReqT modifiedMessage = modify(message);
delegate().onMessage(modifiedMessage);
}
};
}

Return 404 for every null response

I'd like to return 404 when the response object is null for every response automatically in spring boot.
I need suggestions.
I don't want to check object in controller that it is null or not.
You need more than one Spring module to accomplish this. The basic steps are:
Declare an exception class that can be used to throw an exception when a repository method does not return an expected value.
Add a #ControllerAdvice that catches the custom exception and translates it into an HTTP 404 status code.
Add an AOP advice that intercepts return values of repository methods and raises the custom exception when it finds the values not matching expectations.
Step 1: Exception class
public class ResourceNotFoundException extends RuntimeException {}
Step 2: Controller advice
#ControllerAdvice
public class ResourceNotFoundExceptionHandler
{
#ExceptionHandler(ResourceNotFoundException.class)
#ResponseStatus(HttpStatus.NOT_FOUND)
public void handleResourceNotFound() {}
}
Step 3: AspectJ advice
#Aspect
#Component
public class InvalidRepositoryReturnValueAspect
{
#AfterReturning(pointcut = "execution(* org.example.data.*Repository+.findOne(..))", returning = "result")
public void intercept(final Object result)
{
if (result == null)
{
throw new ResourceNotFoundException();
}
}
}
A sample application is available on Github to demonstrate all of this in action. Use a REST client like Postman for Google Chrome to add some records. Then, attempting to fetch an existing record by its identifier will return the record correctly but attempting to fetch one by a non-existent identifier will return 404.
Simplest way to do this in Spring is write your own exception class like below
#ResponseStatus(value = HttpStatus.NOT_FOUND)
class ResourceNotFoundException extends RuntimeException{
}
Then just throw the ResourceNotFoundException from anywhere.
if (something == null) throw new ResourceNotFoundException();
For more -> Read
Similar to #manish's answer (https://stackoverflow.com/a/43891952/986160) but without the AspectJ pointcut and using another #ControllerAdvice instead:
Step 1: NotFoundException class:
public class NotFoundException extends RuntimeException {
public NotFoundException(String msg) {
super(msg);
}
public NotFoundException() {}
}
Step 2: Check if body returned in endpoint is null and throw NotFoundException:
#ControllerAdvice
public class NotFoundAdvice implements ResponseBodyAdvice {
#Override
public boolean supports(MethodParameter returnType, Class converterType) {
return true;
}
#Override
#SuppressWarnings("unchecked")
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
if (body == null) {
throw new NotFoundException("resource not found");
}
return body;
}
}
Step 3: handle NotFoundException and make the response have a status of 404
#ControllerAdvice
public class GlobalExceptionAdvice {
#Data
public class ErrorDetails {
private Date timestamp;
private String message;
private String details;
public ErrorDetails(Date timestamp, String message, String details) {
super();
this.timestamp = timestamp;
this.message = message;
this.details = details;
}
}
#ExceptionHandler(NotFoundException.class)
public final ResponseEntity<ErrorDetails> notFoundHandler(Exception ex, WebRequest request) {
ErrorDetails errorDetails = new ErrorDetails(new Date(), ex.getMessage(),
request.getDescription(false));
return new ResponseEntity<>(errorDetails, HttpStatus.NOT_FOUND);
}
}
Alternative to Step 3:
You can just annotate your NotFoundException with #ResponseStatus and override fillInStackTrace() (from https://stackoverflow.com/a/31263942/986160) so that it has similar effect to GlobalExceptionAdvice and doesn't show stacktrace like this:
#ResponseStatus(value = HttpStatus.NOT_FOUND,reason = "resource not found")
public class NotFoundException extends RuntimeException {
public NotFoundException(String msg) {
super(msg);
}
public NotFoundException() {}
#Override
public synchronized Throwable fillInStackTrace() {
return this;
}
}
There is no need to throw exceptions, now ResponseBodyAdvice does the trick:
#ControllerAdvice
public class NullTo404 implements ResponseBodyAdvice<Object> {
#Override
public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
return true;
}
#Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request,
ServerHttpResponse response) {
if (body == null) {
response.setStatusCode(HttpStatus.NOT_FOUND);
}
return body;
}
}
Similarly, you can implement ResponseBodyAdvice<Optional<?>>, and check for Optional.isEmpty() before setting the response status. It has the added benefit of working nicely with CrudRepository. Most controller methods eventually ends like this:
public Optional<Product> getProductBySku(#PathVariable String sku) {
// logic goes here...
return productRepository.findBySku(sku);
}

Resources