How to use Spring Data Binding Object for Snake case attributes while maintaining the Java Naming Conventions standard?

Asked

Viewed 514 times

1

Hello, is there any way to perform Binding date of objects in Spring using Snake case?

For example, suppose the following request GET on a job REST:

http://localhost:8080/Foobar? foo_bar=example

For the Spring Binding date to work, in the Foobar class the attribute must have the exact name with the request parameter, i.e.:

public class FooBar {

    @NotBlank(message = "Campo 'foo_bar' não pode ser vazio")
    private String foo_bar;

}

But that name for the attribute is not acceptable according to Java Naming Conventions.

Therefore, the class would be as follows:

public class FooBar {

    @JsonProperty("foo_bar")
    @NotBlank(message = "Campo 'foo_bar' não pode ser vazio")
    private String fooBar;

}

However, given the format of the request, the Spring Binding date cannot perform the bind.

Does anyone know how to keep up the formality with the convention and at the same time make the bind work?

Addendum: Restcontroller has a method like this:

@RequestMapping("/foobar")
public ResponseEntity<Object> foobar(@Validated FooBar fooBar, Errors errors) {

        if(errors.hasErrors()) {
            return ResponseEntity.badRequest().body(errors);
        }

        return ResponseEntity.ok(fooBar);
    }

Grateful from now on.

1 answer

1


A good idea is to create a filter that converts Snake case values to Camel case (or any other format).

This filter will be run for every request, and will handle the parameters accordingly and pass the request to the controller with the request values already formatted.

Talk is easy! Show the code!

AppConfig.java

import java.io.IOException;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.filter.OncePerRequestFilter;

import com.google.common.base.CaseFormat;

@Configuration
public class AppConfig {

    @Bean
    public Filter snakeConverter() {
        return new OncePerRequestFilter() {

            @Override
            protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
                    throws ServletException, IOException {
                final Map<String, String[]> formattedParams = new ConcurrentHashMap<>();

                for (String param : request.getParameterMap().keySet()) {
                    String formattedParam = CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, param);
                    formattedParams.put(formattedParam, request.getParameterValues(param));
                }

                filterChain.doFilter(new HttpServletRequestWrapper(request) {
                    @Override
                    public String getParameter(String name) {
                        return formattedParams.containsKey(name) ? formattedParams.get(name)[0] : null;
                    }

                    @Override
                    public Enumeration<String> getParameterNames() {
                        return Collections.enumeration(formattedParams.keySet());
                    }

                    @Override
                    public String[] getParameterValues(String name) {
                        return formattedParams.get(name);
                    }

                    @Override
                    public Map<String, String[]> getParameterMap() {
                        return formattedParams;
                    }
                }, response);
            }
        };
    }

}

Now we come to the explanation about the code above:

The setting in the snakeConverter will make the magic happen:

The method doFilterInternal is always executed before request to be passed to the controller, this way we are taking all the parameters passed, including a new one Map with the values formatted in case, and forwarding forward through the filterChain.doFilter, that will actually forward the request to the controller.

The HttpServletRequestWrapper envelopes the request and answer, and returns the values we defined in our new parameter map.

Follow the documentation from Onceperrequestfilter

I’m using the Guava to convert from Snake case to Camel case, if using Maven just include this dependency in your pom.xml

    <dependency>
        <groupId>com.google.guava</groupId>
        <artifactId>guava</artifactId>
        <version>27.1-jre</version>
    </dependency>

The code is completely based on the filter created by azhawkes


Using Restlet to test on a simple controller:

To the URL: http://localhost:8080/snakecase? foo_bar=123

inserir a descrição da imagem aqui


I noticed that this question was open on Stackoverflow, I took advantage and answered her too :)

Browser other questions tagged

You are not signed in. Login or sign up in order to post.