Spring boot annotation confusion - java

I am creating a spring boot application based on an assignment in an online MOOC. The course is over and I am just trying the assignment on my own.
THis is my pom.xml file:
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework.samples.service.service</groupId>
<artifactId>VideoManagerServer</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<properties>
<!-- Generic properties -->
<java.version>1.6</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<!-- Web -->
<jsp.version>2.2</jsp.version>
<jstl.version>1.2</jstl.version>
<servlet.version>2.5</servlet.version>
<!-- Spring -->
<spring-framework.version>3.2.3.RELEASE</spring-framework.version>
<!-- Hibernate / JPA -->
<hibernate.version>4.2.1.Final</hibernate.version>
<!-- Logging -->
<logback.version>1.0.13</logback.version>
<slf4j.version>1.7.5</slf4j.version>
<!-- Test -->
<junit.version>4.11</junit.version>
</properties>
<dependencies>
<!-- Spring MVC -->
<!-- Other Web dependencies -->
<!-- Spring and Transactions -->
<!-- Logging with SLF4J & LogBack -->
<!-- Hibernate -->
<!-- Test Artifacts -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring-framework.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>1.2.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>4.1.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.1.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jersey</artifactId>
<version>1.2.5.RELEASE</version>
</dependency>
</dependencies>
THis is my controller:
import java.util.ArrayList;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.PathVariable;
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.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import com.hakeem.videoserver.model.Video;
import com.hakeem.videoserver.service.VideoService;
#RestController
#RequestMapping(value="/api",consumes=MediaType.APPLICATION_JSON_VALUE,produces=MediaType.APPLICATION_JSON_VALUE)
public class VideoController {
#Autowired
VideoService videoService;
#RequestMapping(value="/add",method=RequestMethod.POST)
public void addVideo(#RequestBody Video video){
videoService.addVideo(video);
}
#RequestMapping(value="/all",method=RequestMethod.GET)
public #ResponseBody ArrayList<Video> getAllVideos(HttpServletResponse response){
ArrayList<Video> videos;
videos = videoService.getAllVideos();
if(videos.size() == 0){
response.setStatus(HttpStatus.NO_CONTENT.value());
}
return videos;
}
#RequestMapping(value="/delete",method=RequestMethod.DELETE)
public void deleteVideo(#RequestBody Video video){
videoService.deleteVideo(video);
}
#RequestMapping(value="/find/{id}")
public #ResponseBody Video findVideo(#PathVariable int id, HttpServletResponse response){
return videoService.findVideo(id);
}
#RequestMapping(value="/testing",method=RequestMethod.GET,produces=MediaType.APPLICATION_JSON_VALUE)
public #ResponseBody int testEndPoint(HttpServletResponse response){
return 10;
}
}
The problem is my addVideo endpoint only works when I annotate with #RestController but not when I annotate with #Controller.
However my testEndpoint method works when I annotate with #RestController and #Controller
However if I add #ResponseBody to the Class or the addVideo method then it works.
Using the Postmat plugin on Chrome, when I send the following Post Request:
http://localhost:8080/api/add
With this body for a video:
{
"id":1,
"title":"test",
"contentType":"test1",
"dataUrl":"testurl",
"starRating":"5",
"duration":7
}
I get this message in Postman:
{ "timestamp": 1440349612613,
"status": 405,
"error": "Method Not Allowed",
"exception":"org.springframework.web.HttpRequestMethodNotSupportedException", "message": "Request method 'POST' not supported", "path": "/api/add"
}
And this message in my eclipse console:
WARN PageNotFound - Request method 'POST' not supported
But the video is add and can be retrieved without any errors if I had #ResponseBody to the class or the method return type.
Therefore the main question is:
Why do I need to add #ResponseBody if the addVideo is not returning anything.

There were two questions asked here:
Why #RestController works, but #Controller does not?
If you check the documentation, you will see that RestController is
A convenience annotation that is itself annotated with #Controller
and #ResponseBody.
Why do you need to add #ResponseBody if the return type is void?
The reason for this is that even if there is no actual value you want to return, there will be a response sent to the client (at least an HTTP OK). For this reason you need to use one of these:
#ResponseBody
#ResponseStatus(value = HttpStatus.OK)

Related

Whitelabel page error for graphql mapping

