Can spring mvc trim all strings obtained from forms? - spring

I know struts2 default config will trim all strings obtained from forms.
For example:
I type " whatever " in a form and submit, I will get "whatever" The string has been auto trimmed
Does spring mvc have this function too? THX.

Using Spring 3.2 or greater:
#ControllerAdvice
public class ControllerSetup
{
#InitBinder
public void initBinder ( WebDataBinder binder )
{
StringTrimmerEditor stringtrimmer = new StringTrimmerEditor(true);
binder.registerCustomEditor(String.class, stringtrimmer);
}
}
Testing with an MVC test context:
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
#ContextConfiguration
public class ControllerSetupTest
{
#Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
#Before
public void setup ( )
{
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
}
#Test
public void stringFormatting ( ) throws Exception
{
MockHttpServletRequestBuilder post = post("/test");
// this should be trimmed, but only start and end of string
post.param("test", " Hallo Welt ");
ResultActions result = mockMvc.perform(post);
result.andExpect(view().name("Hallo Welt"));
}
#Configuration
#EnableWebMvc
static class Config
{
#Bean
TestController testController ( )
{
return new TestController();
}
#Bean
ControllerSetup controllerSetup ( )
{
return new ControllerSetup();
}
}
}
/**
* we are testing trimming of strings with it.
*
* #author janning
*
*/
#Controller
class TestController
{
#RequestMapping("/test")
public String test ( String test )
{
return test;
}
}
And - as asked by LppEdd - it works with passwords too as on the server side there is no difference between input[type=password] and input[type=text]

register this property editor:
org.springframework.beans.propertyeditors.StringTrimmerEditor
Example for AnnotionHandlerAdapter:
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
...
<property name="webBindingInitializer">
<bean class="org.springframework.web.bind.support.ConfigurableWebBindingInitializer">
<property name="propertyEditorRegistrar">
<bean class="org.springframework.beans.propertyeditors.StringTrimmerEditor" />
</property>
</bean>
</property>
...
</bean>

You can also use Spring's conversion service, which has the added benefit of working with <mvc:annotation-driven/> and with Spring Webflow. As with the other answers, the major downside is that this is a global change and can't be disabled for certain forms.
You'll need a converter to do the trimming
public class StringTrimmingConverter implements Converter<String, String> {
#Override
public String convert(String source) {
return source.trim();
}
}
Then define a conversion service that knows about your converter.
<bean id="applicationConversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<property name="converters">
<list>
<bean class="mypackage.util.StringTrimmingConverter"/>
</list>
</property>
</bean>
and tie that in to mvc.
<mvc:annotation-driven conversion-service="applicationConversionService"/>
If you use Spring Webflow then it require a wrapper
<bean id="defaultConversionService" class="org.springframework.binding.convert.service.DefaultConversionService">
<constructor-arg ref="applicationConversionService"/>
</bean>
and a setting on your flow builder
<flow:flow-builder-services id="flowBuilderServices" conversion-service="defaultConversionService" development="true" validator="validator" />

Just customized the above code in order to adjust to Spring Boot, if you want to explicit trim function for some fields in the form, you can show them as below:
#Component
#ControllerAdvice
public class ControllerSetup {
#InitBinder({"dto", "newUser"})
public void initBinder(WebDataBinder binder) {
binder.registerCustomEditor(String.class, new StringTrimmerEditor(true));
binder.registerCustomEditor(String.class, "userDto.username", new StringTrimmerEditor(false));
binder.registerCustomEditor(String.class, "userDto.password", new DefaultStringEditor(false));
binder.registerCustomEditor(String.class, "passwordConfirm", new DefaultStringEditor(false));
}
}

You can user a Spring-MVC Interceptor
public class TrimInterceptor extends HandlerInterceptorAdapter {
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
Enumeration<String> e = request.getParameterNames();
while(e.hasMoreElements()) {
String parameterName = e.nextElement();
request.setParameter(parameterName, request.getParameter(parameterName).trim());
}
return true;
}
And set up your HandlerMapping interceptors property
<bean id="interceptorTrim" class="br.com.view.interceptor.TrimInterceptor"/>
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping" p:interceptors-ref="interceptorTrim"/>
}
Or use a Servlet Filter

