use of io.micronaut.web.router.RouteInfo in project micronaut-core by micronaut-projects.
the class RouteExecutor method onError.
/**
* Creates a response publisher to represent the response after being handled
* by any available error route or exception handler.
*
* @param t The exception that occurred
* @param httpRequest The request that caused the exception
* @return A response publisher
*/
public Flux<MutableHttpResponse<?>> onError(Throwable t, HttpRequest<?> httpRequest) {
// find the origination of of the route
Class declaringType = httpRequest.getAttribute(HttpAttributes.ROUTE_INFO, RouteInfo.class).map(RouteInfo::getDeclaringType).orElse(null);
final Throwable cause;
// top level exceptions returned by CompletableFutures. These always wrap the real exception thrown.
if ((t instanceof CompletionException || t instanceof ExecutionException) && t.getCause() != null) {
cause = t.getCause();
} else {
cause = t;
}
RouteMatch<?> errorRoute = findErrorRoute(cause, declaringType, httpRequest);
if (errorRoute != null) {
if (serverConfiguration.isLogHandledExceptions()) {
logException(cause);
}
try {
AtomicReference<HttpRequest<?>> requestReference = new AtomicReference<>(httpRequest);
return buildRouteResponsePublisher(requestReference, Flux.just(errorRoute)).doOnNext(response -> response.setAttribute(HttpAttributes.EXCEPTION, cause)).onErrorResume(throwable -> createDefaultErrorResponsePublisher(requestReference.get(), throwable));
} catch (Throwable e) {
return createDefaultErrorResponsePublisher(httpRequest, e).flux();
}
} else {
Optional<BeanDefinition<ExceptionHandler>> optionalDefinition = beanContext.findBeanDefinition(ExceptionHandler.class, Qualifiers.byTypeArgumentsClosest(cause.getClass(), Object.class));
if (optionalDefinition.isPresent()) {
BeanDefinition<ExceptionHandler> handlerDefinition = optionalDefinition.get();
final Optional<ExecutableMethod<ExceptionHandler, Object>> optionalMethod = handlerDefinition.findPossibleMethods("handle").findFirst();
RouteInfo<Object> routeInfo;
if (optionalMethod.isPresent()) {
routeInfo = new ExecutableRouteInfo(optionalMethod.get(), true);
} else {
routeInfo = new RouteInfo<Object>() {
@Override
public ReturnType<?> getReturnType() {
return ReturnType.of(Object.class);
}
@Override
public Class<?> getDeclaringType() {
return handlerDefinition.getBeanType();
}
@Override
public boolean isErrorRoute() {
return true;
}
@Override
public List<MediaType> getProduces() {
return MediaType.fromType(getDeclaringType()).map(Collections::singletonList).orElse(Collections.emptyList());
}
};
}
Flux<MutableHttpResponse<?>> reactiveSequence = Flux.defer(() -> {
ExceptionHandler handler = beanContext.getBean(handlerDefinition);
try {
if (serverConfiguration.isLogHandledExceptions()) {
logException(cause);
}
Object result = handler.handle(httpRequest, cause);
return createResponseForBody(httpRequest, result, routeInfo);
} catch (Throwable e) {
return createDefaultErrorResponsePublisher(httpRequest, e);
}
});
final ExecutorService executor = findExecutor(routeInfo);
if (executor != null) {
reactiveSequence = applyExecutorToPublisher(reactiveSequence, executor);
}
return reactiveSequence.doOnNext(response -> response.setAttribute(HttpAttributes.EXCEPTION, cause)).onErrorResume(throwable -> createDefaultErrorResponsePublisher(httpRequest, throwable));
} else {
if (isIgnorable(cause)) {
logIgnoredException(cause);
return Flux.empty();
} else {
return createDefaultErrorResponsePublisher(httpRequest, cause).flux();
}
}
}
}
use of io.micronaut.web.router.RouteInfo in project micronaut-core by micronaut-projects.
the class JsonViewServerFilter method doFilter.
@Override
public Publisher<MutableHttpResponse<?>> doFilter(HttpRequest<?> request, ServerFilterChain chain) {
final RouteInfo<?> routeInfo = request.getAttribute(HttpAttributes.ROUTE_INFO, RouteInfo.class).orElse(null);
final Publisher<MutableHttpResponse<?>> responsePublisher = chain.proceed(request);
if (routeInfo != null) {
final Optional<Class<?>> viewClass = routeInfo.findAnnotation(JsonView.class).flatMap(AnnotationValue::classValue);
if (viewClass.isPresent()) {
return Flux.from(responsePublisher).switchMap(response -> {
final Optional<?> optionalBody = response.getBody();
if (optionalBody.isPresent()) {
Object body = optionalBody.get();
MediaTypeCodec codec = codecFactory.resolveJsonViewCodec(viewClass.get());
if (Publishers.isConvertibleToPublisher(body)) {
Publisher<?> pub = Publishers.convertPublisher(body, Publisher.class);
response.body(Flux.from(pub).map(o -> codec.encode((Argument) routeInfo.getBodyType(), o)).subscribeOn(Schedulers.fromExecutorService(executorService)));
} else {
return Mono.fromCallable(() -> {
@SuppressWarnings({ "unchecked", "rawtypes" }) final byte[] encoded = codec.encode((Argument) routeInfo.getBodyType(), body);
response.body(encoded);
return response;
}).subscribeOn(Schedulers.fromExecutorService(executorService));
}
}
return Flux.just(response);
});
}
}
return responsePublisher;
}
use of io.micronaut.web.router.RouteInfo in project micronaut-core by micronaut-projects.
the class RouteExecutor method createResponseForBody.
private Flux<MutableHttpResponse<?>> createResponseForBody(HttpRequest<?> request, Object body, RouteInfo<?> routeInfo) {
return Flux.<MutableHttpResponse<?>>defer(() -> {
Mono<MutableHttpResponse<?>> outgoingResponse;
if (body == null) {
if (routeInfo.isVoid()) {
MutableHttpResponse<Object> data = forStatus(routeInfo);
if (HttpMethod.permitsRequestBody(request.getMethod())) {
data.header(HttpHeaders.CONTENT_LENGTH, "0");
}
outgoingResponse = Mono.just(data);
} else {
outgoingResponse = Mono.just(newNotFoundError(request));
}
} else {
HttpStatus defaultHttpStatus = routeInfo.isErrorRoute() ? HttpStatus.INTERNAL_SERVER_ERROR : HttpStatus.OK;
boolean isReactive = routeInfo.isAsyncOrReactive() || Publishers.isConvertibleToPublisher(body);
if (isReactive) {
Class<?> bodyClass = body.getClass();
boolean isSingle = isSingle(routeInfo, bodyClass);
boolean isCompletable = !isSingle && routeInfo.isVoid() && Publishers.isCompletable(bodyClass);
if (isSingle || isCompletable) {
// full response case
Publisher<Object> publisher = Publishers.convertPublisher(body, Publisher.class);
Supplier<MutableHttpResponse<?>> emptyResponse = () -> {
MutableHttpResponse<?> singleResponse;
if (isCompletable || routeInfo.isVoid()) {
singleResponse = forStatus(routeInfo, HttpStatus.OK).header(HttpHeaders.CONTENT_LENGTH, "0");
} else {
singleResponse = newNotFoundError(request);
}
return singleResponse;
};
return Flux.from(publisher).flatMap(o -> {
MutableHttpResponse<?> singleResponse;
if (o instanceof Optional) {
Optional optional = (Optional) o;
if (optional.isPresent()) {
o = ((Optional<?>) o).get();
} else {
return Flux.just(emptyResponse.get());
}
}
if (o instanceof HttpResponse) {
singleResponse = toMutableResponse((HttpResponse<?>) o);
final Argument<?> bodyArgument = // Mono
routeInfo.getReturnType().getFirstTypeVariable().orElse(// HttpResponse
Argument.OBJECT_ARGUMENT).getFirstTypeVariable().orElse(// Mono
Argument.OBJECT_ARGUMENT);
if (bodyArgument.isAsyncOrReactive()) {
return processPublisherBody(request, singleResponse, routeInfo);
}
} else if (o instanceof HttpStatus) {
singleResponse = forStatus(routeInfo, (HttpStatus) o);
} else {
singleResponse = forStatus(routeInfo, defaultHttpStatus).body(o);
}
return Flux.just(singleResponse);
}).switchIfEmpty(Mono.fromSupplier(emptyResponse));
} else {
// streaming case
Argument<?> typeArgument = routeInfo.getReturnType().getFirstTypeVariable().orElse(Argument.OBJECT_ARGUMENT);
if (HttpResponse.class.isAssignableFrom(typeArgument.getType())) {
// a response stream
Publisher<HttpResponse<?>> bodyPublisher = Publishers.convertPublisher(body, Publisher.class);
Flux<MutableHttpResponse<?>> response = Flux.from(bodyPublisher).map(this::toMutableResponse);
Argument<?> bodyArgument = typeArgument.getFirstTypeVariable().orElse(Argument.OBJECT_ARGUMENT);
if (bodyArgument.isAsyncOrReactive()) {
return response.flatMap((resp) -> processPublisherBody(request, resp, routeInfo));
}
return response;
} else {
MutableHttpResponse<?> response = forStatus(routeInfo, defaultHttpStatus).body(body);
return processPublisherBody(request, response, routeInfo);
}
}
}
// now we have the raw result, transform it as necessary
if (body instanceof HttpStatus) {
outgoingResponse = Mono.just(HttpResponse.status((HttpStatus) body));
} else {
if (routeInfo.isSuspended()) {
boolean isKotlinFunctionReturnTypeUnit = routeInfo instanceof MethodBasedRouteMatch && isKotlinFunctionReturnTypeUnit(((MethodBasedRouteMatch) routeInfo).getExecutableMethod());
final Supplier<CompletableFuture<?>> supplier = ContinuationArgumentBinder.extractContinuationCompletableFutureSupplier(request);
if (isKotlinCoroutineSuspended(body)) {
return Mono.fromCompletionStage(supplier).<MutableHttpResponse<?>>flatMap(obj -> {
MutableHttpResponse<?> response;
if (obj instanceof HttpResponse) {
response = toMutableResponse((HttpResponse<?>) obj);
final Argument<?> bodyArgument = routeInfo.getReturnType().getFirstTypeVariable().orElse(Argument.OBJECT_ARGUMENT);
if (bodyArgument.isAsyncOrReactive()) {
return processPublisherBody(request, response, routeInfo);
}
} else {
response = forStatus(routeInfo, defaultHttpStatus);
if (!isKotlinFunctionReturnTypeUnit) {
response = response.body(obj);
}
}
return Mono.just(response);
}).switchIfEmpty(createNotFoundErrorResponsePublisher(request));
} else {
Object suspendedBody;
if (isKotlinFunctionReturnTypeUnit) {
suspendedBody = Mono.empty();
} else {
suspendedBody = body;
}
outgoingResponse = toMutableResponse(request, routeInfo, defaultHttpStatus, suspendedBody);
}
} else {
outgoingResponse = toMutableResponse(request, routeInfo, defaultHttpStatus, body);
}
}
}
// for head request we never emit the body
if (request != null && request.getMethod().equals(HttpMethod.HEAD)) {
outgoingResponse = outgoingResponse.map(r -> {
final Object o = r.getBody().orElse(null);
if (o instanceof ReferenceCounted) {
((ReferenceCounted) o).release();
}
r.body(null);
return r;
});
}
return outgoingResponse;
}).doOnNext((response) -> {
applyConfiguredHeaders(response.getHeaders());
if (routeInfo instanceof RouteMatch) {
response.setAttribute(HttpAttributes.ROUTE_MATCH, routeInfo);
}
response.setAttribute(HttpAttributes.ROUTE_INFO, routeInfo);
});
}
use of io.micronaut.web.router.RouteInfo in project micronaut-core by micronaut-projects.
the class RouteExecutor method handleStatusException.
private Publisher<MutableHttpResponse<?>> handleStatusException(HttpRequest<?> request, MutableHttpResponse<?> response) {
HttpStatus status = response.status();
RouteInfo<?> routeInfo = response.getAttribute(HttpAttributes.ROUTE_INFO, RouteInfo.class).orElse(null);
if (status.getCode() >= 400 && routeInfo != null && !routeInfo.isErrorRoute()) {
RouteMatch<Object> statusRoute = findStatusRoute(request, status, routeInfo);
if (statusRoute != null) {
return executeRoute(request, false, Flux.just(statusRoute));
}
}
return Flux.just(response);
}
use of io.micronaut.web.router.RouteInfo in project micronaut-core by micronaut-projects.
the class RoutingInBoundHandler method encodeHttpResponse.
private void encodeHttpResponse(ChannelHandlerContext context, NettyHttpRequest<?> nettyRequest, MutableHttpResponse<?> response, @Nullable Argument<Object> bodyType, Object body) {
boolean isNotHead = nettyRequest.getMethod() != HttpMethod.HEAD;
if (isNotHead) {
if (body instanceof Writable) {
getIoExecutor().execute(() -> {
ByteBuf byteBuf = context.alloc().ioBuffer(128);
ByteBufOutputStream outputStream = new ByteBufOutputStream(byteBuf);
try {
Writable writable = (Writable) body;
writable.writeTo(outputStream, nettyRequest.getCharacterEncoding());
response.body(byteBuf);
if (!response.getContentType().isPresent()) {
response.getAttribute(HttpAttributes.ROUTE_INFO, RouteInfo.class).ifPresent((routeInfo) -> response.contentType(routeExecutor.resolveDefaultResponseContentType(nettyRequest, routeInfo)));
}
writeFinalNettyResponse(response, nettyRequest, context);
} catch (IOException e) {
final MutableHttpResponse<?> errorResponse = routeExecutor.createDefaultErrorResponse(nettyRequest, e);
writeFinalNettyResponse(errorResponse, nettyRequest, context);
}
});
} else if (body instanceof Publisher) {
response.body(null);
DelegateStreamedHttpResponse streamedResponse = new DelegateStreamedHttpResponse(toNettyResponse(response), mapToHttpContent(nettyRequest, response, body, context));
nettyRequest.prepareHttp2ResponseIfNecessary(streamedResponse);
context.writeAndFlush(streamedResponse);
context.read();
} else {
encodeResponseBody(context, nettyRequest, response, bodyType, body);
writeFinalNettyResponse(response, nettyRequest, context);
}
} else {
response.body(null);
writeFinalNettyResponse(response, nettyRequest, context);
}
}
Aggregations