When I send a request using a proxy client, if I get a certain response, I would like to be able to modify the request and then send the same request again for all requests.
Normally I would do something like:
BookStore proxy = JAXRSClientFactory.create("http://books", BookStore.class);
try
{
proxy.getBook("someId");
}
catch(WebApplicationException ex)
{
Response r = ex.getResponse();
if (r.getStatusCode() == 404)
{
proxy.getBook("anotherId");
}
}
But in this case, there is a common thing I want to do for all requests: If I get a specific http code, modify some header values, and then try again (probably with a limit on the amount of retries).
I haven't seen a way that cxf proxy clients explicitly support this, how could I go about implementing it?
You need to write an interceptor to do this for every request.
here you go for sample code and documentation http://cxf.apache.org/docs/jax-rs-filters.html
Related
I'm new to Java and found a confusing behaviour related with RestTemplate.
It happened with an API returning large body (~5MB) over a quite slow network condition. The code is like below
ResponseEntity<MyEntity[]> result = restTemplate.exchange(url, HttpMethod.GET, entity, MyEntity[].class);
And also a ClientHttpRequestInterceptor is set to log before and after the request.
The confusing thing is that the after request log is logged only a while after remote server giving the response, and the HTTP Status code can be print in the log.
But the above statement took much more time to finally receive the data. Look inside the thread stack, it was reading data from socket.
I also look inside the resttemplate class and found:
response = request.execute();
handleResponse(url, method, response);
if (responseExtractor != null) {
return responseExtractor.extractData(response);
}
It seems to extractData after the execute().
My doubt is:
How does the client side know the status code even before get all the data? It just extracts necessary fields from the top packets?
Since the server has already sent out the response, where the response data is stored during the process?
It stores the data that it receives from the underlying HTTP in memory.
Client side can know what's the status code because with HTTP you get the headers and status code first before the response body. But this doesn't matter with RestTemplate as it promises to give you an object of ResponseEntity in the end, which contains everything from the http response be it status codex headers or body.
RestTemplate is an abstraction over an HttpClient, most client's give you the option to implement callbacks for separate events like onHeadersReceived(), onStatusReceived() etc. But if you are using RestTemplate this means you don't require such fine grained control.
I am trying to transfer an entity via a HTTP PUT request using following code.
public ClientEntity createEntity(URI absoluteUri,
ClientEntity ce) {
ODataEntityCreateRequest<ClientEntity> request = client
.getCUDRequestFactory().getEntityCreateRequest(absoluteUri, ce);
request.setAccept("application/json;odata.metadata=minimal");
ODataEntityCreateResponse<ClientEntity> response = request.execute();
return response.getBody();
}
The function getEntityCreateRequest, however, only creates a POST request and allows (as far as I know) no alteration of the used HttpMethod.
Unfortunately, ODataEntityUpdateRequest is also not an option, because this request only allows the HttpMethod PATCH or REPLACE.
Within the documentation I have found a function setMethod(HttpMethod method), but this method is only available for the server not the client implementation (https://olingo.apache.org/javadoc/odata4/org/apache/olingo/server/api/ODataRequest.html).
Further I discovered setUseXHTTPMethod(boolean value), which tunnels PUT, MERGE, PATCH, DELETE via POST. I checked my client's configuration to make sure, that isUseXHTTPMethod is false, which it is. (Reference to functions: https://olingo.apache.org/javadoc/odata4/org/apache/olingo/client/api/Configuration.html)
Hence I am wondering how to make a PUT request with Apache Olingo?
Thank you very much for your input.
ODataEntityUpdateRequest with UpdateType.REPLACE should be equivalent to a PUT method.
Notice the source code, line 31.
Implement the updateEntity method.
I have a Swing application that requires to send login details to a server (written in Node.js) for verification.
So far i've managed to successfully send http POST requests and get JSON objects from the server, only problem is, when sniffing with WireShark i can actually see the request body and thus the password and the username in it, so I guess thats not very secured, i don't mind the server, I'm not so sure i want a SSL connection since I dont mind about the objects returning security.
my code looks something like that:
// Http members
private AsyncHttpClient asyncHttpClient = new AsyncHttpClient();
and the function to make the call itself is looks like this:
private void login(String username, String password) throws IOException, InterruptedException, ExecutionException {
asyncHttpClient.preparePost(LOGIN_URL).
addFormParam("email", username).
addFormParam("password", password).
execute(new AsyncCompletionHandler<Response>() {
#Override
public Response onCompleted(Response response) {
// do something with the response object
return response;
}
});
}
As you can see nothing elaborate here.
in WireShark the request body looks like this:
Line-based text data: application/x-www-form-urlencoded
email=myUserName&password=myPassword
I did try to look around and search for answers both here and other places and reading the documents but it looks like there is a simple way of doing this and I'm just missing something..
Thanks for any answer
If you don't want anyone along the line to be able to see your data, you need to encrypt it somehow.
The easiest way to encomplish this at a reasonable level of security is using a https connection.
If you only care about the data, you can also try to implement some form of end-to-end encryption yourself, but this is hard to get right.
Summary: Just use https
Things to make it more secure:
add Transport encryption aka HTTPS
attackers from outside your computer see nothing
send password as hash, not cleartext
your passwoed remains unknown to an attacker watching pre-encrypted data in your browser. But could still resend same request to gain access
one - time token per sms
...
I have a simple REST client with some basic functionality, and so far I'm stuck as I don't know how to process those request and send them correctly into the server. So far I've tried this in the filter, without any luck.
if (!request.getHeader("/rest/").equals(null)){
String loginForm = config.getInitParameter("LoginParam");
res.sendRedirect(req.getContextPath() + loginForm);
return;
}
And I get the following error because of that deny.
Exception in thread "main" org.jboss.resteasy.client.ClientResponseFailure
How should I check that the request is coming from the REST client so I can let it pass through?
I'd normally add header Accept: application/json (or xml or whatever) to indicate the client wants to get data as oppose to HTML.
I use Netbeans to generate web service client code, client-style JAX-WS, so i can invoke a web service API.
However, when I invoke the web service API, I get the exception:
com.sun.xml.internal.ws.client.ClientTransportException: The server sent HTTP status code 307: Temporary Redirect
Why do I get this? What is the workaround? I know the problem isn't with the web service itself, because I can get responses fine via soapUI and .Net.
Faced the same problem about a month ago.
Web service client classes were generated using Apache CXF and web service returned HTTP
status 307, which led to the same exception.
Invocation of the same web service method using soapUI with property Follow Redirects set to true was successful and returned needed data.
After googling awhile, it looked like there is no property to enable following redirects in the JAX-WS for this.
So, below is the code which is currently working, though I'm not sure it is compliant with any standards:
Supposing generated client classes looks like:
// generated service class
public class MyWebServiceClient extends javax.xml.ws.Service {
// ...
private final QName portName = "...";
// ...
public RetrieveMyObjects getRetrieveMyObjects() {
return super.getPort(portName, RetrieveMyObject.class);
}
// ...
}
// generated port interface
// annotations here
public interface RetrieveMyObjects {
// annotations here
List<MyObject> getAll();
}
Now, upon executing following code:
MyWebServiceClient wsClient = new MyWebServiceClient("wsdl/location/url/here.wsdl");
RetrieveMyObjectsPort retrieveMyObjectsPort = wsClient.getRetrieveMyObjects();
wsClient should return instance which is both instance of RetrieveMyObjects & javax.xml.ws.BindingProvider interfaces. It is not stated anywhere on the surface of JAX-WS, but it seems that a lot of code is based on that fact. One can re-assure him\herself by executing something like:
if(!(retrieveMyObjectsPort instanceof javax.xml.ws.BindingProvider)) {
throw new RuntimeException("retrieveMyObjectsPort is not instance of " + BindingProvider.class + ". Redirect following as well as authentication is not possible");
}
Now, when we are sure that retrieveMyObjectsPort is instance of javax.xml.ws.BindingProvider we can send plain HTTP POST request to it, simulating SOAP request (though it looks incredibly incorrect & ugly, but this works in my case and I didn't find anything better while googling) and check whether web service will send redirect status as a response:
// defined somewhere before
private static void checkRedirect(final Logger logger, final BindingProvider bindingProvider) {
try {
final URL url = new URL((String) bindingProvider.getRequestContext().get(ENDPOINT_ADDRESS_PROPERTY));
logger.trace("Checking WS redirect: sending plain POST request to {}", url);
final HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setInstanceFollowRedirects(true);
connection.setRequestMethod("POST");
connection.setRequestProperty("Content-Type", "text/html; charset='UTF-8'");
connection.setDoOutput(true);
if(connection.getResponseCode() == 307) {
final String redirectToUrl = connection.getHeaderField("location");
logger.trace("Checking WS redirect: setting new endpoint url, plain POST request was redirected with status {} to {}", connection.getResponseCode(), redirectToUrl);
bindingProvider.getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, redirectToUrl);
}
} catch(final Exception e) {
logger.warn("Checking WS redirect: failed", e);
}
}
// somewhere at the application start
checkRedirect(logger, (BindingProvider) retrieveMyObjectsPort);
Now, what this method does is: it takes BindingProvider.ENDPOINT_ACCESS_PROPERTY of retrieveMyObjectsPort i.e. the url to which this port method will be sending SOAP requests and sends plain HTTP POST request as described above. Then it checks whether response status is 307 - Temporary Redirect (other statuses like 302 or 301 may also be included) and if it is, gets the URL to which web service is redirecting and sets new endpoint for the specified port.
In my case this checkRedirect method is called once for each web service port interface and then everything seems to work fine:
Redirect is checked on url like http://example.com:50678/restOfUrl
Web service redirects to url like https://example.com:43578/restOfUrl (please note that web service client authentication is present) - endpoint of a port is set to that url
Next web service requests executed via that port are successful
Disclaimer: I'm quite new to webservices and this is what I managed to achieve due to the lack of solutions for this questions, so please correct me if something is wrong here.
Hope this helps
Yes I know this post is old, but I've had similar errors, and thought maybe somebody would benefit from my solution.
the one that plagued me the most was:
com.sun.xml.ws.client.ClientTransportException: The server sent HTTP status code 200: OK
Which turns out to mean an incomplete response header. Apparently jax-ws does some kind of validation that includes validating the HTTP headers as well. And the server I was using was just sending an empty header.
It worked like a charm after adding 'application/soap+xml' to the Content-Type header.