39

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.

cdeszaq
  • 30,869
  • 25
  • 117
  • 173
Gordian Yuan
  • 4,750
  • 6
  • 29
  • 35

6 Answers6

52

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]

Janning Vygen
  • 8,877
  • 9
  • 71
  • 102
  • 2
    This is the best answer for Spring 3.2 or later, though the test code distracts from its simplicity. You only need the first code block. The rest of the code isn't specific to the question. And instead of a putting it into a *ControllerAdvice* class, you can also put it into the controller class or the controller's base class directly. – Codo Apr 21 '14 at 18:00
  • Also working with Spring Boot 1.3 and Spring MVC 4.2 – fatiherdem Jun 16 '16 at 01:01
  • What about passowords? – LppEdd Oct 27 '17 at 17:25
  • Having trouble testing this from SpringTestRunner? MockMVC does not pick up this configuration and it must be added explicitly with a call to `.setControllerAdvice(...)` – IcedDante Feb 16 '18 at 17:08
  • 1
    It not working on request body. – Phan Kieu Hung Nov 24 '22 at 09:53
11

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>
Janning Vygen
  • 8,877
  • 9
  • 71
  • 102
Lonre Wang
  • 2,265
  • 1
  • 16
  • 16
  • 2
    I am not sure how did you all manage to make it work, but in Spring Portlet MVC, this simply won't budge. First of all, StringTrimmerEditor doesn't implement PropertyEditorRegistrar interface and, even if it did, it has no default no-arg constructor. I ended up writing my own StringTrimmerEditorRegistrar and injecting in ConfigurableWebBindingInitializer, but I am curios to know how did everyone manage to make it work? – quantum Jan 28 '12 at 16:56
  • 1
    StringTrimmerEditor has two constructors, you can pass boolean or string and boolean, check the documentation – Luis Ramirez-Monterosa Apr 17 '13 at 14:57
  • 2
    If you use Spring 3.2 or later, you might want to look at Janning's answer. (*AnnotationMethodHandlerAdapter* is now deprecated.) – Codo Apr 21 '14 at 17:55
4

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" />
Patrick
  • 3,901
  • 1
  • 25
  • 30
2

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));
        }
}
Sarvar Nishonboyev
  • 12,262
  • 10
  • 69
  • 70
1

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

Arthur Ronald
  • 33,349
  • 20
  • 110
  • 136
  • 2
    Be wary of an approach such as this - a user may use a space as the first and/or last character of their password. Your implementation above also is not null safe. – anger Apr 23 '10 at 00:36
  • HttpServletRequest class doesn't have setParameter method – Eugene Maysyuk Mar 10 '20 at 12:33
0

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.