Spring returns EmptyCollectionEmbeddedWrapper instead of just an empty array - spring

(Using only #RepositoryRestResource and methods defined inside the repository)When making an http request and a repository method returns no result, is there any workaround to change the returned "content" to an empty array if it really is empty , instead of :
HttpEntityMethodProcessor - Writing [Resources { content: [org.springframework.hateoas.core.EmbeddedWrappers$EmptyCollectionEmbeddedWrapp
My repo:
#RepositoryRestResource(collectionResourceRel = "some", path = "some")
public interface SomeRepository extends PagingAndSortingRepository<SomePojo, String>
{}
false:
"content": [
{
"value": [],
"rel": null,
"collectionValue": true,
"relTargetType": "xy.cxyPojo"
}
]
}
good:
"content": []
}
Edit: found the solution, it is the following:
The best and easiest solution i found finally for this so far is the following. Implement a custom ResourceProcessor, which is automatically picked up and used by spring(because of the #Component). Override the process method and in the method return a new Resource() which is initialized with an empty list, instead of the old Resource you got as an argument, add the links and all what you want and that's it. Like this:
import java.util.Collections;
import javax.servlet.http.HttpServletRequest;
import org.springframework.hateoas.Link;
import org.springframework.hateoas.ResourceProcessor;
import org.springframework.hateoas.Resources;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
#Component
public class ResourceProcessorEmpty implements ResourceProcessor<Resources<Object>>
{
#Override
public Resources<Object> process(final Resources<Object> resourceToThrowAway)
{
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
// In my case i needed the request link with parameters, and the empty content[] array
Link link = new Link(request.getRequestURL().toString() + "?" + request.getQueryString());
Resources<Object> newResource = new Resources<>(Collections.emptyList());
newResource.add(link);
return newResource;
}
}
For clarification: if you use Resources<Object>, that will handle empty collections(when that "EmptyCollectionEmbeddedWrapper" dummy object would be returned), whereas Resources<Resource<Object>> will handle non-empty collections. In this case the first needs to be used.

Related

How can i implement slf4j MDC in springboot webflux [duplicate]

I referenced with the blog post Contextual Logging with Reactor Context and MDC but I don't know how to access reactor context in WebFilter.
#Component
public class RequestIdFilter implements WebFilter {
#Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
List<String> myHeader = exchange.getRequest().getHeaders().get("X-My-Header");
if (myHeader != null && !myHeader.isEmpty()) {
MDC.put("myHeader", myHeader.get(0));
}
return chain.filter(exchange);
}
}
Here's one solution based on the latest approach, as of May 2021, taken from the official documentation:
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import java.util.function.Consumer;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.MDC;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpHeaders;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
import reactor.core.publisher.Mono;
import reactor.core.publisher.Signal;
import reactor.util.context.Context;
#Slf4j
#Configuration
public class RequestIdFilter implements WebFilter {
#Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
String requestId = getRequestId(request.getHeaders());
return chain
.filter(exchange)
.doOnEach(logOnEach(r -> log.info("{} {}", request.getMethod(), request.getURI())))
.contextWrite(Context.of("CONTEXT_KEY", requestId));
}
private String getRequestId(HttpHeaders headers) {
List<String> requestIdHeaders = headers.get("X-Request-ID");
return requestIdHeaders == null || requestIdHeaders.isEmpty()
? UUID.randomUUID().toString()
: requestIdHeaders.get(0);
}
public static <T> Consumer<Signal<T>> logOnEach(Consumer<T> logStatement) {
return signal -> {
String contextValue = signal.getContextView().get("CONTEXT_KEY");
try (MDC.MDCCloseable cMdc = MDC.putCloseable("MDC_KEY", contextValue)) {
logStatement.accept(signal.get());
}
};
}
public static <T> Consumer<Signal<T>> logOnNext(Consumer<T> logStatement) {
return signal -> {
if (!signal.isOnNext()) return;
String contextValue = signal.getContextView().get("CONTEXT_KEY");
try (MDC.MDCCloseable cMdc = MDC.putCloseable("MDC_KEY", contextValue)) {
logStatement.accept(signal.get());
}
};
}
}
Given you have the following line in your application.properties:
logging.pattern.level=[%X{MDC_KEY}] %5p
then every time an endpoint is called your server logs will contain a log like this:
2021-05-06 17:07:41.852 [60b38305-7005-4a05-bac7-ab2636e74d94] INFO 20158 --- [or-http-epoll-6] my.package.RequestIdFilter : GET http://localhost:12345/my-endpoint/444444/
Every time you want to log manually something within a reactive context you will have add the following to your reactive chain:
.doOnEach(logOnNext(r -> log.info("Something")))
If you want the X-Request-ID to be propagated to other services for distributed tracing, you need to read it from the reactive context (not from MDC) and wrap your WebClient code with the following:
Mono.deferContextual(
ctx -> {
RequestHeadersSpec<?> request = webClient.get().uri(uri);
request = request.header("X-Request-ID", ctx.get("CONTEXT_KEY"));
// The rest of your request logic...
});
You can do something similar to below, You can set the context with any class you like, for this example I just used headers - but a custom class will do just fine.
If you set it here, then any logging with handlers etc will also have access to the context.
The logWithContext below, sets the MDC and clears it after. Obviously this can be replaced with anything you like.
public class RequestIdFilter implements WebFilter {
private Logger LOG = LoggerFactory.getLogger(RequestIdFilter.class);
#Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
HttpHeaders headers = exchange.getRequest().getHeaders();
return chain.filter(exchange)
.doAfterSuccessOrError((r, t) -> logWithContext(headers, httpHeaders -> LOG.info("Some message with MDC set")))
.subscriberContext(Context.of(HttpHeaders.class, headers));
}
static void logWithContext(HttpHeaders headers, Consumer<HttpHeaders> logAction) {
try {
headers.forEach((name, values) -> MDC.put(name, values.get(0)));
logAction.accept(headers);
} finally {
headers.keySet().forEach(MDC::remove);
}
}
}
As of Spring Boot 2.2 there is Schedulers.onScheduleHook that enables you to handle MDC:
Schedulers.onScheduleHook("mdc", runnable -> {
Map<String, String> map = MDC.getCopyOfContextMap();
return () -> {
if (map != null) {
MDC.setContextMap(map);
}
try {
runnable.run();
} finally {
MDC.clear();
}
};
});
Alternatively, Hooks.onEachOperator can be used to pass around the MDC values via subscriber context.
http://ttddyy.github.io/mdc-with-webclient-in-webmvc/
This is not full MDC solution, e.g. in my case I cannot cleanup MDC values in R2DBC threads.
UPDATE: this article really solves my MDC problem: https://www.novatec-gmbh.de/en/blog/how-can-the-mdc-context-be-used-in-the-reactive-spring-applications/
It provides correct way of updating MDC based on subscriber context.
Combine it with SecurityContext::class.java key populated by AuthenticationWebFilter and you will be able to put user login to your logs.
My solution based on Reactor 3 Reference Guide approach but using doOnSuccess instead of doOnEach.
The main idea is to use Context for MDC propagation in the next way:
Fill a downstream Context (which will be used by derived threads) with the MDC state from an upstream flow (can be done by .contextWrite(context -> Context.of(MDC.getCopyOfContextMap())))
Access the downstream Context in derived threads and fill MDC in derived thread with values from the downstream Context (the main challenge)
Clear the MDC in the downstream Context (can be done by .doFinally(signalType -> MDC.clear()))
The main problem is to access a downstream Context in derived threads. And you can implement step 2 with the most convenient for you approach). But here is my solution:
webclient.post()
.bodyValue(someRequestData)
.retrieve()
.bodyToMono(String.class)
// By this action we wrap our response with a new Mono and also
// in parallel fill MDC with values from a downstream Context because
// we have an access to it
.flatMap(wrapWithFilledMDC())
.doOnSuccess(response -> someActionWhichRequiresFilledMdc(response)))
// Fill a downstream context with the current MDC state
.contextWrite(context -> Context.of(MDC.getCopyOfContextMap()))
// Allows us to clear MDC from derived threads
.doFinally(signalType -> MDC.clear())
.block();
// Function which implements second step from the above main idea
public static <T> Function<T, Mono<T>> wrapWithFilledMDC() {
// Using deferContextual we have an access to downstream Context, so
// we can just fill MDC in derived threads with
// values from the downstream Context
return item -> Mono.deferContextual(contextView -> {
// Function for filling MDC with Context values
// (you can apply your action)
fillMdcWithContextView(contextView);
return Mono.just(item);
});
}
public static void fillMdcWithContextValues(ContextView contextView) {
contextView.forEach(
(key, value) -> {
if (key instanceof String keyStr && value instanceof String valueStr) {
MDC.put(keyStr, valueStr);
}
});
}
This approach is also can be applied to doOnError and onErrorResume methods since the main idea is the same.
Used versions:
spring-boot: 2.7.3
spring-webflux: 5.3.22 (from spring-boot)
reactor-core: 3.4.22 (from spring-webflux)
reactor-netty: 1.0.22 (from spring-webflux)
I achieved this with :-
package com.nks.app.filter;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.MDC;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
import reactor.core.publisher.Mono;
/**
* #author nks
*/
#Component
#Slf4j
public class SessionIDFilter implements WebFilter {
private static final String APP_SESSION_ID = "app-session-id";
/**
* Process the Web request and (optionally) delegate to the next
* {#code WebFilter} through the given {#link WebFilterChain}.
*
* #param serverWebExchange the current server exchange
* #param webFilterChain provides a way to delegate to the next filter
* #return {#code Mono<Void>} to indicate when request processing is complete
*/
#Override
public Mono<Void> filter(ServerWebExchange serverWebExchange, WebFilterChain webFilterChain) {
serverWebExchange.getResponse()
.getHeaders().add(APP_SESSION_ID, serverWebExchange.getRequest().getHeaders().getFirst(APP_SESSION_ID));
MDC.put(APP_SESSION_ID, serverWebExchange.getRequest().getHeaders().getFirst(APP_SESSION_ID));
log.info("[{}] : Inside filter of SessionIDFilter, ADDED app-session-id in MDC Logs", MDC.get(APP_SESSION_ID));
return webFilterChain.filter(serverWebExchange);
}
}
and, values associated with app-session-id for the thread can be logged.

