Spring MVC #RequestParam - empty List vs null - spring-mvc

By default Spring MVC assumes #RequestParam to be required. Consider this method (in Kotlin):
fun myMethod(#RequestParam list: List<String>) { ... }
When passing empty list from javaScript, we would call something like:
$.post("myMethod", {list: []}, ...)
In this case however, as the list is empty, there is no way to serialize empty list, so the parameter essentially disappears and so the condition on required parameter is not satisfied. One is forced to use the required: false on the #RequestParam annotation. That is not nice, because we will never receive the empty list, but null.
Is there a way to force Spring MVC always assume empty lists in such case instead of being null?

To get Spring to give you an empty list instead of null, you set the default value to be an empty string:
#RequestParam(required = false, defaultValue = "")

This can be managed in the serialization with ObjectMapper. If you are using jackson in your spring MVC, you can do either the following.
1) Configure your object mapper:
objectMapper.configure(SerializationConfig.Feature.WRITE_EMPTY_JSON_ARRAYS, false);
2) Or if you are using beans via xml config:
<bean name="objectMapper" class="org.springframework.http.converter.json.JacksonObjectMapperFactoryBean" autowire="no">
<property name="featuresToDisable">
<list>
<value type="org.codehaus.jackson.map.SerializationConfig.Feature">WRITE_EMPTY_JSON_ARRAYS</value>
</list>
</property>
</bean>

You can try a WebDataBinder in your controller.
#InitBinder
public void initBinder(WebDataBinder binder) {
binder.registerCustomEditor(List.class, "list", new CustomCollectionEditor( List.class, true));
}

