2011-10-27 17 views
7

Mentre si lavora con javascript che utilizza ampiamente i servizi REST - incluso l'uso di vocab come GET, PUT, POST, DELETES, ecc; Ho trovato difficile schernire il lato server in modo che lo sviluppo del front end possa andare avanti indipendentemente (dal back-end).Come prendere in giro una chiamata REST complessa dal lato server?

E 'anche utile a volte catturare i dati multi-step, in modo che possiamo aiutare riprodurre l'intera catena di REST, anche (o bug relativi al front-end che vengono attivati ​​da queste catene)

Quali strumenti posso utilizzare per simulare le chiamate REST, specialmente quelle di stato? (Ad esempio, se faccio un PUT su qualche risorsa, mi aspetto che il prossimo GET su di esso cambi in qualche modo)

Ho provato SOAPUI 4.0.1 ed è REST che il mocking è deludente. Inoltre, il mio bisogno è al di là del single state mocking (che chiunque può fare con un file .json statico). Devo fare lo stato di transizione tipo di mock; lavorare con intestazioni Content-Range sarebbe il migliore.

Chiunque?

risposta

2

In realtà ho finito per creare il mio motore Mock Java REST che può praticamente prendere in giro qualsiasi risposta. Finché è possibile eseguire manualmente o tagliare un file di testo che simula l'intera risposta HTTP, è possibile utilizzare la mia soluzione per deridere il servizio.

Ecco il servlet:

package com.mockrest.debug; 

import java.io.BufferedReader; 
import java.io.IOException; 
import java.io.InputStream; 
import java.io.InputStreamReader; 
import java.io.OutputStream; 
import java.io.OutputStreamWriter; 
import java.util.Arrays; 
import java.util.regex.Matcher; 
import java.util.regex.Pattern; 

import javax.servlet.GenericServlet; 
import javax.servlet.ServletException; 
import javax.servlet.ServletRequest; 
import javax.servlet.ServletResponse; 
import javax.servlet.http.HttpServlet; 
import javax.servlet.http.HttpServletRequest; 
import javax.servlet.http.HttpServletResponse; 
import javax.servlet.http.HttpSession; 

/** 
* Servlet implementation class MockGridData 
*/ 
public class MockRest extends HttpServlet { 
    private static final long serialVersionUID = 1L; 

    /** 
    * @see HttpServlet#HttpServlet() 
    */ 
    public MockRest() { 
     super(); 
     // TODO Auto-generated constructor stub 
    } 

    @Override 
    public void service(ServletRequest req, ServletResponse res) 
      throws ServletException, IOException { 
     sub:{ 
      HttpServletRequest request = (HttpServletRequest)req; 
      HttpServletResponse response = (HttpServletResponse)res; 
      String setdata = request.getParameter("__setdata"); 
      if (setdata!=null && setdata.length()>0){ 
       System.err.println("Setting Data..."); 
       HttpSession sess = request.getSession(true); 
       String data = "/"+request.getParameter("__setdata"); 
       sess.setAttribute("data", data); 
       try{ 
        InputStream is = getServletContext().getResourceAsStream(data); 
        if (is!=null){ 
         is.close(); 
         response.getWriter().write("Successfully pointed next REST call to:"+data); 
        } 
        else{ 
         response.sendError(500, "Cannot find resource:"+data); 
        } 
       } 
       catch (IOException ioe){ 
        response.sendError(500, Arrays.deepToString(ioe.getStackTrace())); 
       } 

      } 
      else{ 
       System.err.println("Fetching Data..."); 
       HttpSession sess = request.getSession(false); 
       if (sess==null || sess.getAttribute("data")==null){ 
        response.sendError(500,"Session invalid or no Previous Data Set!"); 
       } 
       String rsrc = (String)sess.getAttribute("data"); 
       System.err.println("Resource Being used:"+rsrc); 
       InputStream is = getServletContext().getResourceAsStream(rsrc); 
       if (is!=null){ 
        String statusline = readLine(is); 
        Pattern statusPat = Pattern.compile("^HTTP/1.1 ([0-9]+) (.*)$"); 
        Matcher m = statusPat.matcher(statusline); 
        if (m!=null && m.matches()){ 
         int status = Integer.valueOf(m.group(1)); 
         response.setStatus(status, m.group(2)); 
        } 
        else{ 
         throw new ServletException("Bad input file: status line parsing failed, got this as status line:"+statusline); 
        } 
        String line; 
        Pattern httpHeaderPat = Pattern.compile("^([^:]+): (.*)$"); 
        while ((line=readLine(is))!=null){ 
         if (line.length()==0){ 
          // end of headers 
          break; 
         } 
         Matcher m2 = httpHeaderPat.matcher(line); 
         if (m2!=null && m2.matches()){ 
          response.setHeader(m2.group(1), m2.group(2)); 
         } 
        } 
        OutputStream os = response.getOutputStream(); 
        byte[] buf = new byte[1024]; 
        int size; 
        while ((size=is.read(buf))>0){ 
         os.write(buf, 0, size); 
        } 
        os.flush(); 
       } 
      } 
     } 
    } 

