2009-07-08 7 views
39

Sto lavorando su un servizio web RESTful in Java. Ho bisogno di un buon modo per inviare messaggi di errore al client se qualcosa non va.Come inviare correttamente un messaggio HTTP al client

Secondo lo Javadoc, HttpServletResponse.setStatus(int status, String message) è deprecato "a causa del significato ambiguo del parametro del messaggio."

C'è un modo preferito per impostare il messaggio di stato o "reason phrase" della risposta? Il metodo sendError(int, String) non lo fa.

MODIFICA: Per chiarire, desidero modificare la riga di stato HTTP, ad esempio "HTTP/1.1 404 Not Found", non il contenuto del corpo. In particolare, vorrei inviare risposte come "HTTP/1.1 400 Missing customerNumber parameter".

+0

C'è qualcosa di sbagliato nella frase di motivo predefinita che il contenitore di servlet restituisce quando si utilizza sendError? – laz

+0

Non c'è niente di particolarmente sbagliato in questo, solo che voglio inviare un messaggio più specifico. –

risposta

16

Non penso che nessun cliente RESTful si aspetterebbe di guardare la frase della ragione per capire cosa è andato storto; la maggior parte dei servizi RESTful che ho visto/utilizzato invieranno le informazioni sullo stato standard e un messaggio espanso nel corpo della risposta. sendError(int, String) è l'ideale per quella situazione.

+3

Un paio di persone hanno sostenuto che non dovrei nemmeno mandare il messaggio tramite la frase di ragionamento, e credo di esserne convinto. Non posso però usare sendError (int, String), perché il contenitore maneggia il messaggio. Se chiamo sendError (400, "blah"), il corpo della risposta (almeno in WebSphere) è "Errore 400: blah". Non buono, soprattutto se si desidera restituire XML o qualche altro formato rigoroso. Chiamerò setStatus (int) e scriverò manualmente il contenuto del corpo. –

0

Non è molto chiaro quello che stai cercando di realizzare. Il mio primo pensiero è stato sendError ma tu dici che non fa quello che vuoi ... hai guardato alla creazione di una serie di "risposte di errore", che significa specifici contenuti xml o JSON (o qualsiasi altra cosa tu stia utilizzando come linguaggio di trasferimento) che contiene il messaggio di errore o il codice e qualsiasi altra informazione utile?

Ho fatto qualcosa del genere per i servizi RESTful basati su Spring-mvc qualche tempo fa e ha funzionato bene, ma devi catturare e gestire praticamente ogni eccezione per impedire al client di ricevere un messaggio generico di 500 o qualcosa del genere. Gli Spring Exception Resolver hanno funzionato bene per questo.

Spero che questo aiuti ... se non, forse un po 'più di chiarezza su ciò che stai cercando di realizzare. Scusate se sono denso e mi manca qualcosa di ovvio.

2

Non conosco bene le "migliori pratiche" in merito a REST. Ma so che il concetto è basato su HTTP e su come dovrebbe funzionare naturalmente. Quindi, che ne dici di utilizzare un tipo mime e un testo semplice all'interno del corpo per un errore dell'applicazione, come "application/myapp-exception" e alcuni "Bla bla"? Puoi fornire una libreria client per questo.

Non utilizzerei i codici di risposta HTTP per gli errori dell'applicazione. Perché mi piace sapere cosa non funziona: che si tratti della mia applicazione o del mio server HTTP.

(spero, ci vediamo alcuni consigli migliori pratiche anche qui.)

+3

In tal caso, * 403 Forbidden * denota un server HTTP malfunzionante o un'applicazione non funzionante? E che dire di * 418 Sono una teiera *, come definito in http://www.ietf.org/rfc/rfc2324.txt? ;-) – Arjan

+0

Hehe, buon punto. Ma ho proposto questa soluzione, perché i contenuti dell'intestazione sono limitati. Quindi sì, devo correggermi. È corretto utilizzare il codice di risposta per indicare un errore di applicazione/server. Ma non vorrei inviare il messaggio di errore con l'intestazione, a causa delle limitazioni (codifica, lunghezza ecc.). Per inviare il messaggio di errore, propongo di utilizzare il tipo MIME e il corpo della risposta. Quindi la tua applicazione non deve conoscere alcuna limitazione quando crea il messaggio di errore per il cliente. – cafebabe

0

