How to get the response header in a RestEasy client? - java

i´m implementing a Restful service using Jax-RS 2.0 (Resteasy 3.0.7.Final) and share the interface between client and service.
The return value is void because ClientResponse is deprecated since RestEasy introduced JAX-RS 2.0 in version 3+.
To return the location of the new created object i inject the response, using the #Context annotation, and add the Content-Location header.
For example:
Shared Interface:
#Path("/")
#Consumes("application/xml")
#Produces("application/xml")
interface Resource {
#Path("createSomething")
void createSomething(AnyObject object);
...
}
Implementation class (The Service):
class ResourceImpl {
...
#Context org.jboss.resteasy.spi.HttpResponse response;
...
#Override
void createSomething(AnyObject object) throws AnyException {
String id = service.create(object);
response.getOutputHeaders().putSingle("Content-Location",
"/createSomething/" + id);
response.setStatus(Response.Status.CREATED.getStatusCode());
}
}
The client (build with the Resteasy Proxy Framework):
...
ResteasyClient client = new ResteasyClientBuilder().build();
ResteasyWebTarget target = client.target(baseUrl);
Resource resource = (Resource) target.proxy(Resource.class);
resource.createSomething(anyObject);
...
How can i retrieve Header information (and others, like Atom Links) which has been injected by the service?
Is it reasonable to use client side Filters and Interceptors?
Thank You

The best solution i found was to use a Filter to process the incoming response header.
public class HeaderFilter implements ClientResponseFilter {
private Map<String, String> headers = new HashMap<>();
private List<String> headerFilter = new ArrayList<>();
public final void addHeaderFilter(final String header) {
headerFilter.add(header);
}
public final void removeHeaderFilter(final String header) {
headerFilter.remove(header);
}
public final String getHeader(final String header) {
return headers.get(header);
}
#Override
public final void filter(final ClientRequestContext requestContext,
final ClientResponseContext responseContext)
throws IOException {
headers = new HashMap<>();
for (String headerToLookFor : headerFilter) {
String header = responseContext.getHeaderString(headerToLookFor);
if (header != null) {
headers.put(headerToLookFor, header);
} else {
...
}
}
}
}

Related

How to handle set-cookie Header in SignalR java Client?

I am using SignalR java client in android and .net core3 as my webservice.
I configured a set-cookie header in my responses from webservice to prevent DDOS attacks , but now I can't connect to my Hub because there is no option in SignalR java client to Handle set-cookie header .
How can I fix this problem?
Well after alot of search I came to this :
hubConnection = HubConnectionBuilder.create(HUB_URL).withHandshakeResponseTimeout(60000).withHeaders(mapHeader).setHttpClientBuilderCallback(param1 -> {
param1.addInterceptor(new ApiClient.ReceivedCookiesInterceptor(G.context));
param1.addInterceptor(new ApiClient.AddCookiesInterceptor(G.context));
}).withTransport(TransportEnum.ALL).withAccessTokenProvider(Single.defer(() -> Single.just("An Access Token"))).build();
(setHttpClientBuilderCallback) will give me a configuration builder which I can use to handle setcookie from response .
this is my ReceivedCookiesInterceptor:
public static class ReceivedCookiesInterceptor implements Interceptor {
private Context context;
public ReceivedCookiesInterceptor(Context context) {
this.context = context;
}
#Override
public Response intercept(Chain chain) throws IOException {
Response originalResponse = chain.proceed(chain.request());
if (!originalResponse.headers("Set-Cookie").isEmpty()) {
Log.i("HubLogin", "intercept: "+originalResponse.headers("Set-Cookie"));
HashSet<String> cookies = (HashSet<String>) PreferenceManager.getDefaultSharedPreferences(context).getStringSet("PREF_COOKIES", new HashSet<String>());
for (String header : originalResponse.headers("Set-Cookie")) {
cookies.add(header);
}
SharedPreferences.Editor memes = PreferenceManager.getDefaultSharedPreferences(context).edit();
memes.putStringSet("PREF_COOKIES", cookies).apply();
memes.apply();
}
return originalResponse;
}
}
and this is my AddCookiesInterceptor:
public static class AddCookiesInterceptor implements Interceptor {
public static final String PREF_COOKIES = "PREF_COOKIES";
private Context context;
public AddCookiesInterceptor(Context context ) {
this.context = context;
}
#Override
public Response intercept(Interceptor.Chain chain) throws IOException {
Request.Builder builder = chain.request().newBuilder();
HashSet<String> preferences = (HashSet<String>) PreferenceManager.getDefaultSharedPreferences(context).getStringSet(PREF_COOKIES, new HashSet<>());
for (String cookie : preferences) {
builder.addHeader("Cookie", cookie);
}
return chain.proceed(builder.build());
}
}