I'm new to spring boot and trying to learn how to implement graphql. #GetMapping and #PostMapping work as normal but when I try to use #QueryMapping or #SchemaMapping, following the Spring documentation I get the whitelabel error page. Playing around I'm able to guess that it's not recognizing these two as mapping. I've checked my dependencies but I'll include them below in case, I've verified the project structure, and I've also tried changing from #Controller to #RestController to no avail.
Controller file
package com.example.GraphQLBooks.controller;
import com.example.GraphQLBooks.model.Book;
import com.example.GraphQLBooks.repository.BookRepository;
import org.springframework.graphql.data.method.annotation.QueryMapping;
import org.springframework.graphql.data.method.annotation.SchemaMapping;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
#RestController
public class BookController {
private final BookRepository bookRepository;
public BookController(BookRepository bookRepository) {
this.bookRepository = bookRepository;
}
//This works with #RestController but breaks with #Controller
#GetMapping("/test")
public String controllerTest(){
return "Hello";
}
//#SchemaMapping(typeName = "Query",value = "allBooks")
#QueryMapping
public List<Book> findAll() {
return bookRepository.findAll();
}
}
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.2</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>GraphQL-Books</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>GraphQL-Books</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-graphql</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-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webflux</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.graphql</groupId>
<artifactId>spring-graphql-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.graphql-java</groupId>
<artifactId>graphql-java</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
application.properties
spring.graphql.graphiql.enabled=true
I tested the repositories using #GetMapping and they output as expected and when I got to my "/test" mapping it outputs Hello as expected. Any insight and tips are appreciated, and let me know if more info is needed.
Figured it out. My schema had a typo had schema.graphql instead of schema.graphqls and it should be localhost:8080/graphiql not localhost:8080/graphql dumb mistakes I know. And you can't use #RestController with graphql has to be #Controller which makes sense because we're not using a Rest Api.
Had similar issue. The problem was graphqls file wasn't placed in the right folder.
Spring boot graphQL looks for the graphqls file in the following directory by default: ~/src/main/resources/graphql/schema.graphqls

Java Validation messages are not thrown when validations fail in depending class coming from JAR file

I am developing a Quarkus service application let's call it Project2, this application depends on Project1 which has the Book class with Validation Annotations.
When an invalid JSON is provided then I do not get the invalid message from the Book class. However, if I place the Book class within Poject2 directly without any dependency then I get the error messages.
I would like to know how to get the error messages from the Dependency class which are coming from JAR?
Following is my Project2 which has the BookService:
import org.project1.com.Book;
import javax.validation.Valid;
import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.core.MediaType;
#Path("/api")
public class BookService {
#Path("/generateTestData")
#POST
#Consumes(MediaType.APPLICATION_JSON)
public void generateTestData(#Valid final Book book){
System.out.println("After Successful Validation : " + book.toString());
}
}
Following is my Project1 Book class:
import lombok.Data;
import lombok.ToString;
import javax.validation.Valid;
import java.util.List;
#Data
#ToString
public class Book {
#NotNull(message="Book ID cannot be NULL")
#Min(value = 1, message="Book ID cannot be less than 1")
private int bookId;
#NotNull(message="Author name cannot be NULL")
private String author;
}
When I make a invalid request from Postman to http://localhost:8080/api/generateTestData then I do not get any validation messages (bookId should be >= 1 to be a valid input):
{
"bookId" : 0,
"author" : "Batman"
}
Can someone please let me know what's wrong here and how can I fix the issue? I have added the following dependency within my pom.xml:
<dependencies>
<dependency>
<groupId>org.project1.com</groupId>
<artifactId>project1</artifactId>
<version>1.0.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-arc</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-config-yaml</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-smallrye-openapi</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-container-image-jib</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.22</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-hibernate-validator</artifactId>
</dependency>
</dependencies>
I think y need to add this dependency to get your rest endpoint work otherwise y will get an error invalid MediaType from the resteasy library
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy-jackson</artifactId>
</dependency>
When you add the dependency, Your endpoint function will be called and you will get 400 (Bad request) and the details of the invalid constraints returned in the response.

javax.validation.constraints does not work with Spring web

