No value at JSON path "$" - spring

I am testing rest controller. Here is test code:
mockMvc.perform(get("/index/get-all"))
.andExpect(status().isOk())
.andDo(print())
.andExpect(jsonPath("$",hasSize(2)));
I get response body:
Body = [{"id":"123"},{"id":"1234"}]
And I get error:
java.lang.AssertionError: No value at JSON path "$", exception: net/minidev/json/writer/JsonReaderI
What am I doing wrong?

Your body returns an array with objects. To access each object in Spring MVC Test use following assertion:
.andExpect(jsonPath("[0].id").value("123"))
.andExpect(jsonPath("[1].id").value("1234"))

Get the same error.
Try to use higher version json-smart in your dependency, and exclude the lower version from the package which contains json-smart. For me, I changed my dependency to:
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path</artifactId>
<version>2.2.0</version>
<scope>test</scope>
<exclusions>
<exclusion>
<artifactId>json-smart</artifactId>
<groupId>net.minidev</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path-assert</artifactId>
<version>2.2.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>net.minidev</groupId>
<artifactId>json-smart</artifactId>
<version>2.2</version>
<scope>test</scope>
</dependency>

Double check your url: "/index/get-all" and make sure you are using the full path of your controller class. I ran into this same issue because my url was missing a part of the path ("/Folder/"). What I had been using was "/folderId=1234" and realized I forgot to add the controller's #RequestMapping annotated part at the top of the class, so it was supposed to be "/Folder/folderId=1234".
Example controller code
#RestController
#RequestMapping ("/Folder")
public class FolderController {
#RequestMapping (value = "/folderId={Id}", method = RequestMethod.GET)
public Folder getFolderById (#PathVariable String folderId, HttpSession session)
{
// controller code
}
}
The incorrect url I was using.
mockMvc.perform(
get("/folderId=1234")
.andExpect(status().isOk())
.andDo(print())
.andExpect(jsonPath("$",hasSize(2)));
Correct url.
mockMvc.perform(
get("/Folder/folderId=1234")
.andExpect(status().isOk())
.andDo(print())
.andExpect(jsonPath("$",hasSize(2)));

I had a scenario where the stack trace was indicating something similar to your error:
java.lang.AssertionError: No value at JSON path "$[0].contentId"
Caused by: java.lang.NoClassDefFoundError: net/minidev/json/writer/JsonReaderI
I found this post: https://github.com/json-path/JsonPath/issues/159#issuecomment-411306322 which caught my attention. I was using the net.minidev:json-smart:1.1.1 in my project.
What I did was just updating the version to 2.3 and the issue was solved.

Related

Spring Security custom HTML login page not recognised using thymeleaf

i have followed this link for all configurations to set link up my custom html page for login. However, when i access localhost/login, i am faced with error status 500.
I am unable to render a simple html page when I access localhost:8080/login.
Are there extra configurations needed?
Should the html page be located at templates folder? How does the application know it should render "login.html" ?
Is my controller being recognised?
config
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
#EnableWebSecurity
#Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().fullyAuthenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll();
}
}
controller
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
#Controller
public class LoginController {
#GetMapping("/login")
public String login() {
return "login";
}
}
replacing "login" above with below also shows error.
<h1> Login page <h1>
dependencies in pom.xml
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.ldap</groupId>
<artifactId>spring-ldap-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-ldap</artifactId>
</dependency>
<dependency>
<groupId>com.unboundid</groupId>
<artifactId>unboundid-ldapsdk</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
<version>1.4.193</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
login.html is located in templates.
error message:
javax.servlet.ServletException: Circular view path [login]: would dispatch back to the current handler URL [/login] again. Check your ViewResolver setup! (Hint: This may be the result of an unspecified view, due to default view name generation.)
My folder hierachy:
#RestController is a composed annotation that is itself meta-annotated with #Controller and #ResponseBody indicating a controller whose every method inherits the type-level #ResponseBody annotation and therefore writes directly to the response body vs view resolution and rendering with an HTML template.
That's why I advised replacing #Controller by #RestController.
You will not get static resources in your html file. to get this you have to add this.
#Override
public void configure(WebSecurity web) throws Exception {
web
.ignoring()
.antMatchers("/resources/**", "/static/**", "/css/**", "/js/**", "/images/**");
}
Its behaving as if it cant find your controller. Did you put it in the same package as the main class or deeper in the hierachy?
The component scan which looks for the controller annotation scans from the package your main class is in and beneath it. If you put the controller further out in your folder hierachy it isnt found.
Ok, I have managed to resolved the error but was not conclusive of the cause of it.
The ultimate fix was to keep the mapping url to "/login" in the LoginController and SecurityConfiguration files, and to changed the naming of my html and thymeleaf's references of it in my controller file and html file name as per below.
#Controller
public class LoginController {
#GetMapping("/login")
public String login2() {
return "login2";
}
}
The main reason I would believe has been causing this is because my application and package name were also named "login" which made thymeleaf unable to refer to the right file properly. I have selected the answer above as best answer for others who faced the issue due to different reasons to try out the same experiment to obtain their useful result.

Spring Boot + WebFlux. How to display a simple index.jsp?

Am trying to use spring webflux in a spring boot application to try to display a simple jsp page. For the life of me, am not able to make it work. It's not able to find the "view". It works fine if I use Spring webmvc instead.
Here's the simple setup:
pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
Here's the controller:
#Controller
public class WebController
{
#RequestMapping(value = { "/", "/index" }, headers = "Accept=text/html", method = RequestMethod.GET, produces = "text/html")
public Mono<String> index() {
return Mono.just ("index");
}
}
application.properties:
(Though I doubt this may not work with webflux)
spring.mvc.view.prefix=/jsp/
spring.mvc.view.suffix=.jsp
Folder structure:
Here's the folder structure where index.jsp is (I know index.jsp is in lots of places only because I was trying to see where I need to put it to make it work):
Finally here's the error I get when I try to load: https://localhost:8443/
(Partial stack)
java.lang.IllegalStateException: Could not resolve view with name 'index'. java.lang.IllegalStateException: Could not resolve view with name 'index'.
at org.springframework.web.reactive.result.view.ViewResolutionResultHandler.lambda$resolveViews$3(ViewResolutionResultHandler.java:278)
Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException:
Error has been observed at the following site(s):
|_ checkpoint ⇢ Handler com.xxxxxxx.xxxxxxx.controller.WebController#index() [DispatcherHandler]
Stack trace:
at org.springframework.web.reactive.result.view.ViewResolutionResultHandler.lambda$resolveViews$3(ViewResolutionResultHandler.java:278)
at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onNext(FluxMapFuseable.java:107)
at reactor.core.publisher.Operators$MonoSubscriber.complete(Operators.java:1755)
at reactor.core.publisher.MonoCollectList$MonoCollectListSubscriber.onComplete(MonoCollectList.java:121)
at reactor.core.publisher.FluxConcatMap$ConcatMapImmediate.drain(FluxConcatMap.java:359)
at reactor.core.publisher.FluxConcatMap$ConcatMapImmediate.onComplete(FluxConcatMap.java:268)
at reactor.core.publisher.Operators.complete(Operators.java:135)
What do I need to do to show a simple jsp page? Embedded server is Tomcat and deployed as jar file in spring boot.
Note: Displaying static files work. So this url works just fine: https://localhost:8443/js/test.js

Parse JSON using Spring SPEL

Can someone tell me why this does not work:
#Test
public void should_parse_json() {
Expression expression = new SpelExpressionParser().parseExpression("#jsonPath(get('JsonData'), '$.someData')");
Map<String, Object> data = new HashMap<>();
data.put("JsonData", "{\"someData\": 100}");
StandardEvaluationContext context = new StandardEvaluationContext(data);
context.addPropertyAccessor(new JsonPropertyAccessor());
assertThat(expression.getValue(context, Object.class)).isEqualTo(100);
}
I get error "org.springframework.expression.spel.SpelEvaluationException: EL1006E: Function 'jsonPath' could not be found"
And I have following jar in classpath:
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path</artifactId>
</dependency>
The SPEL documentation did not help me.
Such a #jsonPath() SpEL function is a part of Spring Integration infrastructure: https://docs.spring.io/spring-integration/docs/current/reference/html/spel.html#spel-functions.
It's not going to work with the plain Spring and only SpEL.
However I see that you use a JsonPropertyAccessor. This one indeed a part of Spring Integration and you definitely have that in your classpath.
From here I think you just miss to register a SpEL function into the StandardEvaluationContext: https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/core.html#expressions-ref-functions
context.registerFunction("jsonPath", BeanUtils.resolveSignature("evaluate", JsonPathUtils.class));
Add dependencies.
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-core</artifactId>
</dependency>
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path</artifactId>
<version>2.7.0</version>
</dependency>
Register SpEL function
evaluationContext.registerFunction("jsonPath", BeanUtils.resolveSignature("evaluate", JsonPathUtils.class));

De-serialization error spring boot reactive

I have a simple controller
#RestController
#RequestMapping("path")
public class MyController {
#PostMapping(consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public Flux<SomeObject> run(#RequestBody Flux<RequestObject> request){
//do something and return flux
}
...
}
On calling this url I'm getting the exception
"Type definition error: [simple type, class reactor.core.publisher.Flux]; nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Can not construct instance of reactor.core.publisher.Flux (no Creators, like default construct, exist): abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information\n at [Source: (PushbackInputStream); line: 1, column: 1]
I understand this error and usually, I would just add an
annotation if needed
#JsonDeserialize(as = SomeConcreteClass.class)
But in this case, to which Flux concrete example should I bind? Also, Doesn't Spring boot has a default auto-deserializers for Reactor Types (Mono, Flux)?
My pom (relevant stuff):
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-commons</artifactId>
</dependency>
You're actually using Spring MVC right now.
Remove the spring-boot-starter-web and make sure no other dependency brings it transitively.

