Play! Framework - Can my view template be localised when rendering it as an AsyncResult? - internationalization

I've recently started using the Play! framework (v2.0.4) for writing a Java web application. In the majority of my controllers I'm following the paradigm of suspending the HTTP request until the promise of a web service response has been fulfilled. Once the promise has been fulfilled, I return an AsyncResult. This is what most of my actions look like (with a bunch of code omitted):
public static Result myActionMethod() {
Promise<MyWSResponse> wsResponse;
// Perform a web service call that will return the promise of a MyWSResponse...
return async(wsResponse.map(new Function<MyWSResponse, Result>() {
#Override
public Result apply(MyWSResponse response) {
// Validate response...
return ok(myScalaViewTemplate.render(response.data()));
}
}));
}
I'm now trying to internationalise my app, but hit the following error when I try to render a template from an async method:
[error] play - Waiting for a promise, but got an error: There is no HTTP Context available from here.
java.lang.RuntimeException: There is no HTTP Context available from here.
at play.mvc.Http$Context.current(Http.java:27) ~[play_2.9.1.jar:2.0.4]
at play.mvc.Http$Context$Implicit.lang(Http.java:124) ~[play_2.9.1.jar:2.0.4]
at play.i18n.Messages.get(Messages.java:38) ~[play_2.9.1.jar:2.0.4]
at views.html.myScalaViewTemplate$.apply(myScalaViewTemplate.template.scala:40) ~[classes/:na]
at views.html.myScalaViewTemplate$.render(myScalaViewTemplate.template.scala:87) ~[classes/:na]
at views.html.myScalaViewTemplate.render(myScalaViewTemplate.template.scala) ~[classes/:na]
In short, where I've got a message bundle lookup in my view template, some Play! code is attempting to access the original HTTP request and retrieve the accept-languages header, in order to know which message bundle to use. But it seems that the HTTP request is inaccessible from the async method.
I can see a couple of (unsatisfactory) ways to work around this:
Go back to the 'one thread per request' paradigm and have threads block waiting for responses.
Figure out which language to use at Controller level, and feed that choice into my template.
I also suspect this might not be an issue on trunk. I know that there is a similar issue in 2.0.4 with regards to not being able to access or modify the Session object which has recently been fixed. However I'm stuck on 2.0.4 for the time being, so is there a better way that I can resolve this problem?

Gonna answer my own question here. A colleague of mine found what was ultimately a simple solution:
public static Result myActionMethod() {
final Context ctx = ctx(); // (1)
Promise<MyWSResponse> wsResponse;
// Perform a web service call that will return the promise of a MyWSResponse...
return async(wsResponse.map(new Function<MyWSResponse, Result>() {
#Override
public Result apply(MyWSResponse response) {
Context.current.set(ctx); // (2)
// Validate response...
return ok(myScalaViewTemplate.render(response.data()));
}
}));
}
Obtain a reference to the HTTP context at the beginning of the action
Restore it in the ThreadLocal once you're in the async block

Related

NestJS handle HttpModule errors

NestJS has the following example for using their HttpModule:
#Injectable()
export class CatsService {
constructor(private readonly httpService: HttpService) {}
findAll(): Observable<AxiosResponse<Cat[]>> {
return this.httpService.get('http://localhost:3000/cats');
}
}
My question is, how does the client code (most likely a Controller) handle this response? How does it treat Observables so that Cat[] may be accessed. Or what if the Http request throws an error such as a 404.
How does a NestJS client (Controller) in this case interact with the findAll() method provided by the service?
I am not too familiar with NestJs, but if you want to run an observable http request generally you do the following to consume and catchError
this.catService.findAll().pipe(
// axio wraps the result in data
map(res=>res.data),
catchError(e=>{
...handle error 404
return of(e)
})).subscribe()
If, for whatever reason, the URL you provide to the HttpService returns a 404, that error will propagate back through the service, to the controller, and eventually to the client that called the original URL. Under the hood, NestJS will subscribe to all returned Observables so that you don't need to worry about it, you can just return the call directly from your Controller. So in the example above, say we have a controller that looks like this:
#Controller('cats')
export class CatsController {
constructor(private readonly catsService: CatsService) {}
#Get()
findAllCats(): Observable<Cat[]> {
return this.catsService.findAll();
}
}
And CatsService looks like this
#Injectable()
export class CatsService {
constructor(private readonly httpService: HttpService) {}
findAll(): Observable<Cat[]> {
return this.httpService.get('http://localhost:3000/cats');
}
}
Assuming you are calling off to another server (i.e. this server is not running on port 3000) and /cats is not a valid endpoint and that server returns a 404 to you, the httpService's response will bubble up through the CatsService to the CatsController where NestJS will handle the subscription, and send the response back to the client. If you are looking to do some custom error handling, that will need to be handled in a different way. A great way to test how the HttpService responds to things is to create a simple endpoint and call off to a bad URL (like https://google.com/item which is a 404)

