Hoe een complexe REST-aanroep van de server kant bespotten?

Tijdens het werken met JavaScript dat REST-services op grote schaal gebruikt - inclusief vocabs zoals GET, PUT, POST, DELETES, enz; Ik heb het moeilijk gevonden om de serverkant te bespotten zodat de ontwikkeling van de front-end onafhankelijk kan doorgaan (van back-end).

Het is ook handig om gegevens in meerdere stappen vast te leggen, zodat we kunnen helpen de volledige keten van REST te reproduceren, zelfs (of bugs met betrekking tot de frontend die uit deze ketens worden geactiveerd)

Welke hulpmiddelen kan ik gebruiken om REST-oproepen te bespotten, met name stateful ones? (d.w.z. als ik een PUT op een bepaalde bron doe, verwacht ik dat de volgende GET erop iets gaat veranderen)

Ik heb SOAPUI 4.0.1 geprobeerd en het is REST-spot is teleurstellend. Bovendien is mijn behoefte meer dan single state mocking (wat iedereen kan doen met een statisch .json-bestand). Ik moet het overgangstype van moppen doen; werken met Content-Range headers zou het beste zijn.

Iedereen?

7

2 antwoord

Hier is nog een ander hulpprogramma voor de rest van spot: https://github.com/mkotsur/restito .

3
toegevoegd

Uiteindelijk heb ik uiteindelijk mijn eigen Java REST Mock Engine gemaakt die in principe elke reactie kan bespotten. Zolang je een tekstbestand kunt knippen of plakken dat het hele http-antwoord simuleert, kun je mijn oplossing gebruiken om de service te bespotten.

Hier is de 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();
    }

}

Om het te configureren, plaatst u vooraf ingebouwde antwoordbestanden in uw map WebContent . Ik beëindig deze bestanden meestal met .http extensies.

Een voorbeeld van een init.http -bestand staat hieronder. Beweren dat we dit bestand in WebContent in een map met de naam data </​​code> hebben geplaatst:

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
  }
]

Headers must scheiden met body door een lege regel (geen spaties, nada). Mensen die bekend zijn met http zullen merken dat het een puur http-antwoord is. Dit is expres.

U kunt deze tool gebruiken om een ​​van de http-headers te simuleren die u wilt dat het antwoord heeft; zelfs zo ver gaan om te reageren met verschillende serverheader (in mijn voorbeeld simuleerde ik het antwoord dat deed alsof ik IIS 6.0 was); of een andere HTTP-statuscode, etc.

Om het aan te roepen vanuit uw browser/javascript; primeur het eerst met:

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

Dan in je JavaScript of REST AJAX-gesprek, als het gaat

http://yourserver/yourweb/MockGridData

met elke methode of parameter; het krijgt het http-antwoord dat je eerder hebt gemaakt; zelfs tot het inhoudsbereik; Cache headers; enz. Als u dan de volgende AJAX-oproep nodig hebt om iets anders terug te sturen, belt u eenvoudig opnieuw met __ setdata </​​code>. Ik stel voor dat je een paar knoppen instelt om de expliciete statusovergang in je web-app uit te voeren.

Ervan uitgaande dat alles is ingesteld, kan een ontwikkelaar voor een gesimuleerde REST-keten het volgende doen:

  1. invoke

    http://yourserver/yourweb/MockGridData?__setdata=data/init.http
    
  2. run a JavaScript module that will result in calling (say, with GET)

    http://yourserver/yourweb/MockGridData
    
  3. click a button that then does:

    http://yourserver/yourweb/MockGridData?__setdata=data/step1.http
    
  4. run another JavaScript step that will result in calling (say, with PUT)

    http://yourserver/yourweb/MockGridData
    
  5. click another button that then does:

    http://yourserver/yourweb/MockGridData?__setdata=data/step2.http
    
  6. run another JavaScript step that will result in calling (say, with GET)

    http://yourserver/yourweb/MockGridData
    

    but this time expecting different result than #4.

Dit zou zelfs met binaire en gzipped antwoorden moeten werken, maar ik heb dat niet getest.

2
toegevoegd
Gefeliciteerd met de oplossing. Wanneer u in staat bent, moet u uw antwoord als 'geaccepteerd' markeren, zodat anderen van u succes kunnen leren. Proost ~
toegevoegd de auteur Andrew Kozak, de bron