first,trim requestparam which is String,you can create a class and implimplements WebBingdingInitializer
#ControllerAdvice
public class CustomWebBindingInitializer implements WebBindingInitializer {
#InitBinder
#Override
public void initBinder(WebDataBinder webDataBinder, WebRequest webRequest) {
webDataBinder.registerCustomEditor(String.class, new StringTrimmerEditor(true));
}
}
please use componentScan make this Class to be a Spring Bean.
But, I don't know how to trim the String value in requestBody JSON data.

Related

How to create multiple instances of RestController in Spring boot application?

I want to create different instances of Spring RestController with different instances of beans (service, dao, cache) injected.
Currently, we have implemented this with JAX-RS Restful API.
But when I try to implement the same in Spring RestController, I'm getting below error when Spring tries to create a new bean "product2RestController".
Because bean "product1RestController" is already mapped to the rest url and created.
Stacktrace:
Error creating bean with name 'requestMappingHandlerMapping' defined in class path resource [org/springframework/boot/autoconfigure/web/WebMvcAutoConfiguration$EnableWebMvcConfiguration.class]: Invocation of init method failed; nested exception is java.lang.IllegalStateException: Ambiguous mapping. Cannot map 'product2RestController' method
#Path("/products")
public class ProductLookupRestService {
#Context
HttpServletRequest httpServletRequest;
#Context
HttpServletResponse httpServletResponse;
#Context
UriInfo uriInfo;
private Map<Integer, AbstractProductRestService> productServiceLoopup;
public void setProductServiceLoopup(Map<Integer, AbstractProductRestService> productServiceLoopup) {
this.productServiceLoopup = productServiceLoopup;
}
#Path("/{productId}")
public AbstractProductRestService getReport(#PathParam("productId") int productId) {
AbstractProductRestService productRestService = this.productServiceLoopup.get(productId);
productRestService.setHttpServletRequest(httpServletRequest);
productRestService.setHttpServletResponse(httpServletResponse);
productRestService.setUriInfo(uriInfo);
return productRestService;
}
}
public abstract class AbstractProductRestService {
#Context
protected HttpServletRequest httpServletRequest;
#Context
protected HttpServletResponse httpServletResponse;
#Context
protected UriInfo uriInfo;
protected IProductService productService;
protected IProductCache productCache;
public void setHttpServletRequest(HttpServletRequest httpServletRequest) {
this.httpServletRequest = httpServletRequest;
}
public void setHttpServletResponse(HttpServletResponse httpServletResponse) {
this.httpServletResponse = httpServletResponse;
}
public void setUriInfo(UriInfo uriInfo) {
this.uriInfo = uriInfo;
}
public void setProductService(IProductService productService) {
this.productService = productService;
}
public void setProductCache(IProductCache productCache) {
this.productCache = productCache;
}
#POST
#Path("/filters")
#Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public Response createFilters(Form filterForm) {
//Save the filters and return uuid
ResponseBuilder responseBuilder = Response.created(uriInfo.getAbsolutePathBuilder().path("uuid").build());
return responseBuilder.build();
}
}
public class ProductRestService extends AbstractProductRestService {
#GET
#Path("/filters/{uuid}/detail")
public String getProductDetail(){
// do cache check : productCache
return this.productService.readProductDetail();
}
}
public class ProductServiceImpl implements IProductService{
#Override
public String readProductDetail() {
// Call to dao to get the data
return null;
}
}
<bean id="product1RestService" class="com.poc.jaxrs.rest.ProductRestService">
<property name="productService" ref="product1Service"/>
<property name="productCache" ref="product1Cache"/>
</bean>
<bean id="product2RestService" class="com.poc.jaxrs.rest.ProductRestService">
<property name="productService" ref="product2Service"/>
<property name="productCache" ref="product2Cache"/>
</bean>
<bean id="restProductLookupService" class="com.poc.jaxrs.rest.ProductLookupRestService">
<property name="productServiceLoopup">
<map key-type="java.lang.Integer">
<entry key="1"><ref bean="product1RestService"/></entry>
<entry key="2"><ref bean="product2RestService"/></entry>
</map>
</property>
</bean>
I use below like urls to invoke service.
POST Call: http://localhost:8080/productservice/products/1/filters
GET Call: http://localhost:8080/productservice/products/1/filters/uuid/detail
In my spring application context xml, I'm creating multiple instances of ProductRestService class with different beans (service, cache) instances injected.
Same way I want to create multiple instances of Spring RestController, But I'm getting Ambiguous mapping error.
Thanks for your help in advance.

