I have the following controller method:
#RequestMapping(method = RequestMethod.GET, value = "/account/{loginId:.+}")
public #ResponseBody CloudWebServiceResponse getLogin(#PathVariable(value = "loginId") String loginId) throws CloudWebServiceInvocationException {
return internalService.getLogin(progressId);
}
When is pass loginId as "abc.com", it gives 406 status code otherwise its working perfectly fine.
I have the following WebConfig file:
#Configuration
#Import(HibernateConfig.class)
#EnableWebMvc
// #EnableAsync()
// #EnableAspectJAutoProxy
#ComponentScan(basePackages = "com.azim.web.service.*", basePackageClasses = { WebSecurityConfig.class }, excludeFilters = { #ComponentScan.Filter(Configuration.class) })
public class WebConfig extends WebMvcConfigurationSupport {
#Override
protected void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
configurer.favorPathExtension(false).favorParameter(true).parameterName("mediaType").ignoreAcceptHeader(true).useJaf(false).defaultContentType(MediaType.APPLICATION_JSON)
.mediaType("xml", MediaType.APPLICATION_XML).mediaType("json", MediaType.APPLICATION_JSON).mediaType("html", MediaType.APPLICATION_JSON);
}
#Bean(name = "validator")
public Validator validator() {
return new LocalValidatorFactoryBean();
}
}
Its sending 406 status code for only .com and not for .randomvalue.
I tried adding jackson-core-asl and jackson-databind-asl jars suggested by other threads on stackoverdflow but nothing works for me.
Please help to sort out this issue.
Finally, I got the solution.
Instead of extending to WebMvcConfigurationSupport class it should extend to WebMvcConfigurerAdapter. Then the code becomes:
#Configuration
#Import(HibernateConfig.class)
#EnableWebMvc
// #EnableAsync()
// #EnableAspectJAutoProxy
#ComponentScan(basePackages = "com.azim.web.service.*", basePackageClasses = { WebSecurityConfig.class }, excludeFilters = { #ComponentScan.Filter(Configuration.class) })
public class WebConfig extends WebMvcConfigurerAdapter {
#Override
protected void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
configurer.favorPathExtension(false).favorParameter(true).parameterName("mediaType").ignoreAcceptHeader(true).useJaf(false).defaultContentType(MediaType.APPLICATION_JSON)
.mediaType("xml", MediaType.APPLICATION_XML).mediaType("json", MediaType.APPLICATION_JSON).mediaType("html", MediaType.APPLICATION_JSON);
}
#Bean(name = "validator")
public Validator validator() {
return new LocalValidatorFactoryBean();
}
}
Related
I have to call an API with the access token from the browser which is protected by Resource Server with spring boot 2.4.6
GET API - http://127.0.0.1:9090/api/user/benz#gmail.com
When I call the above API then the browser throws the following CORS blocked exception
No 'access-control-allow-origin' header is present on the requested resource
WebSecurityConfig
#EnableGlobalMethodSecurity(prePostEnabled = true)
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
private AuthEntryPoint authEntryPoint;
public WebSecurityConfig(AuthEntryPoint authEntryPoint){
this.authEntryPoint=authEntryPoint;
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable().exceptionHandling().authenticationEntryPoint(authEntryPoint)
.and().authorizeRequests().antMatchers("/api/contact").permitAll()
.and().authorizeRequests().antMatchers("/api/forget/**").permitAll()
.and().authorizeRequests().antMatchers("/api/user/register","/api/user/login").permitAll()
.and().authorizeRequests().anyRequest().authenticated()
.and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/api/contact")
.antMatchers("/api/forget/**")
.antMatchers("/api/user/register","/api/user/login");
}
}
CrossConfig
#Configuration
public class CrossConfig {
#Bean
public WebMvcConfigurer crossConfigurer(){
return new WebMvcConfigurer() {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**").allowedMethods("GET","POST","PUT","DELETE","OPTIONS")
.allowedHeaders("*");
}
};
}
}
ResourceServerConfig
#Configuration
#EnableResourceServer
#EnableConfigurationProperties(SecurityProperties.class)
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
/............./
}
Controller
#CrossOrigin(origins = "*",maxAge = 3600)
#RestController
#RequestMapping("/api/user")
public class VoyageUserController {
private VoyageUserService voyageUserService;
public VoyageUserController(VoyageUserService voyageUserService){
this.voyageUserService=voyageUserService;
}
#GetMapping(value = "/{id}",produces = {MediaType.APPLICATION_JSON_VALUE})
#PreAuthorize("hasAnyRole('ROLE_USER','ROLE_MODERATOR','ROLE_ADMIN')")
public ResponseEntity<VoyageUserResponse> getUser(#PathVariable("id") String email){
return (email.trim().isEmpty()) ?
new ResponseEntity<>(HttpStatus.BAD_REQUEST) :
new ResponseEntity<>(voyageUserService.findVoyageUser(email),HttpStatus.OK);
}
}
API Request From React using Axios
if(email){
axios.get(`http://127.0.0.1:9090/api/user/${email}`,{
headers:{
'Authorization':'bearer '+token}
})
.then(res=>{
const userDetail = res.data;
console.log(userDetail);
this.setState({
voyageUser:userDetail
});
this.setIsLogged(true);
}).catch(error=>{
console.log(error);
this.setIsLogged(false);
});
}
I have done all things which are available in StackOverflow as answers, But still, the browser throws CORS blocked exception.
Note - There are no syntax errors in my code, if the question contains something,just ignore.
Solved - The problem is identified as The ResourceServerConfig class is loading before WebSecurityConfig class that is the cause for CORS error, So i decided to do authorization configuration in ResourceServerConfig class.
ResourceServerConfig
#Configuration
#EnableResourceServer
#EnableConfigurationProperties(SecurityProperties.class)
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
#Autowired
private AuthEntryPoint authEntryPoint;
#Override
public void configure(HttpSecurity http) throws Exception {
http.cors().configurationSource(corsConfigurationSource()).and().csrf().disable().exceptionHandling().authenticationEntryPoint(authEntryPoint)
.and().authorizeRequests().antMatchers("/api/contact").permitAll()
.and().authorizeRequests().antMatchers("/api/forget/**").permitAll()
.and().authorizeRequests().antMatchers("/api/user/register","/api/user/login").permitAll()
.and().authorizeRequests().anyRequest().authenticated()
.and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("*"));
configuration.addAllowedHeader("*");
configuration.addAllowedMethod("*");
configuration.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
}
WebSecurityConfig
#EnableGlobalMethodSecurity(prePostEnabled = true)
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
}
It works with browsers without CORS error
springboot auto config TransactionManager and SqlFactory, it works well ,but when i use #Transactional the program wait in a daze, i flow the code and find
beanFactory.getBean(PlatformTransactionManager.class) is very slow.
but when i add this:
#Autowired
private PlatformTransactionManager platformTransactionManager;
it works. so what's the problem. i have no idea, and i'm using grpc with springboot now, please help thx;
#Configuration
#SpringBootApplication
#ComponentScan(value = "cn.com.autohome.autopay.wallet")
#EnableTransactionManagement
#EnableAutoConfiguration
#EnableAspectJAutoProxy(proxyTargetClass = true, exposeProxy = true)
public class AppMain extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(AppMain.class, args);
}
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return builder.sources(AppMain.class);
}
}
#Configuration
#MapperScan(basePackages = "cn.com.autohome.autopay.wallet.core.dal.mapper")
#ImportResource(locations = {"classpath:wallet-dal-bean.xml"})
public class WalletMyBatisConfig {
#Autowired
private DataBaseProperties dataBaseProperties;
#Primary
#Bean(name = "walletDataSource")
#ConfigurationProperties("spring.datasource.druid")
public DataSource masterDataSource() throws SQLException {
DruidDataSource dataSource = DruidDataSourceBuilder.create().build();
dataSource.setDriverClassName(dataBaseProperties.getWallet_data_source_driver_class());
dataSource.setUrl(dataBaseProperties.getWallet_data_source_url());
dataSource.setUsername(dataBaseProperties.getWallet_data_source_username());
dataSource.setPassword(dataBaseProperties.getWallet_data_source_password());
return dataSource;
}
}
I have a spring boot application and use camel with it, I read a file and then I try to inserted on my DB, everything is working good the only problem is that I try to use #transactional or transactionTemplate to make a rollback when an error occur but it doesn't make the rollback,
With the #transactional I add to my SpringBootApplication the #EnableTransactionManagement(proxyTargetClass=true) and in my class I add the #Transactional(rollbackFor = Exception.class)
These are my classes:
#SpringBootApplication
#EnableDiscoveryClient
#EnableTransactionManagement(proxyTargetClass=true)
public class MsArchivo510Application {
public static void main(String[] args) {
SpringApplication.run(MsArchivo510Application.class, args);
}
}
#Service
public class ArchivoBS implements Processor{
#Transactional(rollbackFor = Exception.class)
#Override
public void process(Exchange exchange) throws Exception {
//Here I execute stored procedure and one of them fail
}
}
With the transactioTemplate my class end up like this:
#Service
public class ArchivoBS implements Processor{
#Override
public void process(Exchange exchange) throws Exception {
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
#Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
try {
//Here I execute stored procedure and one of them fail
} catch (Exception e) {
e.printStackTrace();
status.setRollbackOnly();
}
}
});
}
}
Am I missing something?, Can someone help me with this issue?,
Thanks in advance
You're in a camel context and Spring-boot may have difficulties to work properly.
You could try to make your transaction operation in a spring service and inject it in you processor then add #Transaction on your service method and call it from your processor.
At the end I noticed that I need to specify to my data source the DataSourceTransactionManager, I have a class with the annotation #Configuration and there I can create multiples data source, so my class were like this:
#Configuration
public class Configuracion {
#Bean(name = "mysqlNocturno")
#ConfigurationProperties(prefix = "spring.nocturno")
public DataSource mysqlDataSourceNocturno() {
return DataSourceBuilder.create().build();
}
#Bean(name = "jdbcTemplateNocturno")
public JdbcTemplate jdbcTemplateNocturno(#Qualifier("mysqlNocturno") DataSource dsMySQL) {
return new JdbcTemplate(dsMySQL);
}
#Bean(name = "mysqlProduccion")
#Primary
#ConfigurationProperties(prefix = "spring.produccion")
public DataSource mysqlDataSourceProduccion() {
return DataSourceBuilder.create().build();
}
#Bean(name = "jdbcTemplateProduccion")
public JdbcTemplate jdbcTemplateProduccion(#Qualifier("mysqlProduccion") DataSource dsMySQL) {
return new JdbcTemplate(dsMySQL);
}
}
The documentation mention that the annotation #EnableTransactionManagement need to be added on my SpringBootApplication class but that is not necessary, it need to be added on my configuration class, so my class end up like this:
#Configuration
#EnableTransactionManagement
public class Configuracion {
#Bean(name = "mysqlNocturno")
#ConfigurationProperties(prefix = "spring.nocturno")
public DataSource mysqlDataSourceNocturno() {
return DataSourceBuilder.create().build();
}
#Bean(name = "jdbcTemplateNocturno")
public JdbcTemplate jdbcTemplateNocturno(#Qualifier("mysqlNocturno") DataSource dsMySQL) {
return new JdbcTemplate(dsMySQL);
}
#Bean(name = "transactionManagerNocturno")
public PlatformTransactionManager transactionManagerNocturno() {
return new DataSourceTransactionManager(mysqlDataSourceNocturno());
}
#Bean(name = "mysqlProduccion")
#Primary
#ConfigurationProperties(prefix = "spring.produccion")
public DataSource mysqlDataSourceProduccion() {
return DataSourceBuilder.create().build();
}
#Bean(name = "jdbcTemplateProduccion")
public JdbcTemplate jdbcTemplateProduccion(#Qualifier("mysqlProduccion") DataSource dsMySQL) {
return new JdbcTemplate(dsMySQL);
}
#Bean(name = "transactionManagerProduccion")
public PlatformTransactionManager transactionManagerProduccion() {
return new DataSourceTransactionManager(mysqlDataSourceProduccion());
}
}
With this configuration I only need to add the #transactional annotation to my class like #Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
#Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
#Override
public void altaArchivo(Mensaje objMensaje, ArchivoCarnet objArchivoCarnet, ArchivoCarnetTrailer objArchivoCarnetTrailer, List<ArchivoCarnetDetalle> lstArchivoCarnetDetalle) {
if (objMensaje.getStrCodigo().equals(ArchivoErrorEnum.OPERACION_EXITOSA.getStrCodigo())) {
archivoDAO.altaArchivoCarnet(objArchivoCarnet);
archivoDAO.altaArchivoCarnetTrailer(objArchivoCarnetTrailer);
archivoDAO.altaArchivoCarnetDetalle(lstArchivoCarnetDetalle);
} else {
archivoDAO.altaBitacoraArchivo510(new BitacoraArchivo510(objMensaje, objArchivoCarnet.getStrNombre()));
}
}
Hope this help someone else :)
In my controller, the following use of #GetMapping works:
#GetMapping(value = "/new")
public String newEssay(){
return "articles/essay_new";
}
But it doesn't work like this:
#GetMapping(value = "/essays/{essayId: [0-9]+}")
//#RequestMapping(value = "/essays/{essayId:[0-9]+}", method = RequestMethod.GET)
public String getEssay(Model model,
#PathVariable("essayId") long essayId) throws NoFindException, ForBiddenException, ParseException {
JsEssay jsEssay = jsBiz.get(JsEssay.class, essayId);
model.addAttribute("jsEssay", jsEssay);
return "articles/essay";
}
I tried it with Spring 4.3.3 and 5.0.0.M5.
Config:
#Configuration
#ComponentScan( basePackages = {"me.freezehome.blog"},
excludeFilters = {
#ComponentScan.Filter(type = FilterType.ANNOTATION, value = EnableWebMvc.class)
}
)
public class RootConfig {
}
#Configuration
#EnableWebMvc
#Import({WebSecurityConfig.class})
public class WebConfig extends WebMvcConfigurerAdapter{
#Bean
public RequestMappingHandlerMapping requestMappingHandlerMapping(){
return new RequestMappingHandlerMapping();
}
#Bean
public RequestMappingHandlerAdapter requestMappingHandlerAdapter(){
return new RequestMappingHandlerAdapter();
}
}
Google results:
Add support for #GetMapping, #PostMapping etc. introduced in Spring 4.3 in ControllerLinkBuilder #471
GetMapping and PostMapping annotations Ask
github source: lbfreeze-blog-develop
Please remove the space after essayId:
Also, you don't need to write value = for #GetMapping.
I'm trying to implement a converter in a Spring Boot app but for some reason my override of addFormatters(FormatterRegistry formatterRegistry) is never called. What's confusing is that other overridden methods are called, addInterceptors works just fine. Security is enabled for this app.
#EnableWebMvc
#Configuration
#ComponentScan(basePackages = {"com.company.web"})
public class WebMvcConfig extends WebMvcConfigurerAdapter {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
#Autowired
private RequestMappingHandlerAdapter requestMappingHandlerAdapter;
#Inject
private TenantIdentifierInterceptorAdapter multiTenancyInterceptor;
#Override
public void addInterceptors(InterceptorRegistry registry) {
logger.info("adding interceptor");
registry.addInterceptor(multiTenancyInterceptor);
}
//THIS IS NOT CALLED
#Override
public void addFormatters(FormatterRegistry formatterRegistry) {
logger.info("adding converters");
formatterRegistry.addConverter(new StringToPersonConverter());
}
private static final String[] CLASSPATH_RESOURCE_LOCATIONS = {"classpath:/META-INF/resources/",
"classpath:/resources/", "classpath:/static/", "classpath:/public/"};
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/**").addResourceLocations(CLASSPATH_RESOURCE_LOCATIONS)
.setCachePeriod(3600).resourceChain(true).addResolver(new PathResourceResolver());
}
#PostConstruct
public void init() {
requestMappingHandlerAdapter.setIgnoreDefaultModelOnRedirect(true);
}
}
For some reason if I add this code to the file the formatter code is hit, but I get an error "A ServletContext is required to configure default servlet handling" and the app won't compile.
#Bean
public ResourceBundleMessageSource messageSource() {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
messageSource.setBasename("messages");
return messageSource;
}
Have you configured a bean with super class WebMvcConfigurationSupport?
If you did so, it will disable WebMvcAutoConfiguration Bootstrap.
Check this code:
#Configuration
#ConditionalOnWebApplication(
type = Type.SERVLET
)
#ConditionalOnClass({Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class})
#ConditionalOnMissingBean({WebMvcConfigurationSupport.class})
#AutoConfigureOrder(-2147483638)
#AutoConfigureAfter({DispatcherServletAutoConfiguration.class, ValidationAutoConfiguration.class})
public class WebMvcAutoConfiguration {
I'm not 100% sure but did you try to remove #EnableWebMvc annotation from the class? Because according to documentation it shouldn't be there:
If you want to keep Spring Boot MVC features, and you just want to add additional MVC configuration (interceptors, formatters, view controllers etc.) you can add your own #Configuration class of type WebMvcConfigurerAdapter, but without #EnableWebMvc.
M. Deinum's comment was the answer. I've removed #EnableWebMvc and removed the method addFormatters, then added:
#Bean
StringToPersonConverter stringToPersonConverter() {
return new StringToPersonConverter();
}
I have solved a similar error
EnableWebMvc
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.TYPE)
#Documented
#Import(DelegatingWebMvcConfiguration.class)
public #interface EnableWebMvc {
}
DelegatingWebMvcConfiguration
#Configuration
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();
#Autowired(required = false)
public void setConfigurers(List<WebMvcConfigurer> configurers) {
if (!CollectionUtils.isEmpty(configurers)) {
this.configurers.addWebMvcConfigurers(configurers);
}
}
//...
#Override
protected void addFormatters(FormatterRegistry registry) {
this.configurers.addFormatters(registry);
}
//...
}
WebMvcConfigurationSupport
public class WebMvcConfigurationSupport implements ApplicationContextAware, ServletContextAware {
//...
#Bean
public FormattingConversionService mvcConversionService() {
FormattingConversionService conversionService = new DefaultFormattingConversionService();
addFormatters(conversionService);
return conversionService;
}
/**
* Override this method to add custom {#link Converter}s and {#link Formatter}s.
*/
protected void addFormatters(FormatterRegistry registry) {
}
//...
}
My custom config with multiple WebMvcConfigurerAdapter implements WebMvcConfigurer
#Configuration
public class WebConfig extends WebMvcConfigurerAdapter
{
#Autowired
private ConversionService conversionService;
#Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers)
{
argumentResolvers.add(new RequestTenantResolverComposite(conversionService));
}
}
The method will not be called if the FormattingConversionService implements ConversionService bean created before WebMvcConfigurer injected.
I solved my question using Lazy annotion for ConversionService with spring verions 4.3.6.