Mono returned by ServerRequest.bodyToMono() method not extracting the body if I return ServerResponse immediately

I am using web reactive in spring web flux. I have implemented a Handler function for POST request. I want the server to return immediately. So, I have implemeted the handler as below -:
public class Sample implements HandlerFunction<ServerResponse>{
public Mono<ServerResponse> handle(ServerRequest request) {
Mono bodyMono = request.bodyToMono(String.class);
bodyMono.map(str -> {
System.out.println("body got is " + str);
return str;
}).subscribe();
return ServerResponse.status(HttpStatus.CREATED).build();
}
}
But the print statement inside the map function is not getting called. It means the body is not getting extracted.
If I do not return the response immediately and use
return bodyMono.then(ServerResponse.status(HttpStatus.CREATED).build())
then the map function is getting called.
So, how can I do processing on my request body in the background?
Please help.
EDIT
I tried using flux.share() like below -:
Flux<String> bodyFlux = request.bodyToMono(String.class).flux().share();
Flux<String> processFlux = bodyFlux.map(str -> {
System.out.println("body got is");
try{
Thread.sleep(1000);
}catch (Exception ex){
}
return str;
});
processFlux.subscribeOn(Schedulers.elastic()).subscribe();
return bodyFlux.then(ServerResponse.status(HttpStatus.CREATED).build());
In the above code, sometimes the map function is getting called and sometimes not.
As you've found, you can't just arbitrarily subscribe() to the Mono returned by bodyToMono(), since in that case the body simply doesn't get passed into the Mono for processing. (You can verify this by putting a single() call in that Mono, it'll throw an exception since no element will be emitted.)
So, how can I do processing on my request body in the background?
If you really still want to just use reactor to do a long task in the background while returning immediately, you can do something like:
return request.bodyToMono(String.class).doOnNext(str -> {
Mono.just(str).publishOn(Schedulers.elastic()).subscribe(s -> {
System.out.println("proc start!");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("proc end!");
});
}).then(ServerResponse.status(HttpStatus.CREATED).build());
This approach immediately publishes the emitted element to a new Mono, set to publish on an elastic scheduler, that is then subscribed in the background. However, it's kind of ugly, and it's not really what reactor is designed to do. You may be misunderstanding the idea behind reactor / reactive programming here:
It's not written with the idea of "returning a quick result and then doing stuff in the background" - that's generally the purpose of a work queue, often implemented with something like RabbitMQ or Kafka. It's "raison d'ĂȘtre" is instead to be non-blocking, so a single thread is never idly blocked, waiting for something else to complete.
The map() method isn't designed for side effects, it's designed to transform each object into another. For side effects, you want doOnNext() instead;
Reactor uses a single thread by default, so your "additional processing" in your map() method would still block that thread.
If your application is for anything more than quick demo purposes, and/or you need to make heavy use of this pattern, then I'd seriously consider setting up a proper work queue instead.
This is not possible.
Web servers (including Reactor Netty, Tomcat, etc) clean up and recycle resources when request processing is done. This means that when your controller handler is done, the HTTP resources, the request itself, reusable buffers, etc are recycled or closed. At that point, you cannot read from the request body anymore.
In your case, you need to read and buffer the whole request body first, then return a response and kick off a task for processing that request in a separate execution.

Can I access the request/response body on an ExchangeFilterFunction?

