2017-04-03 2 views
1

Ich verwende Spring Boot, um einen REST-Dienst zu schreiben. Ich muss eine andere Entität zurückgeben, wenn eine Operation erfolgreich ist und entsprechend fehlschlägt. ResponseEntity im Frühjahr wird durch den Typ T parametriert. Ich weiß, ich kann die Art weglassen und nur ResponseEntity zurückkehren, aber das ist nicht genug, wenn man versucht, die Antwort mit Java 8 Optional ‚s orElse Kette zu schaffen:Anderen generischen Typ mit Optional.map.orElse zurückgeben

public ResponseEntity getDashboard(String user, UUID uuid) { 
Optional<Dashboard> dashboard = dashboardService.getDashboard(user, uuid); 

// this gives unchecked assignment: 'org.springframework.http.ResponseEntity' 
// to 'org.springframework.http.ResponseEntity<my.package.SomeClass>' 
return dashboard 
    .map(ResponseEntity::ok) 
    .orElse(createNotFoundResponse(uuid, "No such object")); 
} 

public static <T> ResponseEntity createNotFoundResp(T entity, String message) { 
    ResponseMessage<T> responseMessage = new ResponseMessage<>(message, entity); 
    return ResponseEntity.status(HttpStatus.NOT_FOUND).body(responseMessage); 
} 

Aufgrund Java Compiler Typinferenz orElse Klausel sollte die Rückkehr gleicher Typ wie wenn das optionale nicht leer ist, dh ResponseEntity<Dashboard> und nicht ResponseEntity<ResponseMessage>. Ich habe versucht, dieses Problem zu unterlaufen, die von verschiedenen Rückwegen wie diese bieten:

if (dashboard.isPresent()) { 
    return ResponseEntity.ok(dashboard.get()); 
} else { 
    return createNotFoundResponse(uuid, "No such object"); 
} 

... aber dann Intellij unterstreicht den dashboard.isPresent() Teil und schreit, dass dieser Block den oben vereinfacht werden kann (was in nicht kontrollierten Warnung führt).

Gibt es eine Möglichkeit, diesen Code ohne Compiler-Warnungen und @SuppressUnchecked Annotationen sauber zu schreiben?

+0

Das Hinzufügen von '' zu 'ResponseEntity' in den Signaturen von ** beiden Methoden ** führte dazu, dass die Warnung für mich wegging. – slim

+0

"Gibt es eine Möglichkeit, diesen Code ohne Compiler-Warnungen sauber zu schreiben?" Ja, mit dem if/else. Intellij ist falsch. (Mein intellij schlägt diese "Vereinfachung" nicht vor). –

+1

@AndyTurner stimmen nicht zu. 'if (x.isPresent() {return f (x.get());} else {return y;}' ist ein Antipattern. 'return x.map (F: f) .oderElse (y)' is The Right Thing To Do. – slim

Antwort

1

Gibt es eine Möglichkeit, diesen Code ohne Compiler-Warnungen und @SuppressUnchecked Annotationen sauber zu schreiben?

Ich kann nicht in diesem Fall Compiler Warnungen loswerden. Eine der möglichen sauberen Lösungen (zumindest keine Compilerwarnungen) lehnt die Idee von Optional.map zugunsten einer einfachen if/else oder ?: -gesteuerten Strategie ab, die jedoch nicht mit fließenden Schnittstellen verfügbar ist.

static <T, U> ResponseEntity<?> okOrNotFound(final Optional<T> optional, final Supplier<? extends U> orElse) { 
    return okOrNotFound(optional, "Not found", orElse); 
} 

static <T, U> ResponseEntity<?> okOrNotFound(final Optional<T> optional, final String message, final Supplier<? extends U> orElse) { 
    return optional.isPresent() 
      ? status(OK).body(optional.get()) 
      : status(NOT_FOUND).body(new NotFound<>(orElse.get(), message)); 
} 
@RequestMapping(method = GET, value = "/") 
ResponseEntity<?> get(
     @RequestParam("user") final String user, 
     @RequestParam("uuid") final UUID uuid 
) { 
    final Optional<Dashboard> dashboard = dashboardService.getDashboard(user, uuid); 
    return okOrNotFound(dashboard,() -> uuid); 
} 

Hinweis orElse ist nicht wirklich das, was man wollte: orElseGet faul ist und ruft nur seine Lieferanten, wenn der gegebene optionale Wert nicht vorhanden ist.

Allerdings bietet Spring einen besseren Weg, um das zu erreichen, was Sie brauchen, und ich glaube, eine sauberere Art, die Dinge so zu machen. Werfen Sie einen Blick auf controller advices, die für solche Zwecke konzipiert sind.

// I would prefer a checked exception having a super class like ContractException 
// However you can superclass this one into your custom super exception to serve various purposes and contain exception-related data to be de-structured below 
final class NotFoundException 
     extends NoSuchElementException { 

    private final Object entity; 

    private NotFoundException(final Object entity) { 
     this.entity = entity; 
    } 

    static NotFoundException notFoundException(final Object entity) { 
     return new NotFoundException(entity); 
    } 

    Object getEntity() { 
     return entity; 
    } 

} 

Nun ist die REST-Controller-Methode wird:

@RequestMapping(method = GET, value = "/") 
Dashboard get(
     @RequestParam("user") final String user, 
     @RequestParam("uuid") final UUID uuid 
) { 
    return dashboardService.getDashboard(user, uuid) 
      .orElseThrow(() -> notFoundException(uuid)); 
} 

Frühling ist intelligent genug, um Objekte zu status(OK).body(T) konvertieren selbst, so dass wir werfen nur eine Ausnahme ein einzelnes Objekt enthalten wir daran interessiert sind, auf Weiter. eine Ausnahme Probe Controller Beratung könnte wie folgt aussehen:

@ControllerAdvice 
final class ExceptionControllerAdvice { 

    @ExceptionHandler(NotFoundException.class) 
    ResponseEntity<NotFound<?>> acceptNotFoundException(final NotFoundException ex) { 
     return status(NOT_FOUND).body(notFound(ex)); 
    } 

} 

wo notFound() Verfahren wie diese implementiert:

static NotFound<?> notFound(final NotFoundException ex) { 
    return notFound(ex, "Not found"); 
} 

static NotFound<?> notFound(final NotFoundException ex, final String message) { 
    return new NotFound<>(ex.getEntity(), message); 
} 

Für meine Spike-Projekt liefert die folgenden Ergebnisse:

  • _http: // localhost: 8080 /?? User = Besitzer & uuid = 00000000-0000-0000-0000-000000000000 - {"description":"dashboard owned by owner"}
  • _http: // localhost: 8080/user = user & uuid = 00000000-0000-0000-0000-000000000000 - {"entity":"00000000-0000-0000-0000-000000000000","message":"Not found"}
Verwandte Themen