Tried this?
fun myMethod(#RequestParam list: List<String> = listOf()) { ... }

Related

Spring MVC - bind enum array

If I post in the format weather=sunny, Spring MVC happily converts this to an Weather enum instance using the enum with name=sunny.
However if I post weather=sunny&weather=windy, then Spring is not able to convert it into an instance of Weather[]. The error I get is:
Failed to convert property value of type 'java.lang.String[]' to required type 'com.blah.Weather[]' for property 'weather'
How can I achieve this?
You can use Converters to perform the custom conversion. For your example, you would need to do something like:
public class WeatherConverter implements Converter<String[], Weather[]> {
#Override
public Weather[] convert(String[] source) {
if(source == null || source.length == 0) {
return new Weather[0];
}
Weather[] weathers = new Weather[source.length];
int i = 0;
for(String name : source) {
weathers[i++] = Weather.valueOf(name);
}
return weathers;
}
}
You can use Converters anywhere you might want type-conversions. Now, what you need to do is register it:
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<list>
<bean class="package.path.WeatherConverter"/>
</list>
</property>
</bean>
And it is done.
You can see more details in the Spring Reference.
You could also look into PropertyEditors, with #InitBinder, and, probably, #ControllerAdvice if you want. However, Converters are much easier to use (IMO).

HTML escape with Spring MVC and Jackson Mapper

I am going to escape HTML in Spring MVC with Jackson Mapper to avoid XSS attack. I search for escaping with Jackson alone and how to config Jackson in Spring. I tried to export json with text like "<" ">", I expected it to escape them to < and >. For example, I added some text enclosed with "bold tag" <b>, I expected to see plain bold tag text in the front end html but it ended up so that the text is shown in bold style in the front end html page.
Below is my approach. I don't know why it didn't work out.
public class CustomObjectMapper extends ObjectMapper {
public CustomObjectMapper() {
this.getJsonFactory().setCharacterEscapes(new CustomCharacterEscapes());
}
}
public class CustomCharacterEscapes extends CharacterEscapes {
private final int[] asciiEscapes;
public CustomCharacterEscapes() {
int[] esc = CharacterEscapes.standardAsciiEscapesForJSON();
esc['<'] = CharacterEscapes.ESCAPE_STANDARD;
esc['>'] = CharacterEscapes.ESCAPE_STANDARD;
esc['&'] = CharacterEscapes.ESCAPE_STANDARD;
esc['\''] = CharacterEscapes.ESCAPE_STANDARD;
asciiEscapes = esc;
}
#Override
public int[] getEscapeCodesForAscii() {
return asciiEscapes;
}
#Override
public SerializableString getEscapeSequence(int ch) {
return null;
}
}
Here is the Spring Bean configuration:
<bean
class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name="messageConverters">
<array>
<bean id="jsonConverter"
class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
<property name="objectMapper">
<bean class="x.y.z.CustomObjectMapper" />
</property>
</bean>
</array>
</property>
</bean>
I have never tried to write my own HttpMessageConverter, but I did find this posting that seems pretty relevant to what you want to do. In looking at their solution vs. what you posted here, I can say the biggest differences I noticed was that you did not seem to implement/override the following:
protected boolean supports(Class<?> clazz), which indicates which class type you are supporting (I would recon in your case this would be Object or Serializable if you want it to be generic enough to handle every possibility, or some class specific to your domain objects)
protected Object readInternal(Class<? extends Object> clazz, HttpInputMessage inputMessage), looks like it's used for the request-side
protected void writeInternal(Object t, HttpOutputMessage outputMessage), which looks like it's used for the response-side
Another approach might be to simple create a custom Jackson serializer in conjunction with #ResponseBody. Or, better yet, if you have a value that is user-driven, and your storing it in a database, escape the values prior to insertion. That way you don't need to do anything at all, and the value(s) in question would be "safe" from end-to-end. If you wanted to get crazy-fancy, you could write a custom java.beans.PropertyEditor that escapes Strings for HTML and plug that into the mix using the InitBinder.
Finally, I would like to recommend that, instead of trying to replace the characters on your own, you use something like Apache Commons-Lang's StringEscapeUtils to escape the values.

Spring MVC is dropping a #PathVariable

If I hit the controller multiple times and hammer it, occasionally my modelCode parameter comes through as null. However the URL has the modelCode in it.
Using Spring Framework 3.0.5.RELEASE
#RequestMapping(value="ws/getallvariants/{channelCode}/{modelCode}/{regionId}/{year}")
public ModelAndView getAllVariants(#PathVariable("channelCode") String channelCode,
#PathVariable("modelCode") String modelCode,#PathVariable("regionId") String regionId,#PathVariable("year") String year){
if (modelCode == null)
{
int i = 0; // this should never hit, but does.
}
Yes, RegEx was the most reliable for me as well. Did this to grab an email address as a parameter:
#RequestMapping(value = "rest/userreadserv/search/{email:.+}", method = RequestMethod.GET)
public ResponseEntity getUserAccountByEmail(#PathVariable String email) {...}
Take a look at Spring MVC #PathVariable getting truncated . The regex approach worked for me:
#RequestMapping({ "/servers/{serverName:.+}" })
Updating the spring framework again to the latest version appears to have worked. Apparently when we updated our framework something didn't work correctly.
EDIT:
So this wasn't our issue at all... We had implemented a String Trimmer Editor incorrectly and it was keeping state. So when we put the call under load it would cross results.
//in your xml dispatcher add this property to your default annotation mapper bean as follow
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping">
<property name="alwaysUseFullPath" value="true"></property>
</bean>

Apache Tiles : change template page at runtime

I have a question : suppose that in a spring MVC 3.0 enviroment i manage Views with Tiles : I have a xml file with definitions of all views. Every view extends a specific template. I have two templates : one for rendering a completeDOM () and one for partialDOM (.....).The problem is, there are some views that can be retrieved in fullDOM and also in partialDOM, but i don't want to write two similars definitions.
I was thinking to a dynamic approach : inject the template of a view at runtime, specifying an http parameter which should contains the name of the template. If the request contains the parameter, than Tiles should override the template exteded by the view, with the template detected by http parameter value.
Some suggestions?
I know this is an old question but I had needed to do this very thing so I thought I'd share my solution.
Tiles allows what they refer to as "runtime composition", which lets you modify definitions. So you can reuse an existing definition and just swap the template:
<tiles:insertDefinition name="existingDefinition" template="alternateTemplate.jsp" />
In spring tilesConfigurer you need to set mutable container:
<property name="useMutableTilesContainer" value="true"/>
<property name="checkRefresh" value="true"/>
And in your Spring Controller:
ModelAndView model = new ModelAndView();
MutableTilesContainer container = (MutableTilesContainer)ServletUtil.getContainer(request.getSession().getServletContext());
Attribute attribute = new Attribute("your template jsp");
HashMap<String, Attribute> attributes = new HashMap<String, Attribute>();
attributes.put("body", attribute);
Definition definition = new Definition("your definition name", "your jsp", attributes);
definition.setExtends("your definition template name");
definition = PatternUtil.replacePlaceholders(definition, "your definition name", new Object());
container.register(definition, request, response);
model.setViewName("your definition name");
I think a view preparer might help:
http://tiles.apache.org/framework/tutorial/advanced/preparer.html

When I use a dynamic class for my WebService arguments, the public properties are not included

I have a dynamic Class that is a Value Object that is used to pass arguments to a WebService. It has two public properties:
package
{
[Bindable]
public dynamic class WebServiceCriteria
{
public var property1:String;
public var property2:String;
}
}
I set these two properties in one part of my application:
var myCriteria:WebServiceCriteria = new WebServiceCriteria();
myCriteria.property1 = "x";
myCriteria.property2 = "y";
Then I added other - dynamic - properties at another point in my application:
myCriteria.property3 = "z";
However, when I pass the instance to the WebService as the arguments, the original two public properties are not sent (as I can see in Fiddler), even though they have values. But, I can see them as properties of my Class instance in the debugger just prior to the send().
operation.arguments = {args: myCriteria};
operation.send(); // only property3 is sent
Why are those two properties not sent?
Here is an example of the SOAP request sent to the WebService:
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<SOAP-ENV:Body SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<intf:webservice_controller xmlns:intf="http://childDir.parentDir">
<args xsi:type="apachesoap:Map" xmlns:apachesoap="http://xml.apache.org/xml-soap">
<item>
<key xsi:type="xsd:string">property1</key>
<value xsi:type="xsd:string"></value>
</item>
<item>
<key xsi:type="xsd:string">property2</key>
<value xsi:type="xsd:string"></value>
</item>
<item>
<key xsi:type="xsd:string">property3</key>
<value xsi:type="xsd:string">z</value>
</item>
</args>
</intf:webservice_controller>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
This behavior is documented in Flex 3.0 manuals. See Dynamic Classes for more information. A direct quote:
[...]Methods created in this way, however, do not have access to any private properties or methods of the [example] class. Moreover, even references to public properties or methods of the [example] class must be qualified with either the this keyword or the class name.
Try to add this to your constructor :
package
{
[Bindable]
public dynamic class WebServiceCriteria
{
public var property1:String;
public var property2:String;
function WebServiceCriteria()
{
prototype.property1 = null;
prototype.property2 = null;
}
}
}
... As it seems like only the Objects properties are enumerable
I don't believe you will be able to send an object to a webservice. If you want to send an object you will need to use remote object. To use a webservice you would need to make your object into some kind of xml or soap request. like
var myCriteria:WebServiceCriteria = new WebServiceCriteria();
myCriteria.property1 = "x";
myCriteria.property2 = "y";
operation.send(<request>
<property1>{myCriteria.property1}</property1>
<property2>{myCriteria.property2}</property2>
</request>);
http://livedocs.adobe.com/flex/3/html/help.html?content=data_intro_2.html

Resources