Given an exchange using WebClient, filtered by a custom ExchangeFilterFunction:
#Override
public Mono<ClientResponse> filter(ClientRequest request, ExchangeFunction next) {
return next.exchange(request)
.doOnSuccess(response -> {
// ...
});
}
Trying to access the response body more than once using response.bodyToMono() will cause the underlying HTTP client connector to complain that only one receiver is allowed. AFAIK, there's no way to access the body's Publisher in order to cache() its signals (and I'm not sure it'd be a good idea, resource-wise), as well as no way to mutate or decorate the response object in a manner that allows access to its body (like it's possible with ServerWebExchange on the server side).
That makes sense, but I am wondering if there are any ways I could subscribe to the response body's publisher from a form of filter such as this one. My goal is to log the request/response being sent/received by a given WebClient instance.
I am new to reactive programming, so if there are any obvious no-nos here, please do explain :)
Only for logging you could add a wiretap to the HttpClient as desribed in this answer.
However, your question is also interesting in a more general sense outside of logging.
One possible way is to create a duplicate of the ClientResponse instance with a copy of the previous request body. This might go against reactive principles, but it got the job done for me and I don't see big downsides given the small size of the response bodies in my client.
In my case, I needed to do so because the server sending the request (outside of my control) uses the HTTP status 200 Ok even if requests fail. Therefore, I need to peek into the response body in order to find out if anything went wrong and what the cause was. In my case I evict a session cookie in the request headers from the cache if the error message indicates that the session expired.
These are the steps:
Get the response body as a Mono of a String (cf (1)).
Return a Mono.Error in case an error is detected (cf (2)).
Use the String of the response body to build a copy of the original response (cf (3)).
You could also use a dependency on the ObjectMapper to parse the String into an object for analysis.
Note that I wrote this in Kotlin but it should be easy enough to adapt to Java.
#Component
class PeekIntoResponseBodyExchangeFilterFunction : ExchangeFilterFunction {
override fun filter(request: ClientRequest, next: ExchangeFunction): Mono<ClientResponse> {
return next.exchange(request)
.flatMap { response ->
// (1)
response.bodyToMono<String>()
.flatMap { responseBody ->
if (responseBody.contains("Error message")) {
// (2)
Mono.error(RuntimeException("Response contains an error"))
} else {
// (3)
val clonedResponse = response.mutate().body(responseBody).build()
Mono.just(clonedResponse)
}
}
}
}
}

Returning error from SignalR server method

I am new to SignalR and there is a small detail I can't get my head around.
My SignalR hub include many channels and the clients can join one or many of these channels via a server method:
joinChannel(string channelName)
What I don't understand is what this method should return.
If it were a normal "RPC" method I would return a status (200 - Ok, 404 - Not found, 403 - Forbidden etc) via IHttpActionResult.
How do I indicate success/failure in SignalR?
What determines if the reply gets to .done or .fail in the client?
Update
Currently my method returns a non-zero value in case of error.
int joinChannel(string channelName) {
...
return errorCode;
}
This works but it create unnecessarily complicated code in the client
hubProxy.server.joinChannel('channel1')
.done(function (result) {
if (result != 0) {
// error handling
}
})
.fail(function (error) {
// error handling
});
To confirm that your action was successfully performed, you can have a client method call. So, basically it would look like this:
public void ServerMethod(argumentList)
{
if (/* server code executed successfully */)
Clients.Caller.onSuccess(arguments);
else Clients.Caller.onFailure(arguments);
}
What this piece of code does is to notify the caller of the server method of a success/failure by calling a client method - method defined in JavaScript. You can also have a method executed on All clients, or only on specific users.
Since it is not an RPC mechanism, I think this is the closest thing you can do to simulate a return type in SignalR.
Hope this helps!
Best of luck!
What about
HubException in
Microsoft.AspNetCore.SignalR.
This is available in ASP.NET Core.
This exception is thrown on the server and sent to client. You can also derive from that class to put your own information in it.

Return an object and control status code

I upgraded from the ASP.NET Web API release on nuget.org (the version before the RC on Friday) to the nightly builds on myget.org. As expected there were many breaking changes, and one of them I can't seem to get around: We have a scenario where we want our action to return an object and set the status code to 201 Created. This was quite easily done before (might not compile - conceptual code from the top of my head):
Session session = GetSessionInfo(requestMessage);
var response = new HttpResonseMessage(HttpStatusCode.Created);
response.Content = response.CreateContent(session);
return response;
CreateContent was actually an extension method located in System.Net.Http.HttpResponseMessageExtensions calling an internal constructor in ObjectContent. With the new release HttpResponseMessageExtensions seems to be gone in the new release and so does the internal constructors of ObjectContent. Now it seems I must call an ObjectContent constructor, and the following seems to be the most appropriate for our needs:
public class ObjectContent<T> : ObjectContent
{
public ObjectContent(T value, MediaTypeFormatter formatter)
{
}
}
However it seems I have to pass a MediaTypeFormatter into it, mixing content negotiation into the logic of the action. In our setup content negotiation is generic and completely decoupled from the controllers.
Does anyone have a suggestion for solving the scenario to return an object, set the response status code, but not have to deal with MediaTypeFormatter, media type or any other content negotiation related stuff?
We redesigned how ObjectContent<T> creation works. Starting with the RC, you should be calling the CreateResponse() set of extension methods (they're off HttpRequestMessage). This will actually produce an instance of HttpResponseMessage with an instance of a content-negotiated ObjectContent<T> inside. So, something like:
Session session = GetSessionInfo(requestMessage);
var response = Request.CreateResponse(HttpStatusCode.Created, session);
return response;

Resources