Error casting object MethodSignature. Spring AOP

Thanks in advance for your support.
Currently I´m stuck in the next problem. I developed an Aspect class to validate my input JSON from al the pkg of RestController.
Complying with certain characteristics.
Each method of my controllers returns a different DTO object.
I created a new generic object to return it from my aspect, when my logic is not fulfilled. When I do tests, I get an error of CannotCastClass "xxxxDTO" to newErrorResponseDTO.
Currently I already can obtain the method signature or the object type. My idea is to cast the return type (from methodSignature) to my new DTOResponse. The object response is always different.
I mention that the architecture and design of the total project was already developed. I only did the aspect
At the moment, I have not succeeded.
I attach evidence. Thanks
I tried ResponseAdvice, and multiple ways to cast objects.
I prefer to stay in the aspect. I get the solution changing all the response DTO in controller to Object generic. Asumming that doing is bad practice, i prefer real solution
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Arrays;
// Other imports missing...
#Aspect
#Component("validateParameterAspect")
public class ValidatorParameterAspect {
public static final Logger logger = Logger.getLogger(ValidatorParameterAspect.class);
#Autowired
ServiciosRest servicio;
#Pointcut("execution(* com.actinver.rest.*.* (..))")
public void executeController() {}
#Pointcut("#annotation(org.springframework.web.bind.annotation.RequestMapping)")
public void logRequestMapping() {}
#Around("logRequestMapping() && executeController() && args(..,#RequestBody requestBody) ")
public Object logRequestBody(ProceedingJoinPoint joinPoint, Object requestBody) throws Throwable {
String vlDataDecrypt = "";
try {
// output = joinPoint.proceed();
System.out.println("--------------123------------");
logger.warn("Entering in Method : " + joinPoint.getSignature().getName());
logger.warn("Class Name : " + joinPoint.getSignature().getDeclaringTypeName());
logger.warn("Arguments : " + Arrays.toString(joinPoint.getArgs()));
logger.warn("Target class : " + joinPoint.getTarget().getClass().getName());
SimpleJSONDataContainer args = (SimpleJSONDataContainer) joinPoint.getArgs()[0];
MethodSignature sign = (MethodSignature) joinPoint.getSignature();
Class<?> ret = sign.getReturnType();
String returnString = sign.getReturnType().getName();
logger.warn("Signature : " + ret);
vlDataDecrypt = AESHelper.decrypt(servicio.getSeedWord(), args.getData());
logger.info(" Decrypt -> " + vlDataDecrypt);
logger.info("args " + args.getData());
ErrorDataResponse res = validDataEmpty(args.getData());
if (res.getResult() == "2") {
return res; // or cast Class<?>
//return ret.cast(res);
}
} catch (Exception e) {
logger.error("Stack trace -> ", e);
}
return joinPoint.proceed();
}
public ErrorDataResponse validDataEmpty(String vlDataDecrypt) {
ErrorDataResponse errorDto = new ErrorDataResponse();
if (vlDataDecrypt == null || vlDataDecrypt.hashCode() == "77631826690E45839D7B49B932CBC81B".hashCode()
&& vlDataDecrypt.equalsIgnoreCase("77631826690E45839D7B49B932CBC81B")) {
errorDto.setResult("2");
errorDto.setMensaje(RestValidatorUtil.EnumErrors.ERROR_INPUT.getMsg());
logger.info("JSON null" + errorDto.getResult());
return errorDto;
}
return errorDto;
}
}
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
// Other imports missing...
#RestController
#RequestMapping("inicio")
public class Bursanet {
public final static Logger logger = Logger.getLogger(Bursanet.class);
#RequestMapping(
value = "cashByDate",
method = { RequestMethod.GET, RequestMethod.POST },
consumes = "application/json",
produces = "application/json"
)
public CashByDateDTO cashByDate(
#RequestBody SimpleJSONDataContainer simpleJSONDataContainer,
Authentication authentication
) {
String vlDataDecrypt = "";
CashByDateDTO outJson = new CashByDateDTO();
CashByDateRequest request = null;
try {
UsernamePasswordAuthenticationToken userPasswordAuthenticationToken =
(UsernamePasswordAuthenticationToken)
((OAuth2Authentication) authentication).getUserAuthentication();
//////example
return outJson;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
It is very difficult to analyse your code because you are not providing an MCVE:
There are no package names in your classes.
There are no imports either.
You use several project-specific classes (not part of the Spring Framework) the code of which you also don't share here.
There is no Spring configuration either.
So I have to make some educated guesses here. From what I can see, I can tell you this:
If you expect ValidatorParameterAspect.logRequestBody(..) to intercept execution of Bursanet.cashByDate(..), it should not work because
in args(.., #RequestBody requestBody) you are expecting that parameter to be the last one in the target method's signature, but actually in Bursanet.cashByDate(..) it is the first one. So the pointcut should never match.
Again in args(.., #RequestBody requestBody) you ought to use a fully qualified class name, i.e. args(.., #org.springframework.web.bind.annotation.RequestBody requestBody).
Please also note that execution(* com.actinver.rest.*.* (..)) only matches methods in classes residing directly in the com.actinver.rest package, not in any subpackages. If you want to include those too, you need to change the pointcut to execution(* com.actinver.rest..* (..)).
In your question you mention you only want to intercept REST controllers, but you do not limit pointcut matching to classes with a #RestController annotation. You could do that via #within(org.springframework.web.bind.annotation.RestController). Right now you are doing it indirectly by only relying on methods with #annotation(org.springframework.web.bind.annotation.RequestMapping), which will also work as long as those methods only occur in #RequestController classes. Probably this is the case in your application, I am just mentioning it as a detail.
Instead of SimpleJSONDataContainer args = (SimpleJSONDataContainer) joinPoint.getArgs()[0];, why don't you bind the first argument to a SimpleJSONDataContainer parameter via args() and then just use the currently unused requestBody advice method parameter in your code? Something like this:
#Around("logRequestMapping() && executeController() && args(#org.springframework.web.bind.annotation.RequestBody requestBody, ..)")
public Object logRequestBody(ProceedingJoinPoint joinPoint, SimpleJSONDataContainer requestBody) throws Throwable {
// (...)
vlDataDecrypt = AESHelper.decrypt(servicio.getSeedWord(), requestBody.getData());
logger.info(" Decrypt -> " + vlDataDecrypt);
logger.info("args " + requestBody.getData());
ErrorDataResponse res = validDataEmpty(requestBody.getData());
// (...)
}
You define MethodSignature sign = (MethodSignature) joinPoint.getSignature(); but don't use it above several times where you repeatedly call joinPoint.getSignature(), too. Instead you could just reorganise the code like this:
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
System.out.println("--------------123------------");
logger.warn("Entering in Method : " + methodSignature.getName());
logger.warn("Class Name : " + methodSignature.getDeclaringTypeName());
logger.warn("Arguments : " + Arrays.toString(joinPoint.getArgs()));
logger.warn("Target class : " + joinPoint.getTarget().getClass().getName());
Class<?> ret = methodSignature.getReturnType();
String returnString = methodSignature.getReturnType().getName();
I never understood why so many people call many JoinPoint methods in order to extract details for logging if instead they could simply log the joinpoint instance. This would show the type of pointcut (e.g. execution()) as well as the target method signature. Okay, if you want to list all method arguments, you can do this additionally, but how about this, wouldn't that be enough?
logger.warn(joinPoint);
// logger.warn("Entering in Method : " + methodSignature.getName());
// logger.warn("Class Name : " + methodSignature.getDeclaringTypeName());
logger.warn("Arguments : " + Arrays.toString(joinPoint.getArgs()));
// logger.warn("Target class : " + joinPoint.getTarget().getClass().getName());
This whole code block I guess you can also remove. It even prints wrong information and calls the return type "signature":
Class<?> ret = methodSignature.getReturnType();
String returnString = methodSignature.getReturnType().getName();
logger.warn("Signature : " + ret);
Now for the part which is probably your problem:
ErrorDataResponse res = validDataEmpty(requestBody.getData());
if (res.getResult() == "2") {
return res; // or cast Class<?>
//return ret.cast(res);
}
Here you are making the aspect advice skip the joinPoint.proceed() call and return another object instead. The method you intercept has the signature public CashByDateDTO cashByDate(..), i.e. it returns a specific DTO type. If you want to return an ErrorDataResponse instead, this would only work if ErrorDataResponse was a subtype of CashByDateDTO, which probably it is not. From the class names I would even say that a *Response and a *DTO are completely different object types. Your advice cannot just change or ignore the method signature. You have to return a CashByDateDTO object, no matter what. If you cannot do that here, maybe you are intercepting the wrong method or trying to do the wrong thing in your aspect.
Sorry for the lengthy reply, but there is so much chaos in your code, I had to point out some details.

Spring Feign: Could not extract response: no suitable HttpMessageConverter found for response type

I am trying to get a Spring Cloud Netflix Feign client to fetch a bit of JSON over HTTP and convert it to an object. I keep getting this error instead:
org.springframework.web.client.RestClientException: Could not extract response: no suitable HttpMessageConverter found for response type [class io.urig.checkout.Book] and content type [application/json;charset=UTF-8]
Here's the bit of JSON returned from the remote service:
{
"id": 1,
"title": "Moby Dick",
"author": "Herman Melville"
}
Here's the corresponding class I'm trying to deserialize to:
package io.urig.checkout;
public class Book {
private long id;
private String title;
private String author;
public Book() {}
public Book(long id, String title, String author) {
super();
this.id = id;
this.title = title;
this.author = author;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
}
And here's my Feign client:
package io.urig.checkout;
import java.util.Optional;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import io.urig.checkout.Book;
#FeignClient(name="inventory", url="http://localhost:8080/")
public interface InventoryClient {
#RequestMapping(method = RequestMethod.GET, value = "books/{bookId}")
public Optional<Book> getBookById(#PathVariable(value="bookId") Long bookId);
}
What do I need to do to get this to work?
I don't know Feign, but when I've had "no suitable HttpMessageConverter found..." errors in the past, it's because the content type has not been registered. Perhaps you need to add this to the RequestMapping:
consumes = "application/json"
All I can suggest is to try to confirm if Feign configuration has MappingJackson2HttpMessageConverter registered as a converter for Book. Not sure if this is something that should work out of the box with Feign, or if you have to do it manually. I see an example on Feign's GitHub that has:
GitHub github = Feign.builder()
.encoder(new JacksonEncoder())
.decoder(new JacksonDecoder())
.target(GitHub.class, "https://api.github.com");
Have you created configuration using Feign.builder() or some equivalent configuration files?
You will need to ensure that you have at least one JSON library on your classpath. Feign supports both GSON and Jackson and Spring Cloud OpenFeign will autoconfigure the SpringEncoder and SpringDecoder instances with the appropriate MessageConverter if they are found on your classpath. Ensure that you have at least one of the following in your pom.xml or build.gradle
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.4</version>
</dependency>
or
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.2</version>
</dependency>
Once they are found, Spring will register the appropriate MessageConverter
I think your problem is the response type. Try converting it to Book from Optional. If you want to return an Optional than you should provide your custom converter.
Sorry, for too late answer.
Had the same problem.
Just add two parameters to your #RequestMapping -
consumes = "application/json", produces = "application/json"
In your code this will look like this -
package io.urig.checkout;
import java.util.Optional;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import io.urig.checkout.Book;
#FeignClient(name="inventory", url="http://localhost:8080/")
public interface InventoryClient {
#RequestMapping(method = RequestMethod.GET, value = "books/{bookId}", consumes = "application/json", produces = "application/json")
public Optional<Book> getBookById(#PathVariable(value="bookId") Long bookId);
}
Thanks to all who tried to help!
As it turned out my issue was a defective Maven dependency, probably corrupted during download or installation. Having entirely deleted the .m2/repository folder on my machine and then updating Maven dependencies for the project the issue is now gone.
I am late here but I would like to add one more point. In my case I observed Spring Feign client returns this exception when you specified the return type as a specific model/entity class and that entity is not found.
You should check the response for the another service which you are calling and see what response it returns in case the entity is not found, or in case an exception is thrown.
So in case an entity is not found or any exception is thrown and that response does not match to what you have specified in return type then this exception is thrown in the client service.

'object%20Object' Being Appended instead of parameters.

I am attempting to make a call to the server using promises. When trying to add my parameters, it comes out as 'object%20Object'
Here is the call
import { Injectable } from '#angular/core';
import { Http } from '#angular/http';
import 'rxjs/add/operator/toPromise';
import 'rxjs/add/operator/map';
import { User } from '../models/user';
#Injectable()
export class UserService {
private baseUserUrl = 'api/User/'
constructor(private http: Http) { }
getUsers(currentPage: number): Promise<User[]> {
return this.http.get(this.baseUserUrl + 'GetUsers?currentPage=' + currentPage)
.map(resp => resp.json() as User[])
.toPromise()
}
}
I was accidentally passing an object into the method, so I wasn't accessing the property, I was accessing the object. I fixed that and removed the object and passed a property.

Spring Data REST returns EmptyCollectionEmbeddedWrapper instead of empty collection

I am developing a Service based on Spring Data REST. Cause of the fact that we are creating the frontend code using swagger (generated via SpringFox) I had to deactivate the return of the HAL-format which works fine with one exception.
If the result of a request is an empty list the response looks like this
{
"links": [
{
"rel": "self",
"href": "http://localhost:9999/users"
},
{
"rel": "profile",
"href": "http://localhost:9999/profile/users"
}
],
"content": [
{
"rel": null,
"collectionValue": true,
"relTargetType": "com.example.User",
"value": []
}
]
}
How can I get an empty List as content?
I have had to adapt the solution provided in the previous solution to use the types introduced by the Spring HATEOAS 1.x
This is the code I'm using:
#Component
public class ResourceProcessorEmpty implements RepresentationModelProcessor<CollectionModel<Object>> {
#Override
public CollectionModel<Object> process(CollectionModel<Object> resourceToThrowAway) {
if (resourceToThrowAway.getContent().size() != 1) {
return resourceToThrowAway;
}
if (!resourceToThrowAway.getContent().iterator().next().getClass().getCanonicalName().contains("EmptyCollectionEmbeddedWrapper")) {
return resourceToThrowAway;
}
CollectionModel<Object> newResource = new CollectionModel<>(Collections.emptyList());
newResource.add(resourceToThrowAway.getLinks());
return newResource;
}
}
The best and easiest solution i found finally for this so far is the following. Implement a custom ResourceProcessor, which is automatically picked up and used by spring(because of the #Component). Override the process method and in the method return a new Resource() which is initialized with an empty list, instead of the old Resource you got as an argument, add the links and all what you want and that's it. Like this:
import java.util.Collections;
import javax.servlet.http.HttpServletRequest;
import org.springframework.hateoas.Link;
import org.springframework.hateoas.ResourceProcessor;
import org.springframework.hateoas.Resources;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
#Component
public class ResourceProcessorEmpty implements ResourceProcessor<Resources<Object>>
{
#Override
public Resources<Object> process(final Resources<Object> resourceToThrowAway)
{
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
// In my case i needed the request link with parameters, and the empty content[] array
Link link = new Link(request.getRequestURL().toString() + "?" + request.getQueryString());
Resources<Object> newResource = new Resources<>(Collections.emptyList());
newResource.add(link);
return newResource;
}
}
For clarification: if you use Resources<Object>, that will handle empty collections(when that "EmptyCollectionEmbeddedWrapper" dummy object would be returned), whereas Resources<Resource<Object>> will handle non-empty collections. In this case the first needs to be used.

Resources