UsernameTokenValidator Can not #Autowired Dao

I have a Spring-ws and i am using Apahce-wss4j for spring-ws authentication. I want to use my Dao class in my custom TokenValidator class. But there was an exception can not #Autowired my Dao class. Here is my code
applicationContext.xml
<bean id="myWssConfig" class="tr.com.xxx.services.MyWssConfig"/>
<bean id="kepDBDAO" class="tr.com.xxx.dao.KepDBDAOImpl"/>
<bean id="ssha" class="tr.com.xxx.utils.SSHA"/>
<bean id="memberStatusService" class="tr.com.xxx.services.MemberStatusServiceImpl"/>
<bean id="myUsernameTokenValidator" class="tr.com.xxx.services.MyUsernameTokenValidator">
<property name="kepDBDAO" ref="kepDBDAO"/>
</bean>
<sws:interceptors>
<bean class="org.springframework.ws.soap.security.wss4j.Wss4jSecurityInterceptor">
<property name="validationActions" value="UsernameToken"/>
<property name="validationCallbackHandler" ref="callbackHandler"/>
<property name="wssConfig">
<ref bean="myWssConfig"/>
</property>
</bean>
</sws:interceptors>
Here is MyWssConfig.java
#Component("myWssConfig")
public class MyWssConfig extends WSSConfig {
public MyWssConfig() {
setValidator(WSSecurityEngine.USERNAME_TOKEN, MyUsernameTokenValidator.class);
setRequiredPasswordType(WSConstants.PASSWORD_TEXT);
}
}
And here is MyUsernameTokenValidator.java
#Component
public class MyUsernameTokenValidator extends UsernameTokenValidator {
private static final Logger LOGGER = LoggerFactory
.getLogger(MyUsernameTokenValidator.class);
#Autowired
private KepDBDAO kepDBDAO;
#Transactional
protected void verifyPlaintextPassword(UsernameToken usernameToken, RequestData data) throws WSSecurityException {
if (usernameToken != null && usernameToken.getPassword() != null) {
byte[] saltValue = null;
kepDBDAO.getWsUsers("basvuru");
String hashPassword = null;
try {
hashPassword = SSHA.calculateSSHA256(saltValue, usernameToken.getPassword());
} catch (NoSuchAlgorithmException e) {
LOGGER.error(e.toString(), e);
} catch (IOException e) {
LOGGER.error(e.toString(), e);
}
usernameToken.setPassword(hashPassword);
super.verifyDigestPassword(usernameToken, data);
}
}
public KepDBDAO getKepDBDAO() {
return kepDBDAO;
}
public void setKepDBDAO(KepDBDAO kepDBDAO) {
this.kepDBDAO = kepDBDAO;
}
}
Couldn't #Autowired my KepDBDAO when I call webservice in SOAPUI.
Help me please.. THank you all guys.
Try this:
1. In applicationContext:
<context:component-scan base-package="tr.com.xxx.dao"/>
<context:component-scan base-package="package for MyUsernameTokenValidator"/>
remove these beans:
kepDBDAO, myUsernameTokenValidator
2. Remove setter and getter for KepDBDAO in MyUsernameTokenValidator
3. Make sure KepDBDAOImpl is marked as #Service
I solved my problem.
#Component("myWssConfig")
public class MyWssConfig extends WSSConfig {
#Autowired
private MyUsernameTokenValidator myUsernameTokenValidator;
//
#PostConstruct
public void myInit() {
setValidator(WSSecurityEngine.USERNAME_TOKEN, myUsernameTokenValidator);
setRequiredPasswordType(WSConstants.PASSWORD_TEXT);
}
}