How to continue request processing after sending response from filter in Jersey?

I have a situation where I need to return an "accepted" response for every request received and publish the actual response later to a separate endpoint outside the service.
To implement the 'accepted' Response I implemented a filter.
public class AcknowledgementFilter implements ContainerRequestFilter{
#Override
public void filter(ContainerRequestContext containerRequestContext) throws IOException {
containerRequestContext.abortWith(Response.accepted().build());
// call Resource method in new Thread() . <------ ?
}
}
Implementation of service endpoints:
#Path("/vendor")
public class VendorHandler {
#POST
public void addVendor(VendorRequest addVendorRequest)){
vendor = new Vendor();
Client.publish(vendor); // publish request to an endpoint
return null;
}
How do I call the addVendor of VendorHandler(or any method depends on request) from the acknowledgement filter?
Is there any other way to implement an accepted response for every request then process the request separately?
You can use AsyncResponse,
#GET
#ManagedAsync
#Produces(MediaType.APPLICATION_JSON)
public void getLives(#Suspended final AsyncResponse asyncResponse,
#DefaultValue("0") #QueryParam("newestid") final int newestId,
#QueryParam("oldestid") final Integer oldestId) {
asyncResponse.setTimeoutHandler(asyncResponse1 -> {
logger.info("reached timeout");
asyncResponse1.resume(Response.ok().build());
});
asyncResponse.setTimeout(5, TimeUnit.MINUTES);
try {
List<Life> lives = oldestId == null ?
Lifes.getLastLives(newestId) : Lifes.getOlderLives(oldestId);
if (lives.size() > 0) {
final GenericEntity<List<Life>> entity = new GenericEntity<List<Life>>(lives) {
};
asyncResponse.resume(entity);
} else LifeProvider.suspend(asyncResponse);
} catch (SQLException e) {
logger.error(e, e);
asyncResponse.resume(new WebApplicationException(Response.Status.INTERNAL_SERVER_ERROR));
}
}
Check this Link for more details.

Client Response Application.Json

