Spring ResponseEntity for optionals
Asked Answered
D

2

9

A construct that I often use in my code is the following:

@RestController
public class HelloController {
  @Autowired
  private HelloService helloService;

  @GetMapping("/hello")
  ResponseEntity<Message> getHelloMessage() {
    Optional<Message> message = helloService.getMessage();
    if (message.isPresent()) {
      return ResponseEntity.ok(message.get());
    }
    return new ResponseEntity(HttpStatus.NO_CONTENT);
  }
}

Following DRY principles I abstract this construct away by;

private <T> ResponseEntity<T> response(Optional<T> value) {
  if (value.isPresent()) {
    return ResponseEntity.ok(value.get());
  }
  return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}

So that my code becomes;

  @GetMapping("/hello")
  ResponseEntity<Message> getHelloMessage() {
    return response(helloService.getMessage());
  }

It would be very nice if this construct was part of the ResponseEntity class from spring so that my code becomes;

 @GetMapping("/hello")
 ResponseEntity<Message> getHelloMessage() {
   return ResponseEntity.optional(helloService.getMessage());
 }

Do you think it is a good idea to implement such a method on ResponseEntity?

Dithionite answered 6/7, 2018 at 10:13 Comment(4)
What is the exact questionNostomania
I am not sure what you are asking. But in my opinion, the method should return the expected response. If something wrong happened, then it should be handled by an ExceptionHandler. Have a look at: spring.io/blog/2013/11/01/exception-handling-in-spring-mvcBurchell
I'm not in favour of throwing exceptions just to be able to get a 204 response. My suggestion is to add a static convenience method to ResponseEntity to handle these optionals.Dithionite
I think it is completely O.K. to have that method, but you actually do not save a lot of lines of code by having it. Sometimes I would prefer simplicity over DRYing everything out to the death, i.e. allow a little repitition if it is beneficial to understand the code. You can, however, have the functionality you want by using another dependency: #39979118Satirist
P
15

I know OP asked for NO_CONTENT response, but if you are after NOT_FOUND instead you have a convenience method since Spring 5.1 - ResponseEntity.of():

 @GetMapping("/hello")
 ResponseEntity<Message> getHelloMessage() {
   return ResponseEntity.of(helloService.getMessage());
 }

If you need NO_CONTENT may I suggest the following which is slightly less verbose:

 @GetMapping("/hello")
 ResponseEntity<Message> getHelloMessage() {
        return helloService.getMessage()
                .map(ResponseEntity::ok)
                .orElseGet(() -> ResponseEntity.noContent().build());
 }

Portentous answered 8/12, 2020 at 13:17 Comment(0)
S
0
@RestController
@RequestMapping("/api/v1/products")
class ProductController {

@Autowired
private ProductService customerService;

@GetMapping
ResponseEntity<List<Product>> getAllProducts() {
    List<Product> products = customerService.findAll();
    return products.isEmpty() ?
            ResponseEntity.noContent().build() :
            ResponseEntity.ok(products);
}

@GetMapping
ResponseEntity<Product> getProductById(@PathVariable("id") int id) {
    return MagicResponse.of(customerService.findById(id))
            .ifPresentOrElse(HttpStatus.OK, HttpStatus.NOT_FOUND);
}

}


public final class MagicResponse<T> {

private final Optional<T> t;

private MagicResponse(T t) {
    this.t = Optional.ofNullable(t);
}


public static <T> MagicResponse of(T t) {
    return new MagicResponse(t);
}

public ResponseEntity<T> ifPresentOrElse(HttpStatus exists, HttpStatus notExists) {
    if (t.isEmpty()) {
        return new ResponseEntity<>(notExists);
    }
    return new ResponseEntity<>(t.get(), exists);

}


}
Subeditor answered 14/9, 2022 at 18:26 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.