【问题标题】:How to mock a complex REST call from the server side?如何从服务器端模拟复杂的 REST 调用?
【发布时间】:2011-12-16 18:58:05
【问题描述】:

在使用广泛使用 REST 服务的 javascript 时,包括使用 GET、PUT、POST、DELETES 等词汇;我发现很难模拟服务器端,因此前端开发可以独立(后端)进行。

有时捕获多步数据也很有用,因此我们甚至可以帮助重现整个 REST 链(或从这些链触发的与前端相关的错误)

我可以使用哪些工具来模拟 REST 调用,尤其是有状态的调用? (即,如果我对某个资源执行 PUT,我希望它的下一个 GET 会以某种方式改变)

我尝试了 SOAPUI 4.0.1,它的 REST 模拟令人失望。另外,我需要的不仅仅是单状态模拟(任何人都可以使用静态 .json 文件来完成)。我需要做状态转换类型的模拟;最好使用 Content-Range 标头。

有人吗?

【问题讨论】:

    标签: web-services rest mocking chaining simulate


    【解决方案1】:

    实际上,我最终创建了自己的 Java REST 模拟引擎,它基本上可以模拟任何响应。只要您可以手工制作或剪切粘贴模拟整个 http 响应的文本文件,您就可以使用我的解决方案来模拟服务。

    这是 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();
        }
    
    }
    

    要对其进行配置,请将预构建的响应文件放在您的 WebContent 文件夹中。我通常以.http 扩展名结束这些文件。

    init.http 文件示例如下。假设我们将此文件放在 WebContent 内名为 data 的文件夹中:

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

    标题必须与正文用空行分隔(无空格,nada)。熟悉 http 的人会注意到它是一个纯粹的 http 响应。这是故意的。

    您可以使用此工具模拟您希望响应具有的任何 http 标头;甚至到目前为止响应不同的服务器标头(在我的示例中,我模拟了假装是 IIS 6.0 的响应);或不同的 HTTP 状态码等。

    从您的浏览器/javascript 调用它;首先用:

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

    然后在您的 javascript 或 REST AJAX 调用中,如果它转到

    http://yourserver/yourweb/MockGridData
    

    使用任何方法或参数;它将获得您之前制作的 http 响应;甚至到内容范围;缓存头;等等。如果您需要随后的 AJAX 调用来返回其他内容,只需再次调用 __setdata。我建议您设置几个按钮来在您的网络应用中进行显式状态转换。

    假设一切都设置好了,对于模​​拟的 REST 链,开发人员可以这样做:

    1. 调用

      http://yourserver/yourweb/MockGridData?__setdata=data/init.http
      
    2. 运行一个 javascript 模块,该模块将导致调用(例如,使用 GET)

      http://yourserver/yourweb/MockGridData
      
    3. 单击一个按钮,然后执行:

      http://yourserver/yourweb/MockGridData?__setdata=data/step1.http
      
    4. 运行另一个将导致调用(例如,使用 PUT)的 javascript 步骤

      http://yourserver/yourweb/MockGridData
      
    5. 单击另一个按钮,然后执行:

      http://yourserver/yourweb/MockGridData?__setdata=data/step2.http
      
    6. 运行另一个将导致调用(例如,使用 GET)的 javascript 步骤

      http://yourserver/yourweb/MockGridData
      

      但这次预期的结果与 #4 不同。

    这甚至应该适用于二进制和压缩响应,但我还没有测试过。

    【讨论】:

    • 祝贺解决方案。如果可以,请确保将您的答案标记为“已接受”,以便其他人可以从您的成功中学习。干杯~
    【解决方案2】:

    这是另一个自制的休息模拟工具:https://github.com/mkotsur/restito

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-07-28
      • 1970-01-01
      • 2012-07-31
      • 2017-06-27
      • 2014-10-31
      • 2023-03-03
      • 2016-03-07
      • 2011-11-06
      相关资源
      最近更新 更多