2017-04-03 499 views
1

我使用Spring Boot來編寫REST服務。 當操作成功並相應地失敗時,我需要返回一個不同的實體。在春天ResponseEntity是通過類型T進行參數化。我知道我可以省略類型並返回剛剛ResponseEntity,而是試圖創建與Java 8 OptionalorElse鏈的響應時是不夠的:使用Optional.map.orElse返回不同​​的泛型類型

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); 
} 

由於Java編譯器的類型推斷orElse條款應返還與可選項不爲空時的類型相同,即ResponseEntity<Dashboard>而不是ResponseEntity<ResponseMessage>。我試圖顛覆此問題,通過提供不同的返回路徑是這樣的:

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

...但隨後的Intellij凸顯dashboard.isPresent()部分和呼喊該塊可以被簡化爲上述一個(這導致未檢查警告)。

有沒有辦法在沒有任何編譯器警告和@SuppressUnchecked註釋的情況下乾淨地編寫此代碼?

+0

將** '添加到**兩種方法**的簽名中的'ResponseEntity',導致警告消失。 – slim

+0

「有沒有辦法在沒有任何編譯器警告的情況下乾淨地編寫代碼」是的,用if/else。 Intellij是錯誤的。 (我intellij不建議「簡化」)。 –

+1

@AndyTurner不同意。 'if(x.isPresent(){return f(x.get());} else {return y;}'是一個反模式。'return x.map(F:f)。orElse(y)'是Right事情要做 – slim

回答

1

有沒有辦法在沒有任何編譯器警告和@SuppressUnchecked註釋的情況下乾淨地編寫此代碼?

我不想在這種情況下襬脫編譯器警告。可能的乾淨解決方案之一(至少沒有編譯器警告)拒絕Optional.map贊成簡單的if/else?:驅動戰略不適用於流暢接口的想法。

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); 
} 

注意orElse是不是真的你想要什麼:orElseGet是懶惰,如果給定可選的值不存在只調用它的供應商。

然而,Spring提供了一種更好的方式來實現你所需要的,我相信這是一種更乾淨的方式。看看專爲此目的而設計的controller advices

// 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; 
    } 

} 

現在REST控制器方法變爲:

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

春天是足夠聰明的對象轉換爲status(OK).body(T)本身,所以我們只是拋包含單個對象,我們感興趣的是一個例外下一步。 ,樣品控制器例外的建議可能如下:

@ControllerAdvice 
final class ExceptionControllerAdvice { 

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

} 

其中notFound()方法是這樣實現的:

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); 
} 

對於我秒殺項目提供了以下結果:

  • _http://本地主機:8080 /??用戶=所有者& UUID = 00000000-0000-0000-0000-000000000000 - {"description":"dashboard owned by owner"}
  • _http://本地主機:8080 /用戶=用戶& UUID = 00000000-0000-0000-0000-000000000000 - {"entity":"00000000-0000-0000-0000-000000000000","message":"Not found"}