#web-services #rest #mocking #chaining #simulate
#веб-сервисы #отдых #насмешливый #цепочка #имитировать
Вопрос:
Работая с javascript, который широко использует службы REST, Включая использование таких слов, как GET, PUT, POST, DELETES и т. Д.; Мне было трудно издеваться над серверной частью, чтобы разработка интерфейса могла продолжаться независимо (от серверной части).
Также иногда полезно собирать многоступенчатые данные, чтобы мы могли помочь воспроизвести даже всю цепочку REST (или ошибки, связанные с интерфейсом, которые запускаются из этих цепочек).
Какие инструменты я могу использовать для моделирования вызовов REST, особенно вызовов с отслеживанием состояния? (т.Е. Если я ПОМЕЩАЮ какой-то ресурс, я ожидаю, что следующий ДОСТУП к нему каким-то образом изменится)
Я попробовал SOAPUI 4.0.1, и его насмешка над REST разочаровывает. Кроме того, моя потребность выходит за рамки насмешки над одним состоянием (что любой может сделать со статическим файлом .json). Мне нужно сделать переход состояния типа mocks; лучше всего было бы работать с заголовками диапазона содержимого.
Кто-нибудь?
Ответ №1:
На самом деле я закончил тем, что создал свой собственный движок Java REST Mock Engine, который в принципе может имитировать любой ответ. Пока вы можете вручную создать или вырезать-вставить текстовый файл, который имитирует весь http-ответ, вы можете использовать мое решение для макетирования службы.
Вот сервлет:
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 amp;amp; 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 amp;amp; 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 amp;amp; 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
файла приведен ниже. Представьте, что мы поместили этот файл в папку data
с именем inside 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
}
]
Заголовки должны разделяться с телом пустой строкой (без пробелов, 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 вернул что-то еще, просто вызовите with __setdata
еще раз. Я предлагаю вам настроить несколько кнопок для выполнения явного перехода состояния в вашем веб-приложении.
Предполагая, что все настроено, для моделируемой цепочки REST разработчик может сделать:
- вызвать
http://yourserver/yourweb/MockGridData?__setdata=data/init.http
- запустите модуль javascript, который приведет к вызову (скажем, с помощью GET )
http://yourserver/yourweb/MockGridData
- нажмите кнопку, которая затем выполняет:
http://yourserver/yourweb/MockGridData?__setdata=data/step1.http
- запустите еще один шаг javascript, который приведет к вызову (скажем, с помощью PUT )
http://yourserver/yourweb/MockGridData
- нажмите другую кнопку, которая затем выполняет:
http://yourserver/yourweb/MockGridData?__setdata=data/step2.http
- запустите еще один шаг javascript, который приведет к вызову (скажем, с помощью GET )
http://yourserver/yourweb/MockGridData
но на этот раз ожидаемый результат отличается от # 4.
Это должно работать даже с двоичными и архивированными ответами, но я этого не проверял.
Комментарии:
1. Поздравляю с решением. Когда вы сможете, пожалуйста, не забудьте отметить свой ответ как «принятый», чтобы другие могли поучиться у вас успеху. Приветствия ~
Ответ №2:
Вот еще один доморощенный инструмент для редактирования rest: https://github.com/mkotsur/restito .