    private String readLine(InputStream is) throws IOException { 
     StringBuffer sb = new StringBuffer(); 
     char c; 
     while ((c=(char)is.read())!='\n'){ 
      sb.append(c); 
     } 
     if (sb.charAt(sb.length()-1) == '\r'){ 
      sb.deleteCharAt(sb.length()-1); 
     } 
     return sb.toString(); 
    } 

} 

per configurarlo, posizionare i file di risposta predefiniti all'interno della cartella WebContent. Di solito finisco questi file con le estensioni .http.

Un file di esempio init.http è di seguito. Finta abbiamo messo questo file all'interno di una cartella chiamata data all'interno WebContent:

HTTP/1.1 200 OK 
Date: Wed, 26 Oct 2011 18:31:45 GMT 
Server: Microsoft-IIS/6.0 
X-Powered-By: ASP.NET 
X-AspNet-Version: 4.0.30319 
Content-Range: items 0-1/2 
Content-Length: 385 
Cache-Control: private 
Content-Type: application/json 

[ 
    { 
    "id": "249F0", 
    "field1": " Global", 
    "displaystartdate": "2007-10-20", 
    "displayenddate": "2012-10-20", 
    "status": "Major Delay", 
    "children": true 
    }, 
    { 
    "id": "962581", 
    "field2": "Europe", 
    "displaystartdate": "2007-10-20", 
    "displayenddate": "2012-10-20", 
    "status": "Major Delay", 
    "children": true 
    } 
] 

intestazioni mosto separato con corpo da una riga vuota (senza spazi, niente). Le persone che hanno familiarità con http noteranno che si tratta di una pura risposta http. Questo è apposta.

È possibile utilizzare questo strumento per simulare una qualsiasi delle intestazioni http di cui si desidera avere la risposta; anche andando così lontano per rispondere con una diversa intestazione del server (nel mio esempio, ho simulato la risposta fingendo di essere IIS 6.0); o un diverso codice di stato HTTP, ecc.

Per richiamarlo dal browser/javascript; primo primo con:

http://yourserver/yourweb/MockGridData?__setdata=data/init.http 

Poi, nel tuo javascript o di riposo AJAX chiamata, se si va a

http://yourserver/yourweb/MockGridData 

con qualsiasi metodo o parametro; otterrà la risposta http precedentemente realizzata; anche fino alla gamma di contenuti; Intestazioni di cache; ecc. Se poi hai bisogno della successiva chiamata AJAX per restituire qualcos'altro, chiama semplicemente con __setdata di nuovo. Ti suggerisco di configurare alcuni pulsanti per eseguire la transizione di stato esplicita nella tua app web.

supponendo che tutto è configurato, per una catena REST simulato, uno sviluppatore può fare:

  1. invocano

    http://yourserver/yourweb/MockGridData?__setdata=data/init.http 
    
  2. lanciare un modulo javascript che si tradurrà in chiamando il numero (ad esempio, con GET

    http://yourserver/yourweb/MockGridData 
    
  3. fare clic su un pulsante che quindi fa:

    http://yourserver/yourweb/MockGridData?__setdata=data/step1.http 
    
  4. eseguire un altro passo javascript che si tradurrà in chiamando il numero (ad esempio, con PUT)

    http://yourserver/yourweb/MockGridData 
    
  5. clic su un altro pulsante che poi fa:

    http://yourserver/yourweb/MockGridData?__setdata=data/step2.http 
    
  6. eseguire un altro javascript passo ciò comporterà la chiamata (ad esempio con GET)

    http://yourserver/yourweb/MockGridData 
    

    ma questa volta mi aspetto un risultato diverso da # 4.

Questo dovrebbe funzionare anche con le risposte binarie e gzip, ma non l'ho provato.

+0

Congratulazioni per la soluzione. Quando sei in grado, assicurati di contrassegnare la tua risposta come "accettata" in modo che altri possano apprendere dal tuo successo. Acclamazioni ~ –