I´m trying to create a client for some Rest application. I tested the Rest application with Advanced REST client in Chrome.
I received:
{
"authorization": false,
"provider": "Provider"
}
That is okey.
but I would like obtain this in my client:
public class MainApp extends Application {
public static final String BASE_URI = "http://localhost:8000/test";
public static final String PATH_NAME= "/sytem/Consumer/r/Provider";
#Override
public void start(Stage stage) throws Exception {
}
public static void main(String[] args) {
Client client = ClientBuilder.newClient();
WebTarget target = client.target(BASE_URI).path(PATH_NAME);
Response response = target.request(MediaType.APPLICATION_JSON).get();
System.out.println(response);
client.close();
}
}
I only receive this in the terminal:
λ java -jar Client_consumer-1.0-SNAPSHOT.jar
org.jboss.resteasy.client.jaxrs.engines.ApacheHttpClient4Engine$1#6591f517
The class is :
#XmlRootElement
public class Auth_response implements Serializable {
private String Provider;
private boolean authorization;
public Auth_response() {
super();
}
public Auth_response(String Provider, boolean authorization) {
super();
this.Provider = Provider;
this.authorization = authorization;
}
public String getProvider() {
return Provider;
}
public boolean isAuthorization() {
return authorization;
}
public void setProvider(String Provider) {
this.Provider = Provider;
}
public void setAuthorization(boolean authorization) {
this.authorization = authorization;
}
#Override
public String toString() {
return "Auth_response{" + "Provider=" + Provider + ", authorization=" + authorization + '}';
}
}
I would like to know how to print properly the response, and how to convert to a java object again. I tried with some examples, but nothing works and I think that I need some guide.
EDIT:
I tried this for getting the object:
Auth_response r=
client.target("http://localhost:8000/test/system/Consumer/r/Provider")
.request(MediaType.APPLICATION_JSON_TYPE).
get(Auth_response.class);
System.out.println("funciona:");
System.out.println(r.getProvider());
System.out.println(r.isAuthorization());
But I obtain the next error:
Caused by: javax.ws.rs.ProcessingException: RESTEASY003145: Unable to find a MessageBodyReader of content-type application/json and type class ah.client_consumer.Auth_response
at org.jboss.resteasy.core.interception.ClientReaderInterceptorContext.throwReaderNotFound(ClientReaderInterceptorContext.java:42)
at org.jboss.resteasy.core.interception.AbstractReaderInterceptorContext.getReader(AbstractReaderInterceptorContext.java:75)
at org.jboss.resteasy.core.interception.AbstractReaderInterceptorContext.proceed(AbstractReaderInterceptorContext.java:52)
at org.jboss.resteasy.plugins.interceptors.encoding.GZIPDecodingInterceptor.aroundReadFrom(GZIPDecodingInterceptor.java:59)
at org.jboss.resteasy.core.interception.AbstractReaderInterceptorContext.proceed(AbstractReaderInterceptorContext.java:55)
at org.jboss.resteasy.client.jaxrs.internal.ClientResponse.readFrom(ClientResponse.java:251)
at org.jboss.resteasy.client.jaxrs.internal.ClientResponse.readEntity(ClientResponse.java:181)
at org.jboss.resteasy.specimpl.BuiltResponse.readEntity(BuiltResponse.java:213)
at org.jboss.resteasy.client.jaxrs.internal.ClientInvocation.extractResult(ClientInvocation.java:105)
... 14 more
Exception running application ah.client_consumer.MainApp
Try changing below line :
Response response = target.request(MediaType.APPLICATION_JSON).get();
System.out.println(response);
To
Response response = target.request(MediaType.APPLICATION_JSON).get(String.class);
System.out.println(response.toString());
Because you have not specified what type of response you are accepting, you are getting object as response.
SOLUTION:
Add to the code:
ResteasyProviderFactory instance=ResteasyProviderFactory.getInstance();
RegisterBuiltin.register(instance);
instance.registerProvider(ResteasyJacksonProvider.class);
The solution was finding in : Unable to find a MessageBodyReader of content-type application/json and type class java.lang.String

Adding authorization header to Jersey SSE Client request