Spring data MongoDb cannot convert proxy bean

I'm using Spring AOP with AspectJ and Spring Data MongoDb and am having a world of trouble persisting objects.
In this case, I have an AclEntryDaoImpl that exposes AclEntryImpl. When AclEntryImpl is provided a Principal that is a standard Java object (a "non-Spring" bean), mongoTemplate.save() works as expected. However when Principal is a Spring bean, Mongo is unable to convert the object and results in a MappingException org.springframework.data.mapping.model.MappingException: No id property found on class class com.sun.proxy.$Proxy33. All my objects need to be Spring beans so that (a) I keep my objects decoupled and (b) my AOP (LoggingAspect) is invoked.
Lastly, I cannot take advantage of Spring converters because Mongo sees the target object AclEntryImpl as a proxy com.sun.proxy.$Proxy33 and so Converter<Principal, DBObject> is never invoked.
Any and all help would be greatly appreciated!
Snippets:
Here's my Spring XML configuration:
<beans>
<context:component-scan base-package="a.b" />
<context:property-placeholder location="config.properties" />
<aop:aspectj-autoproxy />
<bean id="loggingAspect" class="a.b.LoggingAspect" />
<mongo:db-factory host="${database.host}" port="${database.port}" dbname="${database.dbname}" />
<bean id="mongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate">
<constructor-arg name="mongoDbFactory" ref="mongoDbFactory" />
</bean>
<bean id="aclEntryDao" class="a.b.AclEntryDaoImpl">
<lookup-method name="createAclEntry" bean="aclEntry" />
</bean>
</beans>
AclEntryImpl:
#Document
#Component
#Scope("prototype")
public class AclEntryImpl implements AclEntry {
#Id
private String id;
private String service;
#DBRef #Expose
private Principal principal;
#Expose
private boolean accessGranted;
#Expose
private List<Permission> permissions;
#Override #Loggable #MongoSaveReturned
public AclEntry save() {
return this;
}
...getters and setters...
}
AclEntryDaoImpl:
#Repository
public abstract class AclEntryDaoImpl implements AclEntryDao {
#Override #Loggable
public AclEntry addEntry(String serviceName, Principal principal, Permission[] permissions, boolean accessGranted) throws Exception {
AclEntry entry = createAclEntry(); //<-- Spring lookup-method
entry.setService(serviceName);
entry.setPrincipal(principal); //<-- com.sun.proxy.$Proxy33
entry.setAccessGranted(accessGranted);
for (Permission permission : permissions) {
if (!entry.addPermission(permission)) {
return null;
}
}
return entry.save();
}
... other DAO methods ...
}
LoggingAspect:
#Aspect
public class LoggingAspect {
#Autowired
private MongoTemplate mongoTemplate;
#Pointcut("execution(!void a.b..*.*(..))")
public void returningMethods() {}
#AfterReturning(pointcut="returningMethods() && #annotation(MongoSaveReturned)", returning="retVal")
public Object mongoSaveReturnedAdvice(Object retVal) {
Logger logger = null;
try {
logger = getLogger(retVal);
mongoTemplate.save(retVal); //<-- throws MappingException
log(logger, "save: " + retVal.toString());
} catch (Exception e) {
log(logger, "throw: " + e.toString());
}
return retVal;
}
... other logging methods ...
}

Null values as empty strings when using #ResponseBody annotation

