Why doesn't #Getmapping work in some instances? - java

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.

Related

Custom PreAuthorize annotation in Spring Security

I'm trying to understand Spring Security and I'm wondering about creating my own annotations with authorities I've created. I've got something like this:
#PreAuthorize("hasAuthority('STANDARD')")
#Target({ElementType.METHOD, ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
public #interface StandardRole {
}
#PreAuthorize("hasAuthority('ADMIN')")
#Target({ElementType.METHOD, ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
public #interface AdminRole {
}
and actually it looks like:
#AdminRole
#StandardRole
#GetMapping(path = "user", produces = "application/json")
public ResponseEntity<User> getUser(#RequestParam String login) {
...
}
but only first annotation works, second one is ommited. I want to do something like #AllowRoles() annotation, for example #Allow({UserType.ADMIN, UserType.STANDARD}) or #Allow({UserType.ADMIN}).
How can I do this? Thanks.
It's a pity to force the door open.
I used jsr250 annotation in my SecurityConfig class:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(jsr250Enabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
...
#RolesAllowed in my controller:
#RolesAllowed({UserType.TYPE_1, UserType.TYPE_2})
#GetMapping(path = "user", produces = "application/json")
public ResponseEntity<User> getUser() {
Finally, at my CustomDetails implementing UserDetails:
private static final String PREFIX = "ROLE_";
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return Collections.singletonList(
new SimpleGrantedAuthority(
PREFIX + user.getUserType()));
}
I forgot about "ROLE_" prefix. Code is much cleaner.

Making a defined spring bean primary

I have 3 beans of the same type defined in spring.xml. This is inside a jar file which I cannot edit. I want to make one of these primary in my spring-boot application using annotation. Is there a way to do it?
A straightforward approach is to use a bridge configuration, which will register the desired bean as a new primary bean. A simple example:
The interface:
public interface Greeter { String greet(); }
The configuration which you don't control:
#Configuration
public class Config1 {
#Bean public Greeter british(){ return () -> "Hi"; }
#Bean public Greeter obiWan(){ return () -> "Hello there"; }
#Bean public Greeter american(){ return () -> "Howdy"; }
}
The bridge configuration:
#Configuration
public class Config2 {
#Primary #Bean public Greeter primary(#Qualifier("obiWan") Greeter g) {
return g;
}
}
The client code:
#RestController
public class ControllerImpl {
#Autowired
Greeter greeter;
#RequestMapping(path = "/test", method = RequestMethod.GET)
public String test() {
return greeter.greet();
}
}
Result of curl http://localhost:8080/test will be
Hello there
You can use #Qualifier("___beanName__") annotation to choose the correct one
I tried #jihor solutions but it doesn't work. I have a NullPointerException in defined configuration.
Then I find the next solution on Spring Boot
#Configuration
public class Config1 {
#Bean
#ConfigurationProperties(prefix = "spring.datasource.ddbb")
public JndiPropertyHolder ddbbProperties() {
return new JndiPropertyHolder();
}
#ConditionalOnProperty(name = "spring.datasource.ddbb.primary", matchIfMissing = false, havingValue = "true")
#Bean("ddbbDataSource")
#Primary
public DataSource ddbbDataSourcePrimary() {
return new JndiDataSourceLookup().getDataSource(ddbbProperties().getJndiName());
}
#ConditionalOnProperty(name = "spring.datasource.ddbb.primary", matchIfMissing = true, havingValue = "false")
#Bean("ddbbDataSource")
public DataSource ddbbDataSource() {
return new JndiDataSourceLookup().getDataSource(ddbbProperties().getJndiName());
}
}
And my application.properties if I need this datasource as primary, otherwise don't set the property or set false.
spring.datasource.ddbb.primary=true

Spring Boot Get method, Request Mapping by extension/mime

