Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support async views rendering #857

Open
PepeBotella25 opened this issue Aug 14, 2024 · 0 comments
Open

Support async views rendering #857

PepeBotella25 opened this issue Aug 14, 2024 · 0 comments

Comments

@PepeBotella25
Copy link

PepeBotella25 commented Aug 14, 2024

Feature description

Currently the Views rendering (ReactViewsRenderer::render) cannot be async, if happens that it's, it fails with io.netty.util.IllegalReferenceCountException: refCnt: 0.

In my case I'm using micronaut-views-react and my Jasascript waits for a Java Mono.

Despite the docs says that's not supported, I was able to workaround that by implementing my own ViewFilter where I create a Publisher from the Writable returned by ReactViewsRenderer::render, basically the idea is to wait until Writer::write gets called.

So I wonder whether something like this can be implemented to support async "rendering".

I copied the ViewsFilter (removing some logic I didn't need in my case), and "delay" the response until the Writer::write gets called.

It's only waiting for the first call to write, but leaving subscriber.onComplete(); only in flush and close should support multiple calls. As micronaut-views-react does not call neither flush nor close I had to call subscriber.onComplete(); in write too.

public final Publisher<MutableHttpResponse<?>> doFilter(HttpRequest<?> request,
			ServerFilterChain chain) {
		return Flux.from(chain.proceed(request))
				.switchMap(response -> {
					Object body = response.body();

					String view = viewsResolver.resolveView(request, response).orElse(null);
					if (view == null || !view.equals("App")) { // Only use this for my App View
						return Flux.just(response);
					}

					MediaType type = UTF8_HTML;
					Optional<ViewsRenderer> optionalViewsRenderer = viewsRendererLocator.resolveViewsRenderer(view, type.getName(), body);
					if(optionalViewsRenderer.isEmpty()) {
						return Flux.just(response);
					}

					ModelAndView<?> modelAndView = new ModelAndView<>(view, ((ModelAndView<?>) body).getModel().orElse(null));
					viewsModelDecorator.decorate(request, modelAndView);
					Writable writable = optionalViewsRenderer.get().render(view, modelAndView.getModel().orElse(null), request);

					return Flux.from((Publisher<String>) subscriber -> {
						subscriber.onSubscribe(new Subscription() {
							@Override
							public void request(long n)
							{
								try
								{
									writable.writeTo(new Writer() {

										@Override
										public void write(char[] buffer, int off, int len) throws IOException
										{
											subscriber.onNext(String.valueOf(buffer));
											subscriber.onComplete();
										}

										@Override
										public void flush() throws IOException
										{
											subscriber.onComplete();
										}

										@Override
										public void close() throws IOException
										{
											subscriber.onComplete();
										}
									});
								}
								catch (Exception e)
								{
									subscriber.onError(e);
								}
							}

							@Override
							public void cancel()
							{
								subscriber.onComplete();
							}
						});
					}).map(b -> response.body(b).contentType(type));
				});
	}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant