How to reset/remove/clear the request body in rest-assured - java

I have to pass form-parameter as the body to my request. When I try
Response post = request.urlEncodingEnabled(true).log().all().config(RestAssured.config()
.encoderConfig(EncoderConfig.encoderConfig()
.encodeContentTypeAs("x-www-form-urlencoded", ContentType.URLENC)))
I am getting the error message as "You can either send form parameters OR body content in POST, not both!"
When I checked the log, previous api's response passed as body to this request. How to remove/reset/clear the body and pass only the form-parameter.

You should always use a new RequestSpecification Instance for each request.
Before each new request call a function like:
public void beforeNewRequest(){
restUtils.resetRestAssured(); //reset existing instance
restUtils = RestUtils.getInstance(); //get new instance
}
RestUtil.java class
public class RestUtils {
private static RestUtils apiUtilsInstance = null;
private RequestSpecification httpRequest;
private RestUtils() {
httpRequest = RestAssured.given();
}
public static RestUtils getInstance() {
if (apiUtilsInstance == null)
apiUtilsInstance = new RestUtils();
return apiUtilsInstance;
}
public RequestSpecification getRequestSpecification() {
return httpRequest;
}
public void resetRestAssured() {
apiUtilsInstance = null;
}
}

Related

Feign Client Error Handling - Suppress the Error/Exception and convert to 200 success response

I am using feign client to connect to downstream service.
I got a requirement that when one of the downstream service endpoint returns 400 ( it's partial success scenario ) our service need this to be converted to 200 success with the response value.
I am looking for a best way of doing this.
We are using error decoder to handle the errors and the above conversion is applicable for only one endpoint not for all the downstream endpoints and noticed that decode() method should returns exception back.
You will need to create a customized Client to intercept the Response early enough to change the response status and not invoke the ErrorDecoder. The simplest approach is to create a wrapper on an existing client and create a new Response with a 200 status. Here is an example when using Feign's ApacheHttpClient:
public class ClientWrapper extends ApacheHttpClient {
private ApacheHttpClient delegate;
public ClientWrapper(ApacheHttpClient client) {
this.client = client;
}
#Override
public Response execute(Request request, Request.Options options) throws IOException {
/* execute the request on the delegate */
Response response = this.client.execute(request, options);
/* check the response code and change */
if (response.status() == 400) {
response = Response.builder(response).status(200).build();
}
return response;
}
}
This customized client can be used on any Feign client you need.
Another way of doing is by throwing custom exception at error decoder and convert this custom exception to success at spring global exception handler (using #RestControllerAdvice )
public class CustomErrorDecoder implements ErrorDecoder {
#Override
public Exception decode(String methodKey, Response response) {
if (response.status() == 400 && response.request().url().contains("/wanttocovert400to200/clientendpoints") {
ResponseData responseData;
ObjectMapper mapper = new ObjectMapper();
try {
responseData = mapper.readValue(response.body().asInputStream(), ResponseData.class);
} catch (Exception e) {
responseData = new ResponseData();
}
return new PartialSuccessException(responseData);
}
return FeignException.errorStatus(methodKey, response);
}}
And the Exception handler as below
#RestControllerAdvice
public class GlobalControllerExceptionHandler {
#ResponseStatus(HttpStatus.OK)
#ExceptionHandler(PartialSuccessException.class)
public ResponseData handlePartialSuccessException(
PartialSuccessException ex) {
return ex.getResponseData();
}
}
Change the microservice response:
public class CustomFeignClient extends Client.Default {
public CustomFeignClient(
final SSLSocketFactory sslContextFactory, final HostnameVerifier
hostnameVerifier) {
super(sslContextFactory, hostnameVerifier);
}
#Override
public Response execute(final Request request, final Request.Options
options) throws IOException {
Response response = super.execute(request, options);
if (HttpStatus.SC_OK != response.status()) {
response =
Response.builder()
.status(HttpStatus.SC_OK)
.body(InputStream.nullInputStream(), 0)
.headers(response.headers())
.request(response.request())
.build();
}
return response;
}
}
Add a Feign Client Config:
#Configuration
public class FeignClientConfig {
#Bean
public Client client() {
return new CustomFeignClient(null, null);
}
}

Jersy REST service does not return object value from HashMap

We have created the Jersy REST service.
We are returning response object with 10 String fields and one Hash Map in format specified in below code snippet.
We could receive all the 10 string field values.
We can also see the HashMap with key/values when access it from client program.
CustomObject in HashMap became String when access it from client program.
so it throws exception
Listcom.tetherfi.restful.model.AppTypeData#2dc0f069
Test
err: java.lang.ClassCastException: java.lang.String cannot be cast to com.tetherfi.restful.model.AppTypeData.
We use Moxy to do the mapping. Please advise whether there is a way to retain the object value.
Response object from Jersy service
#XmlRootElement
public class ServiceRespParams {
private String param1;
private String param2;
private String param3;
private String param4;
private String param5;
private HashMap<String,ArrayList<AppTypeData>>hm;
public HashMap<String, ArrayList<AppTypeData>> getHm() {
return hm;
}
public void setHm(HashMap<String, ArrayList<AppTypeData>> hm) {
this.hm = hm;
}
Client code
public class Client {
public ServiceResponse postRequest(ServiceRequest request) {
Logger logger = Logger.getLogger("JerseyClient");
ServiceResponse serviceResponse = null;
WebTarget webTarget = null;
Response response = null;
// creating client with MOXy JSON
Client client = ClientBuilder.newClient();
try {
String url = "http://localhost:9080/Rest_ProxyLayer/webapi/doc01";
logger.info("Sending Request.... "+url);
webTarget = client.target(url).property(ClientProperties.CONNECT_TIMEOUT, 1000)
.property(ClientProperties.READ_TIMEOUT, 1000);
logger.info("Request Sent...");
Invocation.Builder invocationBuilder = webTarget.request(MediaType.APPLICATION_JSON);
response = invocationBuilder.get();
logger.info("Response Status :"+response.getStatus());
serviceResponse = response.readEntity(ServiceResponse.class);
System.out.println("Response from service"+ serviceResponse.getHm().get("CreditCard").get(0).getParam1());
} catch (Exception e) {
logger.error("Error!! Check connection..", e);
e.printStackTrace();
}
return serviceResponse;
}
}
It throws class cast exception when try to access the method.
System.out.println("Response from service"+ serviceResponse.getHm().get("CreditCard").get(0).getParam1());
Output from Rest service. Hashmap entries are below. It shows object but it seems to be converted to string.
HM: {CreditCard=[com.tetherfi.restful.model.AppTypeData#1b6c278e, com.tetherfi.restful.model.AppTypeData#208f5a00]}
Thanks
Vinoth

Requests and response from netty server hangs

I have the following code to create a netty web server based on http server created in the netty's example. My buisness logic is the following.
public class HttpServerHandler extends SimpleChannelInboundHandler<Object> {
private final static Logger LOG = LogManager
.getLogger(HttpServerHandler.class);
private WorkflowService workflowService;
private HttpRequest request;
private final StringBuffer buff = new StringBuffer();
private API avalancheApi;
public HttpServerHandler(WorkflowService workflowService) {
this.workflowService = workflowService;
this.avalancheApi = new API(this.workflowService);
}
#Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
LOG.debug("channelActive");
LOG.debug(ctx.toString());
};
#Override
public void channelReadComplete(ChannelHandlerContext ctx) {
ctx.flush();
}
#Override
public void channelRead0(ChannelHandlerContext ctx, Object msg)
throws IOException {
avalancheApi.setContext(ctx);
if (msg instanceof HttpRequest) {
HttpRequest request = this.request = (HttpRequest) msg;
if (HttpHeaders.is100ContinueExpected(request)) {
send100Continue(ctx);
}
String command = getCommand(request);
LOG.debug(command);
Map<String, List<String>> parameters = getParameters(request);
LOG.debug(parameters);
switch (command) {
case "/login":
ctx = avalancheApi.login(parameters);
break;
case "/test":
ctx = avalancheApi.test();
break;
default:
break;
}
}
if (msg instanceof LastHttpContent) {
LOG.debug("msg is of LastHttpContent");
}
if (!HttpHeaders.isKeepAlive(request)) {
// If keep-alive is off, close the connection once the content is
// fully written.
ctx.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(
ChannelFutureListener.CLOSE);
}
}
public class API {
private static final Logger LOG = LogManager.getLogger(API.class);
private ChannelHandlerContext ctx;
private HttpResponse response;
private WorkflowService workflowService;
public API(WorkflowService workflowService) {
this.workflowService = workflowService;
this.ctx = null;
}
public void setContext(ChannelHandlerContext ctx) {
this.ctx = ctx;
}
public ChannelHandlerContext login(Map<String, List<String>> parameters) {
boolean success;
String username = getUsername(parameters);
String password = getPassword(parameters);
User user = null;
user = workflowService.login(username, password);
success = validateLogin(user);
this.response = writeLoginResponse(success);
this.ctx.write(this.response);
writeLoginContext(success, response);
return this.ctx;
}
private void writeLoginContext(boolean success, HttpResponse response) {
JsonObject jsonResponseMessage = new JsonObject();
jsonResponseMessage.addProperty("result", success);
LOG.debug(jsonResponseMessage.toString());
this.ctx.write(Unpooled.copiedBuffer(jsonResponseMessage.toString(),
CharsetUtil.UTF_8));
this.response.headers().set(HttpHeaders.Names.CONTENT_LENGTH,
jsonResponseMessage.toString().length());
}
private HttpResponse writeLoginResponse(boolean success) {
if (success)
return createSuccessfullLoginResponse();
else
return createLoginFailureResponse();
}
private HttpResponse createLoginFailureResponse() {
return Response.loginFailureResponse();
}
private HttpResponse createSuccessfullLoginResponse() {
return Response.loginSuccessResponse();
}
}
Response class is only creating the response and the content_type which is of application/json. Content Length is set in the API class. Using python client with requests, results in the request made in http://localhost/login?username=name&password=pass works only once. The second time everything works, but it doesn't finish processing the request and send the response object. Api calls get executed normally, and I also get the message of LastHttpContext message getting print. The problem sometimes happens with browser too. Am I missing something? Maybe the content data and the content length doesn't match? Could it be that when making requests from python client, the content of the previous context isn't flushed and the content_length value of the header and content length of the context doesn't match?
Just wild guess
this.response.headers().set(HttpHeaders.Names.CONTENT_LENGTH,
jsonResponseMessage.toString().length());
Instead, shouldn't you be doing jsonResponseMessage.toString().getBytes().length ?? Sometimes, one character is not just one byte.
My guess is that you have overwritten the context in your API class, and as a result, are writing the response to the wrong context. Is your HttpServerHandler marked with #Shareable?

405 Method Not Allowed with Spring

I have the following test for an HTTP endpoint:
public static final String DATA_PARAMETER = "data";
public static final String ID_PARAMETER = "id";
public static final String VIDEO_SVC_PATH = "/video";
public static final String VIDEO_DATA_PATH = VIDEO_SVC_PATH + "/{id}/data";
#Multipart
#POST(VIDEO_DATA_PATH)
public VideoStatus setVideoData(#Path(ID_PARAMETER) long id, #Part(DATA_PARAMETER) TypedFile videoData);
#Test
public void testAddVideoData() throws Exception {
Video received = videoSvc.addVideo(video);
VideoStatus status = videoSvc.setVideoData(received.getId(),
new TypedFile(received.getContentType(), testVideoData));
assertEquals(VideoState.READY, status.getState());
Response response = videoSvc.getData(received.getId());
assertEquals(200, response.getStatus());
InputStream videoData = response.getBody().in();
byte[] originalFile = IOUtils.toByteArray(new FileInputStream(testVideoData));
byte[] retrievedFile = IOUtils.toByteArray(videoData);
assertTrue(Arrays.equals(originalFile, retrievedFile));
}
I'm trying to implement the requirements defined by this test with the following endpoint defined in Swing:
#RequestMapping(method = RequestMethod.POST, value = "/video/{id}/data")
public void postVideoData(#PathVariable("id") long videoId,
#RequestParam("data") MultipartFile videoData) throws IOException {
if (videoId <= 0 || videoId > videos.size()) {
throw new ResourceNotFoundException("Invalid id: " + videoId);
}
Video video = videos.get((int)videoId - 1);
InputStream in = videoData.getInputStream();
manager.saveVideoData(video, in);
}
The problem is that I get a "405 Method Not Allowed" error. What am I doing wrong so that my POST method is not being recognized?
The problem is that the client interface expects a VideoStatus object returned from the server. I declared the method on the server side to return void.
I don't know if you already fix your problem, but I got the same error, because I am new with Retrofit too :).
The solution for me, was to put an Annotation to specify the response content type, in my case
#ResponseBody
Another change that I must did, was to change void for a custom status.
Hope this helps or at least gives you a light.
Rgds.
I had the same issue. RetroFit request calls must have either a return type or Callback as last argument.
So in the RetroFitted API:
#POST("/path")
public Void saveWhatever(#Body Whatever whatever);
Than in the controller it must be :
#RequestMapping(value = "/path", method = RequestMethod.POST)
public #ResponseBody Void saveWhatever(#RequestBody Whatever whatever) {
repository.save(whatever);
return null;
}

Send HTTP GET request with header

From my Android app I want to request a URL with GET parameters and read the response.
In the request I must add a x-zip header.
The URL is something like
http://example.com/getmethod.aspx?id=111&method=Test
Can some one provide me code for that?
Two things are important: that it is a GET request and contains the x-zip header .
EDIT:
try {
HttpClient client = new DefaultHttpClient();
String getURL = "http://example.com/getmethod.aspx?id=111&method=Test";
HttpGet get = new HttpGet(getURL);
get.setHeader("Content-Type", "application/x-zip");
HttpResponse responseGet = client.execute(get);
HttpEntity resEntityGet = responseGet.getEntity();
if (resEntityGet != null) {
//do something with the response
Log.i("GET ",EntityUtils.toString(resEntityGet));
}
} catch (Exception e) {
e.printStackTrace();
}
I try with this code but I get code with .net error: Object reference not set to an instance of an object...
I think but I'm not sure this if for x-zip header, is header in my code ok?
Here's a code excerpt we're using in our app to set request headers. You'll note we set the CONTENT_TYPE header only on a POST or PUT, but the general method of adding headers (via a request interceptor) is used for GET as well.
/**
* HTTP request types
*/
public static final int POST_TYPE = 1;
public static final int GET_TYPE = 2;
public static final int PUT_TYPE = 3;
public static final int DELETE_TYPE = 4;
/**
* HTTP request header constants
*/
public static final String CONTENT_TYPE = "Content-Type";
public static final String ACCEPT_ENCODING = "Accept-Encoding";
public static final String CONTENT_ENCODING = "Content-Encoding";
public static final String ENCODING_GZIP = "gzip";
public static final String MIME_FORM_ENCODED = "application/x-www-form-urlencoded";
public static final String MIME_TEXT_PLAIN = "text/plain";
private InputStream performRequest(final String contentType, final String url, final String user, final String pass,
final Map<String, String> headers, final Map<String, String> params, final int requestType)
throws IOException {
DefaultHttpClient client = HTTPClientFactory.newClient();
client.getParams().setParameter(HttpProtocolParams.USER_AGENT, mUserAgent);
// add user and pass to client credentials if present
if ((user != null) && (pass != null)) {
client.getCredentialsProvider().setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(user, pass));
}
// process headers using request interceptor
final Map<String, String> sendHeaders = new HashMap<String, String>();
if ((headers != null) && (headers.size() > 0)) {
sendHeaders.putAll(headers);
}
if (requestType == HTTPRequestHelper.POST_TYPE || requestType == HTTPRequestHelper.PUT_TYPE ) {
sendHeaders.put(HTTPRequestHelper.CONTENT_TYPE, contentType);
}
// request gzip encoding for response
sendHeaders.put(HTTPRequestHelper.ACCEPT_ENCODING, HTTPRequestHelper.ENCODING_GZIP);
if (sendHeaders.size() > 0) {
client.addRequestInterceptor(new HttpRequestInterceptor() {
public void process(final HttpRequest request, final HttpContext context) throws HttpException,
IOException {
for (String key : sendHeaders.keySet()) {
if (!request.containsHeader(key)) {
request.addHeader(key, sendHeaders.get(key));
}
}
}
});
}
//.... code omitted ....//
}
You do it exactly as you showed with this line:
get.setHeader("Content-Type", "application/x-zip");
So your header is fine and the problem is some other input to the web service. You'll want to debug that on the server side.

Categories