Return HTTP 204 on null with spring @RestController

54,977

Solution 1

Of course yes.

Option 1 :

@RestController
public class RepoController {
    @RequestMapping(value = "/document/{id}", method = RequestMethod.GET)
    public Object getDocument(@PathVariable long id, HttpServletResponse response) {
       Object object = getObject();
       if( null == object ){
          response.setStatus( HttpStatus.SC_NO_CONTENT);
       }
       return object ;
    }
}

Option 2 :

@RestController
public class RepoController {
    @RequestMapping(value = "/document/{id}", method = RequestMethod.GET)
    public Object getDocument(@PathVariable long id) {
       Object object = getObject();
       if ( null == object ){
          return new ResponseEntity<Void>(HttpStatus.NO_CONTENT);
       }

       return object ;
    }
}

Might have typos, but you get the concept.

Solution 2

You can use the @ResponseStatus annotation. This way you can have a void method and you don't have to build a ResponseEntity.

@DeleteMapping(value = HERO_MAPPING)
@ResponseStatus(value = HttpStatus.NO_CONTENT)
public void delete(@PathVariable Long heroId) {
    heroService.delete(heroId);
}

BTW returning 200 when the object exists and 204 otherwise it's a bit unusual regarding API REST design. It's common to return a 404 (not found) when the requested object is not found. And this can be achieved using a ControllerAdvice.

In Spring REST it's better to handle Exceptions with a Exception handler instead of putting logic to decide the response status, etc. This is an example using the @ControllerAdvice annotation: http://www.jcombat.com/spring/exception-handling-in-spring-restful-web-service

Solution 3

I solved this problem with a filter. It's global and simple.

package your.package.filter;

import org.springframework.http.HttpStatus;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class NoContentFilter extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
        filterChain.doFilter(httpServletRequest, httpServletResponse);
        if (httpServletResponse.getContentType() == null ||
                httpServletResponse.getContentType().equals("")) {
            httpServletResponse.setStatus(HttpStatus.NO_CONTENT.value());
        }
    }
}

and add the following in your web.xml

<filter>
    <filter-name>restNoContentFilter</filter-name>
    <filter-class>your.package.filter.NoContentFilter</filter-class>
</filter>

<filter-mapping>
    <filter-name>restNoContentFilter</filter-name>
    <url-pattern>/rest/*</url-pattern>
</filter-mapping>

Solution 4

You can try this :

@RestController
public class RepoController {

    @RequestMapping(value = "/document/{id}", method = RequestMethod.GET)
    public ResponseEntity<String> getDocument(@PathVariable long id) {

       if(noError) {
           ............
           return new ResponseEntity<String>(HttpStatus.OK); 
       }
       else {
           return new ResponseEntity<String>(HttpStatus.BAD_REQUEST);
       }
   }
}

Uou need to change HttpStatus.BAD_REQUEST with the equivalent for 204 code status

Solution 5

Question is old but for those that needs a global answer and have Spring 4+, you can create a ResponseBodyAdvice that changes response code base on the controller response. The following exemple do it for all @RestController classes :

@ControllerAdvice(annotations = { RestController.class })
public class NullToNoContentResponseBodyAdvice
    implements ResponseBodyAdvice<Object>
{
    /**
     * {@inheritDoc}
     */
    @Override
    public Object beforeBodyWrite(final Object p_responseBodyObject, final MethodParameter p_methodParameter,
                                  final MediaType p_mediaType, final Class<? extends HttpMessageConverter<?>> p_class,
                                  final ServerHttpRequest p_serverHttpRequest,
                                  final ServerHttpResponse p_serverHttpResponse)
    {
        // ------------------------- DECLARE -------------------------- //

        if (p_responseBodyObject == null)
        {
            p_serverHttpResponse.setStatusCode(HttpStatus.NO_CONTENT);
        }

        // Always return object unchanged or it will break response
        return p_responseBodyObject;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean supports(final MethodParameter p_methodParameter, final Class<? extends HttpMessageConverter<?>> p_class)
    {
        return AbstractGenericHttpMessageConverter.class.isAssignableFrom(p_class);
    }
}
Share:
54,977
user1606576
Author by

user1606576

Updated on July 09, 2022

Comments

  • user1606576
    user1606576 almost 2 years

    This returns 200 OK with Content-Length: 0

    @RestController
    public class RepoController {
        @RequestMapping(value = "/document/{id}", method = RequestMethod.GET)
        public Object getDocument(@PathVariable long id) {
           return null;
        }
    
    }
    

    Simply put I'd like it to return 204 No Content on null.

    Is there a way to force spring-mvc/rest to return 204 on null not 200? I dont want to change every rest method to return ResponseEntity or something like that, only map null to 204

  • Jean-François Beauchef
    Jean-François Beauchef about 7 years
    I think the OP wanted to return 200 on success, and 204 if he had null. You can put anything you want in the ResponseStatus. But the idea was to have multiple possible HTTP codes for one endpoint.
  • Karthik R
    Karthik R about 7 years
    I agree with @Jean-FrançoisBeauchef. This is not the correct handling. This response status is applied irrespective of outcome (excluding exception scenario). The questioner wanted to know how to cater to condition of null
  • spekdrum
    spekdrum about 7 years
    Sorry but I disagree. When using Spring REST it's better to handle exceptions and response codes with an Exception handler. This is an example jcombat.com/spring/…
  • Jean-François Beauchef
    Jean-François Beauchef about 7 years
    @spekdrum yes of course. You should use an exception handler to handle an exception. But this is not what the OP was asking for. He wanted to return 200 on a correct response with content, and 204 (which is also a correct response) when there is no content (or null, if you prefer). The OP doesn't even mention exceptions. And in Java (Spring or otherwise), exceptions should not be used for flow control.
  • Jean-François Beauchef
    Jean-François Beauchef about 7 years
    Look at the answers from mahieus and Marek Raszewski. You will understand what the OP was shooting for.
  • spekdrum
    spekdrum about 7 years
    Im sorry but I have to say that what I'm suggesting is far different from using exceptions for flow control. Furthermore I still think it's a pretty valid answer. Not always we respond what the OP exactly asks, even more when we think that a better aproach can be done. I mean, when you call a GET method to a API it's common to asume that the element exists, throwing an exception otherwise.
  • Yan Khonski
    Yan Khonski almost 7 years
    (HttpStatus.NO_CONTENT.value()) I copy-pasted the working solution +1 vote
  • mad_fox
    mad_fox over 6 years
    Can you provide more detail on what is actually happening here. I'm trying to implement a similar solution. My idea is to automatically return 204 for all methods that have a void return type. It seems like this solution requires the method to take a HttpServletResponse as an input. I would like to avoid that.
  • Marek Raki
    Marek Raki over 6 years
    Try to use a static way to retrieve HttpServletResponse like here The rest should be the same but the @Pointcut should cover only void methods.
  • Stephan
    Stephan over 6 years
    Very nice solution
  • Shadow Man
    Shadow Man over 5 years
    Sadly, neither of these options is a good (clean) solution. Ideally, your controller method would return a typed object rather than Object or ResponseEntity, and there would be some sort of intercepter that would notice that you were returning null, and set the NO_CONTENT status code there (or was I just spoiled by Jersey and RESTEasy).
  • user1606576
    user1606576 almost 4 years
    Maybe this is not the "correct" null handling, but this is what Jersey does, and this is the idiom we settled on using.
  • hurelhuyag
    hurelhuyag almost 3 years
    I think this is the best answer. Thank you.