2014-09-12 12 views
5

Qual è il modo corretto di gestire un parametro errato in un servizio RESTful? Al momento ho un endpoint che restituisce un 400 se viene inviato il tipo di dati errato.REST Gestione di un parametro errato

@ResponseBody 
@RequestMapping(produces = "application/json", method = RequestMethod.GET, value="/v1/test") 
public MyResponse getSomething(@RequestParam BigDecimal bd) { 
    MyResponse r = new MyResponse(); 
    r.setBd(bd); 
    return r; 
} 

Sarebbe davvero bello se l'utente finale dovesse passare, per esempio, una stringa invece di un BigDecimal, che la risposta sarebbe tornato un JSON con il codice di risposta, lo stato, e qualsiasi altra cosa che vorrei per contenere, piuttosto che solo un 400. C'è un modo per farlo?

Aggiornamento: il mio pensiero iniziale era quello di avvolgere tutti i parametri e quindi controllare se è il tipo corretto in quella classe wrapper. Sembra un po 'sciocco. Non c'è un validatore che potrei semplicemente aggiungere al classpath che riconoscerebbe qualcosa del genere?

Inoltre, c'è un modo per gestirlo abbastanza facilmente con un tipo Bean che potrei creare da solo, ma per quanto riguarda i tipi standard come BigDecimal?

UPDATE-2: questo aggiornamento risponde alla risposta che utilizza @ExceptionHandler.

TestController.java

import java.math.BigDecimal; 
import javax.servlet.http.HttpServletRequest; 
import org.springframework.stereotype.Controller; 
import org.springframework.web.bind.ServletRequestBindingException; 
import org.springframework.web.bind.annotation.ExceptionHandler; 
import org.springframework.web.bind.annotation.RequestMapping; 
import org.springframework.web.bind.annotation.RequestMethod; 
import org.springframework.web.bind.annotation.RequestParam; 
import org.springframework.web.bind.annotation.ResponseBody; 

@Controller 
@RequestMapping("/") 
public class TestController { 

    //this is where correct input from user is passed, no binding errors 
    @RequestMapping(produces = "application/json", method = RequestMethod.GET, value="/v1/test") 
    public 
    @ResponseBody 
    MyResponse getSomething(@RequestParam BigDecimal bd) { 
     MyResponse r = new MyResponse(); 
     r.setBd(bd); 
     return r; 
    } 

    //this will handle situation when you except number and user passess string (A123.00 for example) 
    @ExceptionHandler(ServletRequestBindingException.class) 
    public @ResponseBody MyErrorResponse handleMyException(Exception exception, HttpServletRequest request) { 

     MyErrorResponse r = new MyErrorResponse(); 
     r.setEexception(exception); 

     return r; 
    } 


} 

TestUnitTest.java

public class TestUnitTest { 

protected MockMvc mockMvc; 

@Autowired 
protected WebApplicationContext wac; 

@Before 
public void setup() { 
    this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build(); 
} 


@Test 
public void test() throws Exception { 
    String url = "/v1/test?bd=a123.00"; 

    log.info("Testing Endpoint::: " + url); 

    MvcResult result = mockMvc.perform(get(url)) 
         .andExpect(status().isOk()) 
         .andReturn(); 

    log.info("RESPONSE::: " + result.getResponse().getContentAsString()); 
} 

} 

MyResponse.java

import java.math.BigDecimal; 

public class MyResponse { 

    private BigDecimal bd; 

    public BigDecimal getBd() { 
     return bd; 
    } 

    public void setBd(BigDecimal bd) { 
     this.bd = bd; 
    } 

} 

MyErrorResponse.java

public class MyErrorResponse { 

    private Exception exception; 

    public Exception getException() { 
     return exception; 
    } 

    public void setEexception(Exception e) { 
     this.exception = e; 
    } 

} 

risposta

3

Usa primavera @ExceptionHandler insieme con lo standard @RequestMapping annotazioni come questa:

//this is where correct input from user is passed, no binding errors 
@RequestMapping(produces = "application/json", method = RequestMethod.GET, value="/v1/test") 
public 
@ResponseBody 
MyResponse getSomething(@RequestParam BigDecimal bd) { 
    MyResponse r = new MyResponse(); 
    r.setBd(bd); 
    return r; 
} 

//this will handle situation when there's exception during binding, for example you except number and user passess string (A123.00 for example) 
@ExceptionHandler(TypeMismatchException.class) 
public 
@ResponseBody 
MyErrorResponse handleMyException(Exception exception, HttpServletRequest request) { 
    //.... 
} 

TypeMismatchException è un'eccezione generale gettato quando si cerca di impostare una proprietà di fagioli. Si può generalizzare il codice ancora di più e cogliere ogni eccezione vincolante con alcuni metodi, per esempio:

@ExceptionHandler(TypeMismatchException.class) 
public 
@ResponseBody 
String typeMismatchExpcetionHandler(Exception exception, HttpServletRequest request) { 
    return "type mismatch"; 
} 

@ExceptionHandler(MissingServletRequestParameterException.class) 
public 
@ResponseBody 
String missingParameterExceptionHandler(Exception exception, HttpServletRequest request) { 
    return "missing param"; 
} 

@ExceptionHandler(Exception.class) 
public 
@ResponseBody 
String generalExceptionHandler(Exception exception, HttpServletRequest request) { 
    return "general exception"; 
} 

E 'molto flessibile, permettendo a molti parametri in firma e restituito oggetti Annotation Type ExceptionHandler

Con @ResponseBody È possibile restituire qualsiasi oggetto, che può essere serializzato in JSON. È necessario solo avere la libreria di jackson nel classpath ma presumo che tu sappia già questo

+0

Sarebbe bello se avessi un'eccezione per passarlo. Il mio caso credo sia diverso. Mi piacerebbe essere in grado di convalidare i parametri passati al servizio RESTful. Se credi che posso ancora usare questo, ti dispiacerebbe fare un esempio? Grazie! – Matt

+0

Non sono sicuro di aver capito bene. Quindi vuoi consentire all'utente di passare qualsiasi cosa e quindi convalidarlo? '@ ExceptionHandler' viene chiamato quando viene generata un'eccezione, ad esempio durante i parametri di binding (quando si esclude il numero e la stringa è passata ecc.), Quando si arriva a' @ RequestMapping' significa che l'associazione è corretta e che è possibile convalidare. Potresti spiegare un po 'di più? – kamil

+0

Nell'esempio sopra ho, il metodo dovrebbe prendere un BigDecimal. Ma se l'utente finale supera A123.00, restituisce un 400 (l'errore è che c'è un 'A' davanti a 123.00). Sto arrivando alla conclusione che non puoi farlo a meno che non completi il ​​tipo di dati, come suggerisce Sotirios ... – Matt

Problemi correlati