Guys the issue is I'm getting the error
Request method 'POST' not supported
Here is my controller :
package com.mintad.spring.controllers;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.List;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import com.mintad.common.beans.Gouvernorate;
import com.mintad.common.service.GouvernorateService;
#RestController
#RequestMapping("/data")
public class DataController {
private static final Logger LOGGER = LogManager.getLogger(DataController.class);
#Autowired
private GouvernorateService gouvernorateService;
#RequestMapping(value = "/gouvernorates", method = RequestMethod.GET, produces = { "application/json" })
public ResponseEntity<List<Gouvernorate>> getGouvernorates() throws FileNotFoundException {
return new ResponseEntity<List<Gouvernorate>>(gouvernorateService.findAll(), HttpStatus.OK);
}
#RequestMapping(value = "/addGouvernorate", method = RequestMethod.POST, produces = { "application/json" })
public #ResponseBody ResponseEntity<Gouvernorate> addGouvernorate(BindingResult result, Model model, #RequestParam(name = "name") String name,
#RequestParam(name = "delegation") String delegation, #RequestParam(name = "district") String district,
#RequestParam(name = "postalCode") String postalCode) throws IOException {
LOGGER.info("addGouvernorate called");
Gouvernorate gouvernorate = new Gouvernorate(name, delegation, district, postalCode);
gouvernorateService.addGouvernorat(gouvernorate);
return new ResponseEntity<Gouvernorate>(gouvernorate, HttpStatus.OK);
}
#RequestMapping(value = "/addTest", method = RequestMethod.POST)
public #ResponseBody String addTest(BindingResult result, Model model) {
LOGGER.info("addGouvernorate called");
Gouvernorate gouvernorate = new Gouvernorate("test", "test", "test", "test");
gouvernorateService.addGouvernorat(gouvernorate);
return "Your Professional Details Updated";
}
}
I've tried so many solutions but in vain.
I'm calling the controller method as follows using chrome Postman application :
http://localhost:8080/mintad/data/addTest (POST)
http://localhost:8080/mintad/data/addGouvernorate?name=test&delegation=test&district=test&postalCode=test (POST too)
I'll be thankful for any help !
The controller version I've added is correct but it works only if I disabled crsf in my security configuration class ass follows :
package com.mintad.spring.security;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.ComponentScan;
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.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
#Configuration
#EnableWebSecurity
#ComponentScan
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
DataSource dataSource;
#Autowired
#Qualifier("customUserDetailsService")
UserDetailsService userDetailsService;
#Autowired
public void configureGlobalSecurity(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
// #formatter:off
http.csrf().disable().
authorizeRequests()
.antMatchers("/", "/home","/data").permitAll()
.and().formLogin().loginPage("/login")
.defaultSuccessUrl("/welcome").usernameParameter("username").passwordParameter("password")
.and().exceptionHandling().accessDeniedPage("/404");
// #formatter:off
}
}
Guys I was completely mistaken in the way I've concieved the controller.
So addTest method is replace by the following :
#RequestMapping(value = "/gouv/", method = RequestMethod.POST)
public ResponseEntity<String> addGouv(#RequestBody Gouvernorate gouv) {
log("addGouv called");
gouvernorateService.addGouvernorate(gouv);
return new ResponseEntity<String>("User created", HttpStatus.CREATED);
}
Where gouvernorateService is autwired.
Related
I have a Webflux application, where I have a ServerWebExchangeDecorator that decorates the request and responses. I have overrides to do some logging and then call the super methods.
This is what I have in code:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.ServerWebExchangeDecorator;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
import reactor.core.publisher.Mono;
#Component
public class LoggingWebFilter implements WebFilter {
#Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
return chain.filter(decorate(exchange));
}
private ServerWebExchange decorate(ServerWebExchange exchange) {
final ServerHttpRequest decoratedRequest = new LoggingServerHttpRequestDecorator(exchange.getRequest());
final ServerHttpResponse decoratedResponse = new LoggingServerHttpResponseDecorator(exchange.getResponse());
return new ServerWebExchangeDecorator(exchange) {
#Override
public ServerHttpRequest getRequest() {
return decoratedRequest;
}
#Override
public ServerHttpResponse getResponse() {
return decoratedResponse;
}
};
}
}
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import reactor.core.publisher.Flux;
public class LoggingServerHttpRequestDecorator extends ServerHttpRequestDecorator {
private static final Logger logger = LoggerFactory.getLogger(LoggingServerHttpRequestDecorator.class);
public LoggingServerHttpRequestDecorator(ServerHttpRequest delegate) {
super(delegate);
}
#Override
public Flux<DataBuffer> getBody() {
logger.info("getBody method");
return super.getBody();
}
}
import org.reactivestreams.Publisher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
import reactor.core.publisher.Mono;
public class LoggingServerHttpResponseDecorator extends ServerHttpResponseDecorator {
private static final Logger logger = LoggerFactory.getLogger(LoggingServerHttpResponseDecorator.class);
public LoggingServerHttpResponseDecorator(ServerHttpResponse delegate) {
super(delegate);
}
#Override
public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
logger.info("writeWith method");//THIS LINE IS NOT EXECUTED WHEN AN EXCEPTION IS THROWN
return super.writeWith(body);
}
#Override
public Mono<Void> writeAndFlushWith(Publisher<? extends Publisher<? extends DataBuffer>> body) {
logger.info("writeAndFlushWith method");
return super.writeAndFlushWith(body);
}
}
When I do a happy path with a POST request, this works fine, but when an exception is thrown, the Response Decorator is omitted and my custom code is not being executed.
This is a controller code to replicate the issue:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
#RestController
#RequestMapping("/decorator-demo")
public class DecoratorDemoController {
/** The Constant logger. */
private static final Logger logger = LoggerFactory.getLogger(DecoratorDemoController.class);
#PostMapping(produces = MediaType.APPLICATION_STREAM_JSON_VALUE, consumes = MediaType.APPLICATION_STREAM_JSON_VALUE)
public Mono<ResponseEntity<String>> postData(#RequestBody String id) {
logger.info("attempting to post the data");
if(id.length() == 1){
Mono<String> created = Mono.just(id);
return created.flatMap(vo -> Mono.just(ResponseEntity.status(HttpStatus.CREATED).body(vo)));
}
throw new IllegalArgumentException("String length must be 1");
}
}
When I post a single character, I have the logs I am expecting:
LoggingServerHttpRequestDecorator : getBody method
DecoratorDemoController : attempting to post the data
LoggingServerHttpResponseDecorator : writeWith method
But when I post more than one character, this is the logs I am having:
LoggingServerHttpRequestDecorator : getBody method
DecoratorDemoController : attempting to post the data
AbstractErrorWebExceptionHandler : [0b933716] 500 Server Error for HTTP POST "/decorator-demo"
Am I doing something wrong, or missing something?
Try this
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import org.reactivestreams.Publisher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.filter.NettyWriteResponseFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
#Component
public class ResponseInterceptorFilter implements Ordered, GlobalFilter {
private static final Logger log = LoggerFactory.getLogger(ResponseInterceptorFilter.class);
public ResponseInterceptorFilter(SqsPublisher sqsPublisher) {
this.sqsPublisher = sqsPublisher;
}
#Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpResponse response = exchange.getResponse();
DataBufferFactory dataBufferFactory = response.bufferFactory();
ServerHttpResponseDecorator decoratedResponse = new ServerHttpResponseDecorator(response) {
#Override
public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
if (!(body instanceof Flux)) {
return super.writeWith(body);
}
Flux<? extends DataBuffer> flux = (Flux<? extends DataBuffer>) body;
return super.writeWith(flux.buffer().map(dataBuffers -> {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
dataBuffers.forEach(buffer -> {
// three byte copies here
byte[] array = new byte[buffer.readableByteCount()];
buffer.read(array);
try {
outputStream.write(array);
} catch (IOException e) {
// TODO: need catch?
}
DataBufferUtils.release(buffer);
});
byte[] write = outputStream.toByteArray();
String responseBody = new String(write);
log.debug("Response ----> {}", responseBody);
response.getHeaders().setContentLength(write.length);
return dataBufferFactory.wrap(write);
}));
}
};
ServerWebExchange serverWebExchange = exchange.mutate().response(decoratedResponse).build();
return chain.filter(serverWebExchange);
}
#Override
public int getOrder() {
return NettyWriteResponseFilter.WRITE_RESPONSE_FILTER_ORDER - 1; // this is important
}
}
Yes, I know this question is already asked. But regard to this question I have a different problem as well. authenticationManager object of AuthenticationManager in SecurityServiceImpl class which is not authenticating the details which caused me to stuck at this point.
I have tried to give message after each step by which I can track the particular code whether they are working or not and I found in SecurityServiceImpl class that it does not work after this code
authenticationManager.authenticate(usernamePasswordAuthenticationToken);
I don't know this is just my understanding. Help me out of this problem and please provide me if you have better code snippet to solve this particular problem.
SecurityConfiguration
package com.demo.practice.configuration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
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.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
#Configuration
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
Logger logger = LoggerFactory.getLogger(SecurityConfiguration.class);
#Autowired
private UserDetailsService userDetailsService;
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/","/register","/login").permitAll()
.antMatchers("/student/**").hasAuthority("STUDENT")
.antMatchers("/admin/**").hasAuthority("ADMIN")
.anyRequest().authenticated()
.and()
.formLogin().loginPage("/login")
.defaultSuccessUrl("/dashboard");
http.csrf().disable();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder());
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Bean
public BCryptPasswordEncoder bCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}
}
UserServiceImpl
package com.demo.practice.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import com.demo.practice.model.Credentials;
import com.demo.practice.model.Role;
import com.demo.practice.model.User;
import com.demo.practice.repository.UserRepository;
#Service
public class UserServiceImpl implements UserServiceInterface {
#Autowired
private BCryptPasswordEncoder encoder;
#Autowired
UserRepository userRepo;
#Override
public void saveUser(User user,Credentials credential) {
user.setCredential(credential);
user.getCredential().setUsername(user.getEmail());
user.getCredential().setRoles(Role.STUDENT);
user.getCredential().setPassword(encoder.encode(user.getCredential().getPassword()));
userRepo.save(user);
}
#Override
public User findUserByEmail(String name) {
User user=userRepo.findUserByEmail(name);
return user;
}
}
UserDetailsServiceImpl
package com.demo.practice.service;
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.demo.practice.model.Credentials;
import com.demo.practice.repository.CredentialRepository;
#Service
public class UserDetailsServiceImpl implements UserDetailsService {
#Autowired
CredentialRepository credRepo;
#Override
#Transactional(readOnly = true)
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Credentials credential =credRepo.findByUsername(username);
List<GrantedAuthority> grantedAuthorities = new ArrayList<GrantedAuthority>();
grantedAuthorities.add(new SimpleGrantedAuthority(credential.getRoles().toString()));
return new org.springframework.security.core.userdetails.User(credential.getUsername(), credential.getPassword(), grantedAuthorities);
}
}
SecurityServiceImpl
package com.demo.practice.service;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.stereotype.Service;
#Service
public class SecurityServiceImpl implements SecurityServiceInterface {
#Autowired
private AuthenticationManager authenticationManager;
#Autowired
private UserDetailsService userDetailsService;
private static final Logger logger = LoggerFactory.getLogger(SecurityServiceImpl.class);
#Override
public String findLoggedInUsername() {
Object userDetails = SecurityContextHolder.getContext().getAuthentication().getDetails();
if (userDetails instanceof UserDetails) {
return ((UserDetails)userDetails).getUsername();
}
return null;
}
#Override
public void autologin(String username, String password) {
System.out.println("in autologin "+username);
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
System.out.println("in autologin at userdetails"+userDetails);
logger.info("after userdetails "+userDetails);
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(userDetails.getUsername(), userDetails.getPassword(),userDetails.getAuthorities());
logger.info("in autologin after usernamepasswordauthentication! ", usernamePasswordAuthenticationToken);
authenticationManager.authenticate(usernamePasswordAuthenticationToken);
logger.info("after authentication manager ", usernamePasswordAuthenticationToken);
if (usernamePasswordAuthenticationToken.isAuthenticated()) {
SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
logger.debug(String.format("Auto login %s successfully!", username));
}
else System.out.println("auto login failed");
}
}
you are missing one thing, if you want to have an authentication session based, you need to add WebAuthenticationDetails to the token like:
UsernamePasswordAuthenticationToken token =
new UsernamePasswordAuthenticationToken(principalUser, null, List.of(new SimpleGrantedAuthority(principalUser.getRole())));
token.setDetails(new WebAuthenticationDetails(request));
SecurityContextHolder.getContext().setAuthentication(token);
the WebAuthenticationDetails from the doc:
Records the remote address and will also set the session Id if a session already exists (it won't create one).
for more info about the login/logout process take a look at:
https://github.com/pezetem/spring-security-angular-skeleton/blob/master/src/main/java/com/pezetem/blog/code/spring_security_custom_authorizers/security/SecurityController.java
UPDATE:
ok, there could be another reason why it is not working for you. Spring Security adds default prefix to the roles. It is equal to ROLE_. It means that when you have an endpoint configured for .hasAuthority("STUDENT") role must be equal to ROLE_STUDENT, take a look at the line where you create a user, you assign role with Role.STUDENT and the enum value should be ROLE_STUDENT not STUDENT
I am trying to configure a Spring based app, where I want to configure two view resolvers. From my controller, if I return just the string name like "login", then it should be handled by the Thymeleaf resolver, whereas if the controller's method returns an object, then appropriate json view should be used. When I try to run my application as configured below, I get the following error
"Could not resolve view with name 'login' in servlet with name
'dispatcher'"
Requesting you guys to look at the Java classes below. The first is the configuration class, the second is the Controller I am trying to use.
package com.gojha.web;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.web.accept.ContentNegotiationManager;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer;
import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.view.ContentNegotiatingViewResolver;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.spring4.SpringTemplateEngine;
import org.thymeleaf.spring4.view.ThymeleafViewResolver;
import org.thymeleaf.templateresolver.ServletContextTemplateResolver;
import org.thymeleaf.templateresolver.TemplateResolver;
#Configuration
#EnableWebMvc
#ComponentScan("com.gojha.web")
public class WebConfig extends WebMvcConfigurerAdapter {
#Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
#Bean
public ViewResolver viewResolver(ContentNegotiationManager cnm) {
ContentNegotiatingViewResolver cnvr = new ContentNegotiatingViewResolver();
cnvr.setContentNegotiationManager(cnm);
return cnvr;
}
#Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
configurer.defaultContentType(MediaType.APPLICATION_JSON);
}
#Bean
public TemplateResolver templateResolver() {
TemplateResolver templateResolver = new ServletContextTemplateResolver();
templateResolver.setPrefix("/WEB-INF/templates/");
templateResolver.setSuffix(".html");
templateResolver.setTemplateMode("HTML5");
return templateResolver;
}
#Bean
public TemplateEngine templateEngine() {
SpringTemplateEngine templateEngine = new SpringTemplateEngine();
templateEngine.setTemplateResolver(templateResolver());
return templateEngine;
}
#Bean
public ViewResolver viewResolver() {
ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
viewResolver.setTemplateEngine((SpringTemplateEngine) templateEngine());
return viewResolver;
}
}
Controller
package com.gojha.web;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.client.RestTemplate;
import static org.springframework.web.bind.annotation.RequestMethod.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
#Controller
#RequestMapping("/")
public class LoginController {
private RestTemplate restTemplate;
private class Test {
private String a;
public Test() {
super();
}
public Test(String a) {
super();
this.a = a;
}
public String getA() {
return a;
}
public void setA(String a) {
this.a = a;
}
}
#Autowired
public LoginController(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
#RequestMapping(method=GET)
public String testing(){
return "login";
}
#RequestMapping(method=GET, produces="application/json")
public Test testing2(){
return new Test("wow");
}
}
I hope the code is self-explanatory.
I got it working by changing the configuration file and allocating orders to view resolvers. From what I understand, it looks like first it tries to resolve the view using ContentNegotiation, and if it fails, falls back to Thymeleaf resolver. I am marking this as the answer, if someone has a better approach, or a suggested correction, let me know.
package com.gojha.web;
import java.util.ArrayList;
import java.util.List;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.web.accept.ContentNegotiationManager;
import org.springframework.web.servlet.View;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.view.ContentNegotiatingViewResolver;
import org.springframework.web.servlet.view.json.MappingJackson2JsonView;
import org.thymeleaf.spring4.SpringTemplateEngine;
import org.thymeleaf.spring4.view.ThymeleafViewResolver;
import org.thymeleaf.templateresolver.ServletContextTemplateResolver;
import org.thymeleaf.templateresolver.TemplateResolver;
#Configuration
#EnableWebMvc
#ComponentScan("com.gojha.web")
public class WebConfig extends WebMvcConfigurerAdapter {
#Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
configurer.defaultContentType(MediaType.APPLICATION_JSON);
}
#Bean
public ViewResolver viewResolver() {
TemplateResolver templateResolver = new ServletContextTemplateResolver();
templateResolver.setPrefix("/WEB-INF/templates/");
templateResolver.setSuffix(".html");
templateResolver.setTemplateMode("HTML5");
SpringTemplateEngine templateEngine = new SpringTemplateEngine();
templateEngine.setTemplateResolver(templateResolver);
ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
viewResolver.setTemplateEngine(templateEngine);
viewResolver.setOrder(2);
return viewResolver;
}
#Bean
public ViewResolver cnViewResolver(ContentNegotiationManager cnm) {
ContentNegotiatingViewResolver cnvr = new ContentNegotiatingViewResolver();
cnvr.setContentNegotiationManager(cnm);
cnvr.setOrder(1);
List<View> views = new ArrayList<View>();
views.add(jsonView());
cnvr.setDefaultViews(views);
return cnvr;
}
#Bean
public View jsonView() {
MappingJackson2JsonView view = new MappingJackson2JsonView();
view.setPrettyPrint(true);
return view;
}
}
I am testing a spring mvc controller which gets a webservice client autowired and its been mocked vai mockito. But the mocking is not working. Invoking "verify(stuClient, times(1)).getAllStudents(sAndPCommand);" in test returns
Wanted but not invoked:
stuClient.getAllStudents(
com.xyz.crudserviceclient.utilitybeans.SortablePagedCommand#3028e50e
);
-> at com.xyz.controllers.StudentControllerTest.testGetHomePage(StudentControllerTest.java:101)
Actually, there were zero interactions with this mock.
at com.xyz.controllers.StudentControllerTest.testGetHomePage(StudentControllerTest.java:101).....
Below is my controller method in test:
#RequestMapping(value = "/getHomePage.do", method = RequestMethod.GET)
public ModelAndView getHomePage(#RequestParam(value = "first", required = false) Integer first,
#RequestParam(value = "max", required = false) Integer max, #RequestParam(value = "sortBy",
required = false) String sortBy,
#RequestParam(value = "sortDirection", required = false) String sortDir) {
ModelAndView mav = new ModelAndView("home");
SortablePagedCommand sortablePagedCommand = new SortablePagedCommand();
sortablePagedCommand.setFirst(first);
sortablePagedCommand.setMax(max);
sortablePagedCommand.setSort(sortBy);
sortablePagedCommand.setSortDir(sortDir);
PagedResult<StudentBean> students = studentServiceClient.getAllStudents(sortablePagedCommand);
List<StudentBean> studentList = students.getItems();
int noOfRecords = students.getUnfilteredItems();
int noOfPages = (int) Math.ceil(noOfRecords * 1.0 / max);
mav.addObject("sortByField", sortBy);
mav.addObject("sortDirField", sortDir);
mav.addObject("studentList", studentList);
mav.addObject("noOfPages", noOfPages);
mav.addObject("currentPage", first);
return mav;
}
And below is my test Class:
package com.xyz.controllers;
import static org.hamcrest.Matchers.allOf;
import static org.hamcrest.Matchers.hasItem;
import static org.hamcrest.Matchers.hasItems;
import static org.hamcrest.Matchers.hasProperty;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.is;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.forwardedUrl;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.model;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.view;
import java.sql.Date;
import java.util.Arrays;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import com.xyz.crudserviceclient.beans.StudentBean;
import com.xyz.crudserviceclient.client.StudentServiceClient;
import com.xyz.crudserviceclient.utilitybeans.PagedResult;
import com.xyz.crudserviceclient.utilitybeans.SortablePagedCommand;
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "file:WebContent/WEB-INF/dispatcher-servlet.xml" })
#WebAppConfiguration
public class StudentControllerTest {
private MockMvc mockMvc;
#InjectMocks
private StudentController controller;
#Mock
private StudentServiceClient stuClient;
#Autowired
private WebApplicationContext webAppContext;
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mockMvc = MockMvcBuilders.webAppContextSetup(webAppContext).build();
Mockito.reset(stuClient);
}
#SuppressWarnings("unchecked")
#Test
public void testGetHomePage() throws Exception {
StudentBean sb1 = new StudentBean();
sb1.setFirstName("Sai");
sb1.setLastName("Palaparthi");
sb1.setGender("Male");
sb1.setDOB(new Date(System.currentTimeMillis()));
sb1.setEmail("v.p#gmail.com");
sb1.setMobileNumber("8121157247");
sb1.setAddress("Huda");
sb1.setCourses(Arrays.asList("Math", "Chem"));
StudentBean sb2 = new StudentBean();
sb2.setFirstName("Viswanath");
sb2.setLastName("Palaparthi");
sb2.setGender("Male");
sb2.setDOB(new Date(System.currentTimeMillis()));
sb2.setEmail("v.p#gmail.com");
sb2.setMobileNumber("8121157248");
sb2.setAddress("Huda");
sb2.setCourses(Arrays.asList("Math"));
PagedResult<StudentBean> pResult = new PagedResult<StudentBean>(
Arrays.asList(sb1, sb2), 2);
pResult.setFirst(0);
pResult.setUnfilteredItems(2);
Integer first = 0;
Integer max = 5;
String sortBy = "firstname";
String sortDir = "asc";
SortablePagedCommand sAndPCommand = new SortablePagedCommand();
sAndPCommand.setFirst(first);
sAndPCommand.setMax(max);
sAndPCommand.setSort(sortBy);
sAndPCommand.setSortDir(sortDir);
Mockito.when(stuClient.getAllStudents(sAndPCommand))
.thenReturn(pResult);
mockMvc.perform(
get("/getHomePage.do").param("first", String.valueOf(first))
.param("max", String.valueOf(max))
.param("sortBy", sortBy)
.param("sortDirection", sortDir))
.andExpect(status().isOk())
.andExpect(view().name("home"))
.andExpect(forwardedUrl("/jsps/home.jsp"))
.andExpect(model().attribute("sortByField", is(sortBy)))
.andExpect(model().attribute("sortDirField", is(sortDir)))
.andExpect(model().attribute("noOfPages", 1))
.andExpect(model().attribute("currentPage", is(first)))
.andExpect(model().attribute("studentList", hasSize(2)))
.andExpect(
model().attribute(
"studentList",
hasItem(allOf(
hasProperty("firstName", is("Sai")),
hasProperty("lastName",
is("Palaparthi")),
hasProperty("gender", is("Male")),
hasProperty(
"dob",
is(new Date(System
.currentTimeMillis()))),
hasProperty("email",
is("v.p#gmail.com")),
hasProperty("mobileNumber",
is("8121157247")),
hasProperty("address", is("Huda")),
hasProperty("courses",
hasItems("Math", "Chem"))))))
.andExpect(
model().attribute(
"studentList",
hasItem(allOf(
hasProperty("firstName",
is("Viswanath")),
hasProperty("lastName",
is("Palaparthi")),
hasProperty("gender", is("Male")),
hasProperty(
"dob",
is(new Date(System
.currentTimeMillis()))),
hasProperty("email",
is("v.p#gmail.com")),
hasProperty("mobileNumber",
is("8121157248")),
hasProperty("address", is("Huda")),
hasProperty("courses", hasItems("Math"))))));
verify(stuClient, times(1)).getAllStudents(sAndPCommand);
verifyNoMoreInteractions(stuClient);
}
}
You're doing it all wrong - this is obviously an integration test so why involve Mockito at all; additionally, you're injecting mocks into the controller field but your tests are not invoking the controller field - they are interacting with the controller bean from your Spring context.
Take a look at this excellent tutorial, especially the unit test part.
To sum it up, your test setup needs to change, you don't need any of the annotations and also you have to use standalone MockMvc setup for your controller:
public class StudentControllerTest {
private MockMvc mockMvc;
#InjectMocks
private StudentController controller;
#Mock
private StudentServiceClient stuClient;
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
Mockito.reset(stuClient);
}
My issue is that I get always Request method 'POST' not supported !
I'm trying to test my rest web webservice. Here is the WS :
#RequestMapping(value = "/addGouvernorate", method = RequestMethod.POST, produces = { "application/json" })
public ResponseEntity<Gouvernorate> addGouvernorateee(#RequestBody Gouvernorate gouvernorate) throws IOException {
gouvernorateService.addGouvernorat(gouvernorate);
return new ResponseEntity<Gouvernorate>(gouvernorate, HttpStatus.OK);
}
i'm trying to call it with jsoup as follows :
package test.sofiane.rqs;
import java.io.IOException;
import org.jsoup.Connection.Method;
import org.jsoup.Connection.Response;
import org.jsoup.Jsoup;
import org.springframework.security.crypto.codec.Base64;
public class Test {
public static void main(String[] args) throws IOException {
String base64login = new String(Base64.encode("sof:1".getBytes()));
Response docs = Jsoup.connect("http://localhost:8080/site/data/addGouvernorate")
.data("name", "name")
.data("delegation", "delegation")
.data("district", "district")
.data("postalCode", "postalCode")
.method(Method.POST)
.execute();
}
}
I get the exception :
Exception in thread "main" org.jsoup.HttpStatusException: HTTP error fetching URL. Status=405, URL=http://localhost:8080/mintad/data/addGouvernorate
at org.jsoup.helper.HttpConnection$Response.execute(HttpConnection.java:459)
at org.jsoup.helper.HttpConnection$Response.execute(HttpConnection.java:434)
at org.jsoup.helper.HttpConnection.execute(HttpConnection.java:181)
at test.sofiane.rqs.Test.main(Test.java:21)
And with Postman application I get
Etat HTTP 405 - Request method 'POST' not supported
I'm using spring security also.
as follow
package com.site.spring.security;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.ComponentScan;
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.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
/**
* #author Sofiane
*/
#Configuration
#EnableWebSecurity
#ComponentScan
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
DataSource dataSource;
#Autowired
#Qualifier("customUserDetailsService")
UserDetailsService userDetailsService;
#Autowired
public void configureGlobalSecurity(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
// #formatter:off
http.authorizeRequests()
.antMatchers("/", "/home").permitAll()
//.antMatchers("/admin/**").denyAll()//.access("hasRole('ADMIN')")
.and().formLogin().loginPage("/login")
.defaultSuccessUrl("/welcome").usernameParameter("username").passwordParameter("password")
.and().exceptionHandling().accessDeniedPage("/404");
// #formatter:off
}
}
A guess : it can be the csrftoken which I have to send since I'm behind spring security but how to generate it ?
Please help because it's driving me insane guys