Is there a way when using #ResponseBody annotation to have null values mapped to empty strings?
You will have to write a custom Jackson Serializer - a good example is here - http://wiki.fasterxml.com/JacksonHowToCustomSerializers (there is a specific example of how to convert null values to empty Strings that you can use)
Here are all the steps(for Jackson < 2.0):
Write your custom null Serializer:
import java.io.IOException;
import org.codehaus.jackson.JsonGenerator;
import org.codehaus.jackson.JsonProcessingException;
import org.codehaus.jackson.map.JsonSerializer;
import org.codehaus.jackson.map.SerializerProvider;
public class NullSerializer extends JsonSerializer<Object> {
#Override
public void serialize(Object value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException {
jgen.writeString("");
}
}
Register this with Jackson Objectmapper:
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.ser.StdSerializerProvider;
public class CustomObjectMapper extends ObjectMapper{
public CustomObjectMapper(){
StdSerializerProvider sp = new StdSerializerProvider();
sp.setNullValueSerializer(new NullSerializer());
this.setSerializerProvider(sp);
}
}
Register this objectmapper with Spring MVC:
<mvc:annotation-driven>
<mvc:message-converters register-defaults="true">
<bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
<property name="objectMapper">
<bean class="CustomObjectMapper"/>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
I have also faced the same problem in my project and I have therefore quickly come up with a solution for the same. This post will surely help all those who have been struggling with the same issue.
Step 1:- Create your Custom Null Handler Serializer.
public class NullSerializer extends StdSerializer<Object> {
public NullSerializer(Class<Object> t) {
super(t);
}
public NullSerializer() {
this(null);
}
#Override
public void serialize(Object o, com.fasterxml.jackson.core.JsonGenerator jsonGenerator, com.fasterxml.jackson.databind.SerializerProvider serializerProvider) throws IOException {
jsonGenerator.writeString("");
}
}
Step 2:- Create a bean of MappingJackson2HttpMessageConverter.
#Bean
#Primary
public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() {
MappingJackson2HttpMessageConverter jsonConverter = new MappingJackson2HttpMessageConverter();
ObjectMapper mapper = new ObjectMapper();
mapper.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE);
mapper.getSerializerProvider().setNullValueSerializer(new NullSerializer());
jsonConverter.setObjectMapper(mapper);
return jsonConverter;
}
Thank you for taking some time out to read this post. I hope that this was able to resolve your queries to some extent.

Create spring beans, based on a comma-separated list of classes

Is there a way in Spring to create a collection, or array, of beans, based on a comma-separated list of classes. For example:
package mypackage;
public class Bla {
private Set<MyBean> beans;
public void setBeans(Set<MyBean> beans) {
this.beans = beans;
}
}
With the application context:
<bean id="bla" class="mypackage.Bla">
<property name="beans">
<set>
<bean class="mypackage.Bean1, mypackage.Bean2" />
</set>
</property>
</bean>
Preferably the beans are all initialized and wired from the context, leaving the code as simplistic as possible, is this possible?
Use a combination of ApplicationContextAware and ApplicationListener:
public class BeanInitializer implements ApplicationContextAware, ApplicationListener<ContextRefreshedEvent> {
private ApplicationContext context;
private List<Class<?>> beanClasses;
public void onApplicationEvent(final ContextRefreshedEvent event) {
final AutowireCapableBeanFactory beanFactory = this.context.getAutowireCapableBeanFactory();
for (final Class<?> beanClass : this.beanClasses) {
beanFactory.autowire(beanClass, AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE, true);
}
}
public void setApplicationContext(final ApplicationContext context) throws BeansException {
this.context = context;
}
public void setBeanClasses(final List<Class<?>> beanClasses) {
this.beanClasses = beanClasses;
}
}
in your spring config, do this:
<bean class="com.yourcompany.BeanInitializer">
<property name="beanClasses">
<list>
<value>com.yourcompany.Type1</value>
<value>com.yourcompany.Type2</value>
<value>com.yourcompany.Type3</value>
</list>
</property>
</bean>
Edited: Actually, if you want comma separated, it will probably be more like this:
<bean class="com.yourcompany.BeanInitializer">
<property name="beanClasses"
value="com.yourcompany.Type1,com.yourcompany.Type2,com.yourcompany.Type3" />
</bean>
I don't know if there is a built-in property editor that converts a comma delimited string to a list of classes but if not you can either create one yourself or change your setter method to accept a string and parse the string yourself

Resources