I am using Jersey client to connect to an SSE stream. The server requires that I add a header to the http request for authorization, but I can't figure out how to add the header.
Here is my code:
Client client = ClientBuilder.newBuilder().register(SseFeature.class).build();
WebTarget target = client.target(baseurl + "/v1/devices/events/");
eventSource = EventSource.target(target).build();
eventSource.register(getEventListener());
eventSource.open();
Here is an example of the header I need to add:
Authorization: Bearer 38bb7b318cc6898c80317decb34525844bc9db55
It would be something like this for Basic Authentication:
Client client = ClientBuilder.newClient();
HttpAuthenticationFeature feature = HttpAuthenticationFeature.basicBuilder().build();
client.register(feature);
client.register(SseFeature.class);
WebTarget target = client.target(baseurl + "/v1/devices/events/")
.property(HttpAuthenticationFeature.HTTP_AUTHENTICATION_BASIC_USERNAME, "...")
.property(HttpAuthenticationFeature.HTTP_AUTHENTICATION_BASIC_PASSWORD, "...");
...
You already get the password encoded by Jersey.
And if it is a token:
Client client = ClientBuilder.newClient();
WebTarget target = client.target(baseurl + "/v1/devices/events/")
.request("...")
.header(HttpHeaders.AUTHORIZATION, "Bearer " + "... encoded token ...");
Hope it helps!
In case someone would want to add the bearer token header at the Client entity level itself, rather than at the Request entity level (in my case I had a factory method for returning preconfigured Client entities, so I had no way of adding the authorization header within the factory method, as .header(...) becomes available only after you go through the ClientBuilder.newBuilder().register(...).build().target(...).request(...) call chain, as of Jersey 2.x):
// client is a javax.ws.rs.client.Client entity
Feature feature = OAuth2ClientSupport.feature("YOUR_BEARER_TOKEN");
client.register(feature);
// now you can use client.target(...).request(...).post(...), without calling .header(...) after .request(...)
Unfortunately (as you may have guessed) this requires a new dependency: org.glassfish.jersey.security:oauth2-client
<dependency>
<groupId>org.glassfish.jersey.security</groupId>
<artifactId>oauth2-client</artifactId>
<version>2.15</version>
</dependency>
// Using SSL + Header Key
uri = UriBuilder.fromUri(sslUrl).port(sslServerPort).build();
sslConfig = SslConfigurator.newInstance().trustStoreFile(trustStoreFile).trustStorePassword(trustStorePassword);
sslContext = sslConfig.createSSLContext();
client = ClientBuilder.newBuilder().sslContext(sslContext).build();
target = client.target(uri).path(path);
Entity<?> entity = Entity.entity(Object, MediaType.APPLICATION_JSON);
response = target.request().header("key","value").post(entity);
// Using UserName & Password + Header Key
uri = UriBuilder.fromUri(url).port(serverPort).build();
basicAuth = HttpAuthenticationFeature.basic(username, userPassword);
client = ClientBuilder.newBuilder().register(basicAuth).build();
target = client.target(uri).path(path);
Entity<?> entity = Entity.entity(Object, MediaType.APPLICATION_JSON);
response = target.request().header("key","value").post(entity);
// Using only Header Key
uri = UriBuilder.fromUri(url).port(serverPort).build();
client = ClientBuilder.newBuilder().build();
target = client.target(uri).path(path);
Entity<?> entity = Entity.entity(Object, MediaType.APPLICATION_JSON);
response = target.request().header("key","value").post(entity);
Hope this helps you with your problem.
Here is the complete examples
ClientConfig clientConfig = new ClientConfig();
Client client = ClientBuilder.newClient(clientConfig);
WebTarget webTarget = client.target("http://localhost:8080/MyApp/customer/");
Invocation.Builder invocationBuilder =
webTarget.request(MediaType.APPLICATION_JSON).header(HttpHeaders.AUTHORIZATION, "your
secret key");
response = invocationBuilder.get();
output = response.readEntity(String.class);
Dependency for jersey client
<dependency>
<groupId>org.glassfish.jersey.core</groupId>
<artifactId>jersey-client</artifactId>
<version>2.25.1</version>
</dependency>
Try this:
Invocation.Builder invocationBuilder = target.request(MediaType.APPLICATION_JSON).header(HttpHeaders.AUTHORIZATION, "Bearer38bb7b318cc6898c80317decb34525844bc9db55");
I realize this question is a year old but since there are not a lot to be found on that subject, I'll share my solution.
Based on suggested OAuth2Feature, I came up with this solution:
Create a custom feature. Feature will reference a custom filter
Create a custom filter of priority HEADER_DECORATOR
Create a HeaderProvider interface. Provider will be passed to the filter
Register the WebClient with the custom feature
Header provider interface
#FunctionalInterface
public interface ISseHeaderProvider {
Map<String, String> getHeaders();
}
Custom feature
public class SseHeaderSupportFeature implements Feature {
private final SseHeaderSupportFilter filter;
public SseHeaderSupportFeature(ISseHeaderProvider provider) {
this.filter = new SseHeaderSupportFilter(provider);
}
#Override
public boolean configure(FeatureContext context) {
context.register(filter);
return true;
}
}
Custom filter
#Priority(Priorities.HEADER_DECORATOR)
public class SseHeaderSupportFilter implements ClientRequestFilter {
private final ISseHeaderProvider provider;
public SseHeaderSupportFilter(#NotNull ISseHeaderProvider provider) {
this.provider = provider;
}
#Override
public void filter(ClientRequestContext request) throws IOException {
provider.getHeaders().forEach((k, v) -> request.getHeaders().add(k, v));
}
}
Usage
ISseHeaderProvider provider = () -> MapBuilder.<String, String>builder().add("Authorization", "Bearer ...").build();
Client client = ClientBuilder.newBuilder()
.register(SseFeature.class)
.register(new SseHeaderSupportFeature(provider))
.build();
WebTarget target = client.target(UriBuilder.fromPath(getUrl()));
//EventSource eventSource = ....
This solution is generic and allows you to easily add an Authorization header without having to add another dependency.
Following answer is useful:
Server Sent Event Client with additional Cookie
It use a customized WebTarget to add cookie and the same way on header also work.
public class AuthorizationHeaderWebTarget implements WebTarget {
private WebTarget base;
private String token;
public AuthorizationHeaderWebTarget(WebTarget base, String token) {
this.base = base;
this.token = token;
}
// Inject that cookie whenever someone requests a Builder (like EventSource does):
public Invocation.Builder request() {
return base.request().header(HttpHeaders.AUTHORIZATION, token);
}
public Invocation.Builder request(String... paramArrayOfString) {
return base.request(paramArrayOfString).header(HttpHeaders.AUTHORIZATION, token);
}
public Invocation.Builder request(MediaType... paramArrayOfMediaType) {
return base.request(paramArrayOfMediaType).header(HttpHeaders.AUTHORIZATION, token);
}
public Configuration getConfiguration() {
return base.getConfiguration();
}
//All other methods from WebTarget are delegated as-is:
public URI getUri() {
return base.getUri();
}
public UriBuilder getUriBuilder() {
return base.getUriBuilder();
}
public WebTarget path(String paramString) {
return base.path(paramString);
}
public WebTarget matrixParam(String paramString, Object... paramArrayOfObject) {
return base.matrixParam(paramString, paramArrayOfObject);
}
public WebTarget property(String paramString, Object paramObject) {
return base.property(paramString, paramObject);
}
public WebTarget queryParam(String paramString, Object... paramArrayOfObject) {
return base.queryParam(paramString, paramArrayOfObject);
}
public WebTarget register(Class<?> paramClass, Class<?>... paramArrayOfClass) {
return base.register(paramClass, paramArrayOfClass);
}
public WebTarget register(Class<?> paramClass, int paramInt) {
return base.register(paramClass, paramInt);
}
public WebTarget register(Class<?> paramClass, Map<Class<?>, Integer> paramMap) {
return base.register(paramClass, paramMap);
}
public WebTarget register(Class<?> paramClass) {
return base.register(paramClass);
}
public WebTarget register(Object paramObject, Class<?>... paramArrayOfClass) {
return base.register(paramObject, paramArrayOfClass);
}
public WebTarget register(Object paramObject, int paramInt) {
return base.register(paramObject, paramInt);
}
public WebTarget register(Object paramObject, Map<Class<?>, Integer> paramMap) {
return base.register(paramObject, paramMap);
}
public WebTarget register(Object paramObject) {
return base.register(paramObject);
}
public WebTarget resolveTemplate(String paramString, Object paramObject) {
return base.resolveTemplate(paramString, paramObject);
}
public WebTarget resolveTemplate(String paramString, Object paramObject, boolean paramBoolean) {
return base.resolveTemplate(paramString, paramObject, paramBoolean);
}
public WebTarget resolveTemplateFromEncoded(String paramString, Object paramObject) {
return base.resolveTemplateFromEncoded(paramString, paramObject);
}
public WebTarget resolveTemplates(Map<String, Object> paramMap) {
return base.resolveTemplates(paramMap);
}
public WebTarget resolveTemplates(Map<String, Object> paramMap, boolean paramBoolean) {
return base.resolveTemplates(paramMap, paramBoolean);
}
public WebTarget resolveTemplatesFromEncoded(Map<String, Object> paramMap) {
return base.resolveTemplatesFromEncoded(paramMap);
}
}
Following is the code to use it:
EventSource eventSource = new EventSource(new AuthorizationHeaderWebTarget(target, token));
eventSource.register(new EventListener() {
public void onEvent(final InboundEvent inboundEvent) {
//...
}
});
If you use jercy client using header in websource
Client client=Client.create();
WebResource webresource=client.resource(urlLink);
ClientResponse clientResponse=webresource.header("authorization", accessToken)
.accept(MediaType.APPLICATION_JSON).get(ClientResponse.class);

Java CXF WS Client - gZip HTTP REQUEST HEADER being ignored

I am trying to set/request gZip in HTTP REQUEST HEADERS inside my Java CXF WS Client BUT for some reason its being IGNORED. I don't get back gZipped response. Here is how I am trying to set. I am using Apache CXF 2.3.2. What am I missing?
public class LoggerXML implements SOAPHandler<SOAPMessageContext> {
private String uniqueIdentifier;
private String sessionId;
public LoggerXML(String sessionId, String uniqueIdentifier) {
this.sessionId = sessionId;
this.uniqueIdentifier = uniqueIdentifier;
}
protected final void setLogStream(PrintStream ps) {
// out = ps;
}
public void init(Map c) {
uniqueIdentifier = "";
}
public Set<QName> getHeaders() {
return null;
}
public boolean handleMessage(SOAPMessageContext smc) {
Boolean outboundProperty = (Boolean)
smc.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
if(outboundProperty){
// Creating HTTP headers & setting gZip.
Map<String, List<String>> headers = (Map<String,
List<String>>) smc.get(MessageContext.HTTP_REQUEST_HEADERS);
if(headers == null){
//System.out.println("LoggerXML.handleMessage: headers = null");
headers = new HashMap<String, List<String>>();
}
// Add HTTP headers to the web service request
headers.put("Accept-Encoding", Collections.singletonList("gzip,deflate"));
//headers.put("Content-Encoding", Collections.singletonList("gzip"));
//headers.put("Accept-Encoding", Collections.singletonList("gzip"));
smc.put(MessageContext.HTTP_REQUEST_HEADERS, headers);
//smc.put("org.apache.cxf.transport.common.gzip.GZIPOutInterceptor.UseGzip","YES");
}
return true;
}
public boolean handleFault(SOAPMessageContext smc) {
return true;
}
// nothing to clean up
public void close(MessageContext messageContext) {
}
// nothing to clean up
public void destroy() {
}
// Other Methods....
}
This code works for me
// Get the underlying Client object from the proxy object of service interface
Client proxy = ClientProxy.getClient(stub);
// Creating HTTP headers
Map<String, List<String>> headers = new HashMap<String, List<String>>();
headers.put("Accept-Encoding", Arrays.asList("gzip"));
// Add HTTP headers to the web service request
proxy.getRequestContext().put(Message.PROTOCOL_HEADERS, headers);
Refer:http://singztechmusings.wordpress.com/2011/09/17/apache-cxf-how-to-add-custom-http-headers-to-a-web-service-request/

Categories