Missing dependencies for HttpServletRequest with Jersey

I have a Standalone Jersey server running at the beginning of my JunitTest. I'm testing if my JaxRS controller works, as well as my custom HttpClient. Please note that I've always been able to use this JaxRsResourceController embedded in glassfish.
Here is the JaxRsController (light version)
#Path("root")
public class JaxRsResourceController implements
ResourceController<HttpServletRequest> {
#Context
private UriInfo context;
#Context
HttpServletRequest request;
#Context
HttpServletResponse response;
#GET
public String hello(){
System.out.println("Uri is "+this.context.getBaseUri().toString());
return "Hello "+peoples;
}
}
I have no problem with the client, but when I start the server, I have :
GRAVE: The following errors and warnings have been detected with resource and/or provider classes:
SEVERE: Missing dependency for field: javax.servlet.http.HttpServletRequest com.robustaweb.library.rest.controller.implementation.JaxRsResourceController.request
SEVERE: Missing dependency for field: javax.servlet.http.HttpServletResponse com.robustaweb.library.rest.controller.implementation.JaxRsResourceController.response
at com.sun.jersey.api.container.httpserver.HttpServerFactory.create(HttpServerFactory.java:172)
at com.robustaweb.library.rest.server.JerseyServer.startServer(JerseyServer.java:44)
Basically it says that at the #Context injection time, there is no dependency on the HttpServletRequest.
However if I remove the #Context annotations on request and response, but keep it for UriInfo context, it's ok, and I can read the Uri.
I changed a few times the Maven pom wich is now to force the libs in:
<dependencies>
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-server</artifactId>
<version>1.14</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
</dependency>
<dependency>
<groupId>javax.ws.rs</groupId>
<artifactId>jsr311-api</artifactId>
<version>1.1.1</version>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.1</version>
</dependency>
</dependencies>
Any idea ?
servlet dependencies were separated to another module, try adding
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-servlet</artifactId>
<version>1.14</version>
</dependency>
to your pom.
It was not easy, but I found out. The thing is that in my JUnit test, I was creating the server like this :
HttpServer server = HttpServerFactory.create(url);
But that way, you create a lightweight container that does not have servlet containers, and so is the failure reason. So in order to have it all, I used the jersey-test-framework that allow to use the Grizzly web container (or even Embedded glassfish).
Here is the maven :
<dependencies>
<dependency>
<groupId>javax.ws.rs</groupId>
<artifactId>jsr311-api</artifactId>
<version>1.1.1</version>
</dependency>
<!-- Unit test are using jersey server directly -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.sun.jersey.test.framework</groupId>
<artifactId>jersey-test-framework</artifactId>
<version>1.0.3</version>
<scope>test</scope>
</dependency>
</dependencies>
Here is the JerseyServerTest : note that it extends JerseyTest
public class JerseyServerTest extends JerseyTest {
protected String baseUri = "http://localhost:" + TestConstants.JERSEY_HTTP_PORT + "/";
public JerseyServerTest() throws Exception {
super("com.robustaweb.library.rest.controller");
/*
It's possible to NOT call the super() but to manually do :
1) ApplicationDescriptor appDescriptor = new ApplicationDescriptor()
.setRootResourcePackageName(resourcePackageName) // resource packages
.setContextPath(contextPath) //context of app
.setServletPath(servletPath); // context of spi servlet
2)setupTestEnvironment(appDescriptor);
*/
}
#Test
public void testHelloWorldRequest() {
SunRestClient client = new SunRestClient(baseUri + "root");
String result = client.GET("", null);
System.out.println(result);
}
#Test
public void testDeleteRequest() {
SunRestClient client = new SunRestClient(baseUri + "root");
String result = client.DELETE("john", null);
System.out.println(result);
}
}
And finally the Resource file, that contains #GET and #DELETE
#Path("root")
public class JaxRsController extends JaxRsResourceController{
List<String> peoples = new ArrayList<String>();
#GET
public String hello(){
System.out.println("Uri is "+getUri());
return "Hello "+peoples;
}
#DELETE
#Path("{name}")
public String deletePeople(#PathParam("name") String name){
System.out.println("deleting "+name);
this.peoples.remove(name);
return String.valueOf(peoples.size());
}
}
And now it works !
I had some help in this article, and there is a small chapter on the documentation. Beeing able to attach the source code of the Jersey framework really helped, so thantks to IntelliJ also.

Resources