Spring Ajax @ResponseBody with null returned values

10,076

Solution 1

This is not a trivial problem to solve.

Spring has a common pattern in which if a handler method returns null, it is meant to indicate that the handler has already dealt with producing and writing the appropriate response content and that no further action is necessary on the front.

Spring has applied this pattern in its RequestResponseBodyMethodProcesser (the HandlerMethodReturnValueHandler implementation for @ResponseBody). It checks if the return value is null. It sets the request as handled. If the return value is not null, it attempts to serialize it with an appropriate HttpMessageConverter.

One option is to create your own @ResponseBodyNull annotation and a corresponding HandlerMethodReturnValueHandler which does the same except also handles null. Note that you can't necessarily reuse the code from RequestResponseBodyMethodProcess because some HttpMessageConverters would fail trying to use null.

Another similar option is to override RequestResponseBodyMethodProcessor to accept null (with the limitations stated above) and register it explicitly with your RequestMappingHandlerMapping, overwriting the default HandlerMethodReturnValueHandlers. You have to do this carefully (ie. register the same ones), unless you want to lose functionality.

The better solution, IMO, would be to not deal with null in the response body. If getObject doesn't return anything, that seems like a 404 to me. Set the appropriate response code and voila!

You can always inject the HttpServletResponse into your handler method and do something like

Object object = getObject(..);
if (object == null) {
    response.getWriter().print("null");
    // also set the content type to application/json
}
return object;

Assuming you knew that this had to be serialized to JSON.

Solution 2

You can return a ResponseEntity and specify the HTTP status to an error when the object is null:

@RequestMapping(value = "/someUrl.controller", method = RequestMethod.GET)
public ResponseEntity<Object> getObject(@RequestParam("id") Long id) {
    Object object = provider.getObject(id);
    if (object == null ) {
        return new ResponseEntity<Object> (HttpStatus.BAD_REQUEST); // Or any other error status
    } else {
        return new ResponseEntity<Object> (object, HttpStatus.OK);
    }
}

In this way your client will be able to know when the object is null checking the response status.

If you actually need the null value returned you can configure Jackson to serialize it (code from tkuty):

<mvc:annotation-driven>
    <mvc:message-converters register-defaults="true">
        <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
            <property name="objectMapper">
                <bean class="com.fasterxml.jackson.databind.ObjectMapper">
                    <property name="serializationInclusion">
                        <value type="com.fasterxml.jackson.annotation.JsonInclude.Include">NON_NULL</value>
                    </property>
                </bean>
            </property>
        </bean>
    </mvc:message-converters>
</mvc:annotation-driven>

I hope this help you.

Share:
10,076
fashuser
Author by

fashuser

Updated on June 25, 2022

Comments

  • fashuser
    fashuser almost 2 years

    I have about fifty controllers that use @ResponseBody annotation.

    Like this:

    @RequestMapping(value = "/someUrl.controller", method = RequestMethod.GET)
    public @ResponseBody Object getObject(@RequestParam("id") Long id) {
        Object object = provider.getObject(id);
        return object;
    }
    

    Some times getObject method return null. The issues is that on client side I get empty response instead of null.

    In the initial implementation we have custom JsonView object that works as wrapper without @ResponseBody annotation.

    Like this:

    @RequestMapping(value = "/someUrl.controller", method = RequestMethod.GET)
    public JsonView<Object> getObject(@RequestParam("id") Long id) {
        Object object = provider.getObject(id);
        return new JsonView(object);
    }
    

    So it was working fine.

    I have found some solution at How do you override the null serializer in Jackson 2.0? but unfortunatly it works only for fields in the POJOs.

    Have you any ideas how in it can be handled?

    Thanks in advance!

  • fashuser
    fashuser over 9 years
    Do you mean specify annotation under method signature, because this one provoke "Duplicate annotation @ResponseBody" compilation error?Thanks!
  • Phong Nguyen
    Phong Nguyen over 9 years
    No I mean, you shouldn't put annotation after accessor and before datatype of a method. Use my way. Quality Scanner (such as SonarQube) will realize it's a major error and need to be refactor! I faced to it and get experiences. That's not "Duplicate annotation" compilation error. It will compile properly, but you should standardized your code.
  • fashuser
    fashuser over 9 years
    Unfortunatly any approach does not work in my case: 1) The first one continue return empty response as the following code are present in: Object body = responseEntity.getBody(); if (body != null) { writeWithMessageConverters(body, returnType, inputMessage, outputMessage); } else { // flush headers to the HttpServletResponse outputMessage.getBody(); }. So it's just preint null as priviues and avoid writeWithMessageConverters method.
  • Sotirios Delimanolis
    Sotirios Delimanolis over 9 years
    How is that an error? It might be code style opinion, but it's definitely not an error. And your answer doesn't answer the question.
  • Sotirios Delimanolis
    Sotirios Delimanolis over 9 years
    Your second suggestion won't work, because the null will never reach the MappingJackson2HttpMessageConverter, it will stop at the RequestResponseBodyMethodProcessor.
  • Phong Nguyen
    Phong Nguyen over 9 years
    Yeap! it's not a syntax error, but if you apply quality scale. it's a major mistake and should be fixed! If you don't believe check SonarQube rule and find out why!
  • Sotirios Delimanolis
    Sotirios Delimanolis over 9 years
    I don't know what you mean by quality scale and I don't know what you mean by major mistake. I understand that SonarQube doesn't like it as part of their code policy. In any case, your answer doesn't address the issue.