penso che il sendError dovrebbe farlo, ma il server applicazioni possono essere fallendo ... IBM WebSphere 3.5 fallito su di me un molto tempo fa mentre Tomcat propagava il messaggio bene; vedi JavaServer Pages (JSP) and JSTL - Error page: preserve header "HTTP/1.x 400 My message"? sui forum di Sun.

Alla fine ho usato la seguente soluzione, ma questo è una specie di JSP specifici, e può in effetti essere antiche:

<%@ page isErrorPage="true" %> 
<% 
    // This attribute is NOT set when calling HttpResponse#setStatus and then 
    // explicitely incuding this error page using RequestDispatcher#include() 
    // So: only set by HttpResponse#sendError() 
    Integer origStatus = 
     (Integer)request.getAttribute("javax.servlet.error.status_code"); 
    if(origStatus != null) { 
     String origMessage = 
      (String)request.getAttribute("javax.servlet.error.message"); 
     if(origMessage != null) { 
      response.reset(); 
      response.setContentType("text/html"); 
      // deprecated, but works: 
      response.setStatus(origStatus.intValue(), origMessage); 
      // would yield recursive error: 
      // response.sendError(origStatus, origMessage); 
     } 
    } 
%> 

E se vi capita di testare con Internet Explorer: Mostra messaggi di errore HTTP disattivare" ". (Quando non lo disabilita, IE ha qualche strana richiesta di una lunghezza minima del contenuto HTML che, se non soddisfatta, farebbe -o far sì che IE mostri il proprio messaggio di errore.Vedi anche la chiave di registro HKEY_LOCAL_MACHINE\Software\Microsoft\Internet Explorer\Main\ErrorThresholds su Microsoft Description of Hypertext Transport Protocol Error Messages.)

8

Dopo aver chiarito, l'ho provato in Tomcat.Esecuzione

response.sendError(HttpServletResponse.SC_BAD_REQUEST, "message goes here"); 

rendimenti

HTTP/1.1 400 message goes here 

come la prima linea nella risposta.

Deve esserci un problema con il contenitore servlet che si sta utilizzando.

+0

Secondo la documentazione, sendError è garantito solo per inviare il messaggio nel contenuto. Non dice nulla sulla linea di stato. –

+0

Hmmm, non ricordo sendError che imposta alcun contenuto? Ho sempre pensato che la parte del web server avrebbe generato del contenuto predefinito se l'applicazione non lo avesse fornito. Anche il link che hai fornito non sembra menzionare questo? – Arjan

17

Se si utilizza Tomcat, vedere l'org.apache.coyote.USE_CUSTOM_STATUS_MSG_IN_HEADER impostazione:

http://tomcat.apache.org/tomcat-5.5-doc/config/systemprops.html

  • Se questo è vero messaggi di stato HTTP personalizzati saranno utilizzati all'interno di intestazioni HTTP. Gli utenti devono assicurarsi che tali messaggi siano codificati ISO-8859-1, in particolare se l'input fornito dall'utente è incluso nel messaggio, per prevenire una possibile vulnerabilità XSS. Se non specificato, verrà utilizzato il valore predefinito di false.

Vedi questa pagina per qualche dettaglio sulla vulnerabilità originale:

http://www.securityfocus.com/archive/1/archive/1/495021/100/0/threaded

+0

Aha, buono a sapersi! – Arjan

0

In applicazione web primavera alimentato, in esecuzione su Tomcat ho utilizzare i seguenti bean:

import java.util.Map; 
import java.util.Set; 
import java.util.Map.Entry; 

import org.springframework.beans.factory.InitializingBean; 

public class SystemPropertiesInitializingBean implements InitializingBean { 

    private Map<String, String> systemProperties; 

    @Override 
    public void afterPropertiesSet() throws Exception { 
     if (null == systemProperties || systemProperties.isEmpty()) { 
      return; 
     } 

     final Set<Entry<String, String>> entrySet = systemProperties.entrySet(); 
     for (final Entry<String, String> entry : entrySet) { 

      final String key = entry.getKey(); 
      final String value = entry.getValue(); 

      System.setProperty(key, value); 
     } 

    } 

    public void setSystemProperties(final Map<String, String> systemProperties) { 
     this.systemProperties = systemProperties; 
    } 

} 

E in applicationContext .xml:

<bean class="....SystemPropertiesInitializingBean"> 
    <property name="systemProperties"> 
     <map> 
      <entry key="org.apache.coyote.USE_CUSTOM_STATUS_MSG_IN_HEADER" value="true"/> 
     </map> 
    </property> 
</bean>