I'm using Spring Boot to create an HTTP endpoint. I would like to have 2 Get method handlers. One for http://$HOST/something/{key} and a separate one for http://$HOST/something/{key}.xyz Where xyz is an extension I made up, and it's not xml/json.
Example: http://localhost:8080/something/123 should go to method1, and http://localhost:8080/something/123.xyz should go to method2.
This is what I tried:
#Configuration
#Import({
DispatcherServletAutoConfiguration.class,
HttpMessageConvertersAutoConfiguration.class,
ServerPropertiesAutoConfiguration.class
})
public class SpringConfig extends WebMvcAutoConfiguration.WebMvcAutoConfigurationAdapter{
#Bean
#ConditionalOnProperty(prefix = "spring.mvc", name = "invalid")
public OrderedHiddenHttpMethodFilter hiddenHttpMethodFilter()
{
return null;
}
#Bean
#ConditionalOnProperty(prefix = "spring.mvc", name = "invalid")
public OrderedHttpPutFormContentFilter httpPutFormContentFilter()
{
return null;
}
#Bean
#Override
#ConditionalOnProperty(prefix = "spring.mvc", name = "invalid")
public RequestContextFilter requestContextFilter()
{
return null;
}
#Primary
#Bean(name = "jacksonObjectMapper")
public ObjectMapper jacksonObjectMapper()
{
return new Jackson2ObjectMapperBuilder()
.propertyNamingStrategy(PropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES)
.serializationInclusion(JsonInclude.Include.NON_NULL)
.build();
}
#Override
public void configureMessageConverters(final List<HttpMessageConverter<?>> converters)
{
converters.add(new MappingJackson2HttpMessageConverter(jacksonObjectMapper()));
ArrayList<MediaType> list = new ArrayList<>();
MediaType mediaType = new MediaType("application","xyz");
list.add(mediaType);
StringHttpMessageConverter stringHttpMessageConverter = new StringHttpMessageConverter();
stringHttpMessageConverter.setSupportedMediaTypes(list);
List<MediaType> supportedList = stringHttpMessageConverter.getSupportedMediaTypes();
converters.add(stringHttpMessageConverter);
}
And here is my endpoint
#CrossOrigin
#RestController
#RequestMapping(value = "/something")
public class MyEndpoint {
#ResponseBody
#RequestMapping(value = "/{key}",method = RequestMethod.GET,consumes = "application/xyz")
public String getXyzHandler(#PathVariable("key") final String key, final HttpServletRequest httpRequest)
{
return null;
}
#ResponseBody
#RequestMapping(value = "/{key}",method = RequestMethod.GET,consumes = "!application/xyz")
public String getAllExtensionsHandler(#PathVariable("key") final String key)
{
return null;
}
}
All my requests are going to getAllExtensionsHandler and even when http::/localhost:8080/something/123.xyz
What am I missing?
I'm want it to be the right solution and not something hacky that would break everything else.
Thank you!

Spring 406 status code issue for .com as pathvar

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();
}
}

Spring boot MVC does not use the specified custom StringHttpMessageConverter

In the class responsible for app config is this code:
#Bean
public HttpMessageConverter<String> createStringHttpMessageConverter() {
StringHttpMessageConverter converter = new StringHttpMessageConverter
(Charset.forName("UTF-8"));
return converter;
}
I checked in debugger that it is actually executed and also tried alternative with StringHttpMessageConverter as the return type.
However when I debug WebMvcConfig.extendMessageConverters() I see that the StringHttpMessageConverter with the default charset is used instead of my converter with UTF-8 charset.
Why does not Spring Boot use the specified StringHttpMessageConverter ?
I know the workaround might be to change the converters list according to my needs in WebMvcConfig.extendMessageConverters() but I would like to do it the right way
With Spring Boot try to register array of HttpMessageConverters:
import org.springframework.boot.autoconfigure.web.HttpMessageConverters;
import org.springframework.context.annotation.*;
import org.springframework.http.converter.*;
#Configuration
public class MyConfiguration {
#Bean
public HttpMessageConverters customConverters() {
HttpMessageConverter<?> additional = ...
HttpMessageConverter<?> another = ...
return new HttpMessageConverters(additional, another);
}
}
Or if you are not using Spring Boot's auto-configuration, you can use standard Spring WebMvcConfigurer for registering converters:
#Configuration
#EnableWebMvc
public class WebConfiguration extends WebMvcConfigurerAdapter {
#Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder()
.indentOutput(true)
.dateFormat(new SimpleDateFormat("yyyy-MM-dd"))
.modulesToInstall(new ParameterNamesModule());
converters.add(new MappingJackson2HttpMessageConverter(builder.build()));
converters.add(new MappingJackson2XmlHttpMessageConverter(builder.xml().build()));
}
}
You need 2 steps to support utf-8 StringHttpMessageConverter;
First add StringHttpMessageConverter;Second specify charset in produces,code is below;
#RestController
#RequestMapping(value = "/plain")
public class PlainController {
#RequestMapping(value = "/test", method = RequestMethod.GET,
produces = {"text/plain;charset=UTF-8"})
public Response<String> test() {
return new Response<String>("success");
}
}
#Bean
public HttpMessageConverters fastJsonHttpMessageConverters() {
FastJsonHttpMessageConverter fastConverter = new FastJsonHttpMessageConverter();
FastJsonConfig fastJsonConfig = new FastJsonConfig();
fastJsonConfig.setSerializerFeatures(SerializerFeature.PrettyFormat);
fastConverter.setFastJsonConfig(fastJsonConfig);
HttpMessageConverter<?> converter = fastConverter;
StringHttpMessageConverter stringHttpMessageConverter =
new StringHttpMessageConverter(Charset.forName("UTF-8"));
return new HttpMessageConverters(converter, stringHttpMessageConverter);
}

Categories

Resources