2016-01-05 10 views
5

Vorrei utilizzare il sistema di instradamento Akka Http, insieme al suo sistema di reiezione, ma ho bisogno di annidare la risposta Json per un rifiuto all'interno di un blocco di messaggi Json generico.Come avvolgere genericamente un rifiuto con Akka-Http

Ho lavorato in modo molto generico creando un RejectionHandler, aggiungendo casi per tutti i possibili rifiuti e gestendoli tutti con il codice di risposta e il messaggio specifici.

esempio:

// Wraps string into control block format 
def WrappingBlock(msg: String) = ??? 

val myRejectionHandler = RejectionHandler 
.newBuilder() 
.handle{case MalformedRequestContentRejection(msg, detail) => 
      complete(BadRequest, WrappingBlock(msg)) } 
...  // Further lines for all other possible rejections 
...  // along with their response codes and messages. 
...  // It would be nice if this was just generic code 
...  // rather than specific to every rejection type. 
.result() 


val routes = handleRejections(myRejectionHandler){ 
    ... 
} 

Tuttavia, quello che vorrei è il codice di risposta che Akka HTTP fornisce di default e anche il messaggio di stampa piuttosto che viene fornito, appena annidato all'interno di un controllo involucro JSON senza una linea per ogni possibile tipo di rifiuto. Sembra che dovrebbe essere possibile ma non sono stato in grado di completarlo.

risposta

5

Penso che sia possibile fare ciò che si desidera utilizzando una combinazione di handleRejections esplicitamente con mapResponse. In primo luogo, prendere in considerazione questa semplice definizione della rotta:

(get & path("foo")){ 
    complete((StatusCodes.OK, HttpEntity(ContentTypes.`application/json`, """{"foo": "bar"}"""))) 
} 

Se ricevo una richiesta di corrispondenza Risponderò con JSON e il mio interlocutore è felice perché può analizzare la risposta come JSON. Ma se si cerca di chiamare questo endpoint con una richiesta POST, si otterrà una risposta come segue:

HTTP 405 Method Not Allowed 

Date: Wed, 06 Jan 2016 13:19:27 GMT 
Content-Type: text/plain; charset=UTF-8 
Content-Length: 47 
Allow: GET 
Server: akka-http/2.3.12 

HTTP method not allowed, supported methods: GET 

Quindi qui si ottiene una risposta di testo che non è auspicabile. Siamo in grado di risolvere il problema universalmente con l'aggiunta di un paio di direttive alla cima del mio albero di routing in questo modo:

mapResponse(wrapToJson){ 
    handleRejections(RejectionHandler.default){ 
    (get & path("foo")){ 
     complete((StatusCodes.OK, HttpEntity(ContentTypes.`application/json`, """{"foo": "bar"}"""))) 
    } 
    } 
} 

Con wrapToJson essere definito come:

def wrapToJson(resp:HttpResponse):HttpResponse = { 

    //If we get a text/plain response entity, remap it otherwise do nothing 
    val newResp = resp.entity match{ 
    case HttpEntity.Strict(ContentTypes.`text/plain(UTF-8)` , content) => 
     val jsonResp = s"""{"error": "${content.utf8String}"}""" 
     resp.copy(entity = HttpEntity(ContentTypes.`application/json`, jsonResp)) 
    case other => 
     resp 
    } 

    newResp 
} 

Questo è un esempio molto semplice, e probabilmente avresti un modo migliore per generare il json, ma questo serve solo a mostrare come è possibile correggere le risposte del testo del piano dal gestore di rifiuto predefinito. Ora, è necessario nidificare il gestore di rifiuto predefinito sotto lo mapResponse esplicitamente perché la gestione automatica viene aggiunta al di fuori del livello superiore di qualsiasi albero definito e quindi mapResponse non vedrebbe i casi di rifiuto. È comunque possibile ottenere la gestione predefinita tramite lo RejectionHandler.default.

Speriamo che questo sia vicino a quello che cercavi.

+0

È molto interessante non aver pensato di utilizzare una combinazione con la risposta della mappa, vedrò questa opzione. – AlexC

+0

È possibile scrivere come RejectionHandler personalizzato? Non potevo venire con una soluzione del genere perché richiede 'Route' in uscita dove, come nel tuo caso, abbiamo' Direttiva'. – expert

+0

@ruslan, non sono sicuro di quello che stai chiedendo. È certamente possibile creare un gestore di rifiuto personalizzato come descritto qui: http://doc.akka.io/docs/akka-stream-and-http-experimental/2.0.2/scala/http/routing-dsl/rejections.html# Customizing_Rejection_Handling. Non sei sicuro del perché intendi in 'Route' vs' Directive' in uscita. Forse creare una nuova domanda basata su questo che descrive meglio la tua situazione. – cmbaxter

Problemi correlati