Customize mapping request parameters and fields in

2019-04-20 06:27发布

问题:

I have the following class:

public class MyDTO { 

       private String kiosk;
       ...
}

and following url:

http://localhost:1234/mvc/controllerUrl?kiosk=false

and following controller method:

@RequestMapping(method = RequestMethod.GET, produces = APPLICATION_JSON)
@ResponseBody
public ResponseEntity<List<?>> getRequestSupportKludge(final MyDTO myDTO, BindingResult bindingResult) {
    ...
}

Now it is working nice and boolean field resolves properly.

Now url parameter has changed like this:

http://localhost:1234/mvc/controllerUrl?new_kiosk=false

I don't want to change parameter name inside the DTO. Is there way to say spring to understand that new_kiosk request parameter value should be put into kiosk field ?

回答1:

Apart from setting an additional setter you can hande the case by making a custom argument resolver. There's a few ways you can go about it, but there's already a well discussed post. If I were you I would focus on the jkee's answer. Follow it step by step, and than all you should do is annotate your DTO with something like,

public class MyDTO { 

       @ParamName("new_kiosk")
       private String kiosk;
       ...
}

Note that even if you can't change MyDTO class, you can still follow a custom resolver route. In this post I've answered how you can write a parameter type annotation. Combining the two post you can easily come up with an annotation e.g. @ParamMapper that would define the mapping from request to properties. Think of something like

 getRequestSupportKludge(@ParamMapper("new_kiosk;kiosk") MyDTO myDTO, BindingResult bindingResult)


回答2:

There different ways to do that.

If you can change MyDTO class the simplest way is to add a setter as suggested by M.Deinum :

public class MyDTO { 

       private String kiosk;
       ...
       public void setNew_kiosk(String kiosk) {
           this.kiosk = kiosk;
       }
}

That way, you can process http://localhost:1234/mvc/controllerUrl?kiosk=false as well as http://localhost:1234/mvc/controllerUrl?new_kiosk=false

If you are not allowed to do that (because the DTO is part of a library you are not allowed to change or ...), you can use a filter mapped to /mvc/controllerUrl, that would wrap the request with a custom HttpServlerRequestWrapper that will override following methods :

String  getParameter(String name)
Map<String,String[]>    getParameterMap()
Enumeration<String>     getParameterNames()
String[]    getParameterValues(String name)

calling the underlying request methods and processing the special parameter name. Example :

String[]    getParameterValues(String name) {
    String[] values = req.getParameterValues(name); // req is the wrapped request
    if ("kiosk".equals(name) && (values == null) {  // will accept both names
        values = req.getParameterValues("new_kiosk"); // try alternate name
    }
    return values;
}

This will be much harder to write and test, so only go that way if you cannot modify MyDTO class.

You could also try to use a custom implementation of WebBindingInitializer. From Spring Framework Reference Manual :

To externalize data binding initialization, you can provide a custom implementation of the WebBindingInitializer interface, which you then enable by supplying a custom bean configuration for an AnnotationMethodHandlerAdapter, thus overriding the default configuration.

Beware : the recommended usage of that is to register custom editors for a whole application - not your use case. And Spring Framework is oftern described as easy to extend but not to override. Caveat emptor ...

Summary : try to use method 1, if you cannot, then use method2, and only try method3 if you have other reasons to use a custom WebBindingInitializer