Here is my controller:
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
#Slf4j
#RestController
#RequestMapping("/auth-api")
public class AuthController {
#PostMapping("/register")
public RequestResultJSON<String> register(#RequestParam #NotEmpty String username,
#RequestParam #NotEmpty String password,
#RequestParam #NotEmpty String passwordConfirm);
And i use this dependencies in my pom.xml file
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.0.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>2.0.1.Final</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
When I make a post request I expect that method execution will not happen, but it happens with incorrect parameters
Request: http://localhost:8080/auth-api/register?username&password&passwordConfirm
And register method execute with username="", password="", passwordConfirm=""
you need to add #Validated annotation on your controller, which marks it to be validated.
#Slf4j
#Validated
#RestController
#RequestMapping("/auth-api")
public class AuthController {
// your code
}
Additionally, you have to make sure you have one validator implementation like hibernate-validator in your classpath.
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.0.10.Final</version>
</dependency>
If not already present, you can add above dependency in your pom.xml or any other suitable version from here

Spring beans are not initializing in Spring REST

My Spring beans are not getting initialized in Spring Java Config which I am using to create a sample Spring REST Application(As No Web.xml is required I have deleted it) . And also getting 404 while calling the REST endpoint /dest/types.
Can anyone please help. Project Structure
Pom.xml :
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-
instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.travel</groupId>
<artifactId>patcyyRestApp</artifactId>
<packaging>war</packaging>
<version>0.0.1-SNAPSHOT</version>
<name>patcyyRestApp Maven Webapp</name>
<url>http://maven.apache.org</url>
<properties>
<springframework.version>5.0.9.RELEASE</springframework.version>
<jackson.library>2.9.6</jackson.library>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<hibernate.core.version>5.3.6.Final</hibernate.core.version>
<javax.servlet.api.version>3.1.0</javax.servlet.api.version>
<lombok.version>1.18.12</lombok.version>
<apache.commons.version>3.9</apache.commons.version>
<failOnMissingWebXml>false</failOnMissingWebXml>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${springframework.version}</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>${javax.servlet.api.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.library}</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>${hibernate.core.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>${apache.commons.version}</version>
</dependency>
</dependencies>
<build>
<finalName>patcyyRestApp</finalName>
</build>
</project>
Dispatch Initializer :
package com.patcyy.rest.config;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
public class PatcyyDispatcherServletInitializer extends
AbstractAnnotationConfigDispatcherServletInitializer {
#Override
protected Class<?>[] getRootConfigClasses() {
return null;
}
#Override
protected Class<?>[] getServletConfigClasses() {
System.out.println("Inside getServletConfigClasses");
return new Class[] { PatcyyConfig.class };
}
#Override
protected String[] getServletMappings() {
System.out.println("Inside mapping");
return new String[] { "/patcyy" };
}
}
Config :
package com.patcyy.rest.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
#Configuration
#EnableWebMvc
#ComponentScan("com.patcyy.rest")
public class PatcyyConfig {
}
Controller :
package com.patcyy.rest.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.patcyy.rest.response.DestinationTypes;
import com.patcyy.rest.service.IDestinationService;
#RestController
#RequestMapping(path = "/dest", consumes = MediaType.APPLICATION_JSON_UTF8_VALUE, produces =
MediaType.APPLICATION_JSON_UTF8_VALUE)
public class DestinationController extends BaseController {
private final IDestinationService iDestinationService;
/**
* #param iDestinationService
*/
public DestinationController(#Autowired IDestinationService iDestinationService) {
super();
this.iDestinationService = iDestinationService;
}
#GetMapping("/types")
public ResponseEntity<List<DestinationTypes>> getDestinationTypes() {
List<DestinationTypes> destTypes = iDestinationService.getDestinationTypes();
return new ResponseEntity<List<DestinationTypes>>(destTypes, HttpStatus.OK);
}
}
I had to do two modifications to resolve the issue.
As i am using Java Config only, I had deleted the Web.xml. So i had to make changes in server.xml for Tomcat as :
<Context path="/patcyyRestApp" reloadable="true" docBase="D:\battleGround\patcyyRestApp\target\patcyyRestApp"/></Host>
I had to update the servlet mapping in the Dispatcher Intializer as :
return new String[] { "/patcyy/*" };
After making this two changes, It is working fine now.
Please take a look into this Tomcat without web xml
Please share console log for further investigation. As per my understanding after seeing above code , you can replace
"/patcyy" with only "/"
in getServletMappings() method.
your requested resource url will be like:
http://{hostname:port}/patcyyRestApp/dest/types
It's occurring due to invalid url servlet mapping.
i think the problem is in ComponentScan().
#ComponentScan annotation along with #Configuration annotation to specify the packages that we want to be scanned. #ComponentScan without arguments tells Spring to scan the current package and all of its sub-packages.
in your case you need to add basepackages="com.patcyy.rest"

are there any security modules in spring boot without spring security

I'm using spring boot version(2.0.1), and I have a problem with security, so when I try to make a request with ajax like this :
$.ajax({
url : "http://localhost:8080/utilisateurs/addUser",
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
type : "POST",
data : user,
dataType : "application/json"
}
i'm getting an HTTP error 403 , i know the meaning of this error ( the user can log in the server but don't have the right permission ) but the problem is i'm not using any module of security this is my dependancy pom.xml file :
<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-data-rest</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jersey</artifactId>
</dependency>-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-rest-hal-browser</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
so who can block the request, is there any internal module in spring boot how can enable the security,
see my previous post
thank you in advance.
the answer is to manage the CORS on the server side with adding #CrossOrigin annotation to the web service, the web service become like this :
package com.sid.webService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.PathVariable;
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;
import com.sid.dao.entity.Utilisateur;
import com.sid.metier.IMetierUtilisateur;
#Component
#RestController
#RequestMapping("/utilisateurs")
public class webServiceUtilisateur {
#Autowired
private IMetierUtilisateur mr;
#CrossOrigin
#RequestMapping(value="/addUser",method=RequestMethod.POST)
public boolean addUser(#RequestBody Utilisateur u)
{
try
{
mr.ajouterUtilisateur(u);
return true;
}
catch(Exception e)
{
System.out.println(e.getMessage());
return false;
}
}
}
and if you want more customization, add all the domains you are going to be accessing your spring backend from, and make them in the attribute origins like this :
#CrossOrigin(origins="http://localhost:8080/utilisateurs/")
#RequestMapping(value="/addUser",method=RequestMethod.POST)
public boolean addUser(#RequestBody Utilisateur u)
{
try
{
mr.ajouterUtilisateur(u);
return true;
}
catch(Exception e)
{
System.out.println(e.getMessage());
return false;
}
}
}

Categories