How to removing response header with a RestFilter - rest

Is it possible to remove response headers with a RestFilter? Looking at this cookbook you would say it should be possible. However, the filter is only called when the request is incoming, before the call to the resource class. I was expecting to have a hook where I can modify the response headers before sending it back to the client.
i had a look at CORSFilter as an example, but it only sets headers, not remove them.
To be more specific, I want to remove the WWW-Authenticate header that is set by the Auth provider when the session has expired. This header causes a popup in the browser (chrome) that is undesirable.

what you need is a javax.ws.rs.container.ContainerRequestFilter. In jax-rs such filters can be registered in a javax.ws.rs.core.Application. The application used in ICM is com.intershop.component.rest.internal.application.DefaultRestApplication which can be adapted using an com.intershop.component.rest.internal.application.ApplicationClassesProvider that can be registered using a Set-Binding.
So you could create a Guice-Module and your filter:
public class MyRestModule extends AbstractModule
{
#Override
protected void configure()
{
Multibinder<ApplicationClassesProvider> binder = Multibinder.newSetBinder(binder(),
ApplicationClassesProvider.class);
binder.addBinding().toInstance(c->c.accept(MyResponseFilter.class));
}
}
public class MyResponseFilter extends ContainerRequestFilter
{
#Override
public void filter(ContainerRequestContext request, ContainerResponseContext response)
{
response.getHeaders().remove("WWW-Authenticate");
}
}
Please note that this filter will be applied to all requests, so please make sure you remove headers only for requests you really care about.

Related

Dumping bad requests

I have a service implemented with Dropwizard and I need to dump incorrect requests somewhere.
I saw that there is a possibility to customise the error message by registering ExceptionMapper<JerseyViolationException>. But I need to have the complete request (headers, body) and not only ConstraintViolations.
You can inject ContainerRequest into the ExceptionMapper. You need to inject it as a javax.inject.Provider though, so that you can lazily retrieve it. Otherwise you will run into scoping problems.
#Provider
public class Mapper implements ExceptionMapper<ConstraintViolationException> {
#Inject
private javax.inject.Provider<ContainerRequest> requestProvider;
#Override
public Response toResponse(ConstraintViolationException ex) {
ContainerRequest request = requestProvider.get();
}
}
(This also works with constructor argument injection instead of field injection.)
In the ContainerRequest, you can get headers with getHeaderString() or getHeaders(). If you want to get the body, you need to do a little hack because the entity stream is already read by Jersey by the time the mapper is reached. So we need to implement a ContainerRequestFilter to buffer the entity.
public class EntityBufferingFilter implements ContainerRequestFilter {
#Override
public void filter(ContainerRequestContext containerRequestContext) throws IOException {
ContainerRequest request = (ContainerRequest) containerRequestContext;
request.bufferEntity();
}
}
You might not want this filter to be called for all requests (for performance reasons), so you might want to use a DynamicFeature to register the filter just on methods that use bean validation (or use Name Binding).
Once you have this filter registered, you can read the body using ContainerRequest#readEntity(Class). You use this method just like you would on the client side with Response#readEntity(). So for the class, if you want to keep it generic, you can use String.class or InputStream.class and convert the InputStream to a String.
ContainerRequest request = requestProvider.get();
String body = request.readEntity(String.class);

WireMock does not transform response

I'm having an issue using WireMock, I've extended ResponseTransformer and implemented all required methods, it looks like this:
public class TempResponseTransformer extends ResponseTransformer {
#Override
public Response transform(Request request, Response response, FileSource fileSource,
Parameters parameters) {
return Response.Builder.like(response).but().status(404).body("1").build();
}
#Override
public boolean applyGlobally() {
return false;
}
#Override
public String getName() {
return "temp-response-transformer";
}
}
Now I want to apply this particular transformer to one of the stubs, that I wrote, stub looks like this:
private static void initTempStub() {
stubFor(post(urlPathEqualTo("/api/v1/temp"))
.withHeader("AccessToken", matching("[a-zA-Z0-9]+"))
.withHeader("CliendID", matching("[a-zA-Z0-9]+"))
.withHeader("ClientSecret", matching("[a-zA-Z0-9]+"))
.willReturn(aResponse()
.withTransformers("temp-response-transformer")));
}
When I start service and perform post calls I do see that transformer is applied, however responses actually not being transformed.
I've tried to apply transformer in config section when starting service as well, but it doesn't help.
So my question is how should I apply ReponseTransformer correctly so it would transform my responses?
Ok, think I've figured it out.
Here is an issue(?) with WireMock that I was able to figure out while looking at the internals, here is how I passed config:
new WireMockServer(wireMockConfig()
.extensions(TempResponseTransformer.class)
.options().notifier(new ConsoleNotifier(true)))
And an issue with this code is when you call options() from wireMockConfig it would create new instance of WireMockConfig, so I had to extract this config into separate piece of code like this:
var wireMockConfig = new WireMockConfiguration();
wireMockConfig
.extensions(SamplesResponseTransformer.class)
.notifier(new ConsoleNotifier(true));

JAX-RS AsyncResponse.resume() with Location header

UPDATE
Some more digging showed that thrown Exceptions were dropped and the actual problem is that an injected UriInfo could not be resolved in the AsyncResponse's thread!
Accessing #Context UriInfo uriInfo; during AsyncResponse.resume() gives the following LoggableFailure's message:
Unable to find contextual data of type: javax.ws.rs.core.UriInfo
ORIGINAL
According to RFC 7231 HTTP/1.1 Semantics and Control, a POSTshould return 201 CREATED and supply the new resource's location in the header:
the origin server
SHOULD send a 201 (Created) response containing a Location header
field that provides an identifier for the primary resource created
(Section 7.1.2) and a representation that describes the status of the
request while referring to the new resource(s).
When writing a synchronous REST Server, the javax.ws.rs.core.Responseoffers the Response.created() shorthand which does exactly that.
I would save the new entity, build an URI and return
return Response.created(createURL(created)).build();
However, when I switch to an asynchronous approach utilizing a
#Suspended javax.ws.rs.container.AsyncResponse
the HTTP request on the client will hang infinitely:
#POST
public void createUser(#Valid User user, #Suspended AsyncResponse asyncResponse) {
executorService.submit(() -> {
User created = userService.create(user);
asyncResponse.resume(
Response.created(createURL(created)).build()
);
});
}
Through trial-and-error I found out that the modified location header is responsible.
If I return my entity and set the 201 Created, without touching the header, the request will eventually resolve:
#POST
public void createUser(#Valid User user, #Suspended AsyncResponse asyncResponse) {
executorService.submit(() -> {
User created = userService.create(user);
asyncResponse.resume(
Response.status(Status.CREATED).entity(created).build() //this works
//Response.created(createURL(created)).build()
);
});
}
So what's the problem? Am I misunderstanding the concepts?
I am running RestEasy on GlassFish4.1
If you need more information, please comment!
edit
As soon as I change any link or the header, the request will hang.
In case anyone ever has the same problem:
The problem was that I created the location header through an injected #Context UriInfo uriInfo using its .getAbsolutePathBuilder().
The approach was working in a synchronous server because the thread which accessed the UriInfo still had the same Request context.
However, when I switched to an async approach, the underlying Runnable which eventually had to access uriInfo.getAbsolutePathBuilder() was NOT within any context - thus throwing an exception which halted further execution.
The workaround:
In any async method which should return a location header, I .getAbsolutePathBuilder() while still within the context. The UriBuilder implemantion can then be used within the async run:
#POST
public void createUser(#Valid User user, #Suspended AsyncResponse asyncResponse) {
UriBuilder ub = uriInfo.getAbsolutePathBuilder();
executorService.submit(() -> {
User created = userService.create(user);
asyncResponse.resume(
Response.created(createURL(ub, created)).build()
);
});
}
private URI createURL(UriBuilder builder, ApiRepresentation entity) {
return builder.path(entity.getId().toString()).build();
}

Is it possible to place variables into a resource path within a sling servlet?

We are trying to provide a clean URI structure for external endpoints to pull json information from CQ5.
For example, if you want to fetch information about a particular users history (assuming you have permissions etc), ideally we would like the endpoint to be able to do the following:
/bin/api/user/abc123/phone/555-klondike-5/history.json
In the URI, we would specifying /bin/api/user/{username}/phone/{phoneNumber}/history.json so that it is very easy to leverage the dispatcher to invalidate caching changes etc without invalidating a broad swath of cached information.
We would like to use a sling servlet to handle the request, however, I am not aware as to how to put variables into the path.
It would be great if there were something like #PathParam from JaxRS to add to the sling path variable, but I suspect it's not available.
The other approach we had in mind was to use a selector to recognise when we are accessing the api, and thus could return whatever we wanted to from the path, but it would necessitate a singular sling servlet to handle all of the requests, and so I am not happy about the approach as it glues a lot of unrelated code together.
Any help with this would be appreciated.
UPDATE:
If we were to use a OptingServlet, then put some logic inside the accepts function, we could stack a series of sling servlets on and make the acceptance decisions from the path with a regex.
Then during execution, the path itself can be parsed for the variables.
If the data that you provide comes from the JCR repository, the best is to structure it exactly as you want the URLs to be, that's the recommended way of doing things with Sling.
If the data is external you can create a custom Sling ResourceProvider that you mount on the /bin/api/user path and acquires or generates the corresponding data based on the rest of the path.
The Sling test suite's PlanetsResourceProvider is a simple example of that, see http://svn.apache.org/repos/asf/sling/trunk/launchpad/test-services/src/main/java/org/apache/sling/launchpad/testservices/resourceprovider/
The Sling resources docs at https://sling.apache.org/documentation/the-sling-engine/resources.html document the general resource resolution mechanism.
It is now possible to integrate jersy(JAX-RS) with CQ. We are able to create primitive prototype to say "Hello" to the world.
https://github.com/hstaudacher/osgi-jax-rs-connector
With this we can use the #PathParam to map the requests
Thanks and Regards,
San
There is no direct way to create such dynamic paths. You could register servlet under /bin/api/user.json and provide the rest of the path as a suffix:
/bin/api/user.json/abc123/phone/555-klondike-5/history
^ ^
| |
servlet path suffix starts here
then you could parse the suffix manually:
#SlingServlet(paths = "/bin/api/user", extensions = "json")
public class UserServlet extends SlingSafeMethodsServlet {
public void doGet(SlingHttpServletRequest request, SlingHttpServletResponse response) {
String suffix = request.getRequestPathInfo().getSuffix();
String[] split = StringUtils.split(suffix, '/');
// parse split path and check if the path is valid
// if path is not valid, send 404:
// response.sendError(HttpURLConnection.HTTP_NOT_FOUND);
}
}
The RESTful way to approach this would be to have the information stored in the structure that you want to use. i.e. /content/user/abc123/phone/555-klondike-5/history/ would contain all the history nodes for that path.
In that usage. you can obtain an out of the box json response by simply calling
/content/user/abc123/phone/555-klondike-5/history.json
Or if you need something in a specific json format you could use the sling resource resolution to use a custom json response.
Excited to share this! I've worked ~ a week solving this, finally have the best Answer.
First: Try to use Jersey
The osgi-jax-rs-connector suggested by kallada is best, but I couldn't get it working on Sling 8. I lost a full day trying, all I have to show for it are spooky class not found errors and dependency issues.
Solution: The ResourceProvider
Bertrand's link is for Sling 9 only, which isn't released. So here's how you do it in Sling 8 and older!
Two Files:
ResourceProvider
Servlet
The ResourceProvider
The purpose of this is only to listen to all requests at /service and then produce a "Resource" at that virtual path, which doesn't actually exist in the JCR.
#Component
#Service(value=ResourceProvider.class)
#Properties({
#Property(name = ResourceProvider.ROOTS, value = "service/image"),
#Property(name = ResourceProvider.OWNS_ROOTS, value = "true")
})
public class ImageResourceProvider implements ResourceProvider {
#Override
public Resource getResource(ResourceResolver resourceResolver, String path) {
AbstractResource abstractResource;
abstractResource = new AbstractResource() {
#Override
public String getResourceType() {
return TypeServlet.RESOURCE_TYPE;
}
#Override
public String getResourceSuperType() {
return null;
}
#Override
public String getPath() {
return path;
}
#Override
public ResourceResolver getResourceResolver() {
return resourceResolver;
}
#Override
public ResourceMetadata getResourceMetadata() {
return new ResourceMetadata();
}
};
return abstractResource;
}
#Override
public Resource getResource(ResourceResolver resourceResolver, HttpServletRequest httpServletRequest, String path) {
return getResource(resourceResolver , path);
}
#Override
public Iterator<Resource> listChildren(Resource resource) {
return null;
}
}
The Servlet
Now you just write a servlet which handles any of the resources coming from that path - but this is accomplished by handling any resources with the resource type which is produced by the ResourceProvider listening at that path.
#SlingServlet(
resourceTypes = TypeServlet.RESOURCE_TYPE,
methods = {"GET" , "POST"})
public class TypeServlet extends SlingAllMethodsServlet {
static final String RESOURCE_TYPE = "mycompany/components/service/myservice";
#Override
protected void doGet(SlingHttpServletRequest request, SlingHttpServletResponse response) throws ServletException, IOException {
final String [] pathParts = request.getResource().getPath().split("/");
final String id = pathParts[pathParts.length-1];
response.setContentType("text/html");
PrintWriter out = response.getWriter();
try {
out.print("<html><body>Hello, received this id: " + id + "</body></html>");
} finally {
out.close();
}
}
}
Obviously your servlet would do something much more clever, such as process the "path" String more intelligently and probably produce JSON.

extracting the complete envelope xml from MessageContext

I have an interceptor like this:
public class WebServiceInterceptor extends EndpointInterceptorAdapter {
#Inject
private Jaxb2Marshaller myJaxb2Marshaller;
#Inject
private WebServiceHistoryDao webServiceHistoryDao;
#Override
public boolean handleRequest(MessageContext messageContext, Object endpoint)
throws Exception {
Source payloadSource = messageContext.getRequest().getPayloadSource();
Object unmarshaled = myJaxb2Marshaller.unmarshal(payloadSource);
//EXTRACT XML HERE
//is there a better way than this:
String extractedXml = myJaxb2Marshaller.marshal(unmarshaled);
return true;
}
}
How can i extract the whole xml of envelope (for logging purposes - to write it to the DB)
You don't need to write one, there's an existing one in the API - SoapEnvelopeLoggingInterceptor. See the javadoc.
SOAP-specific EndpointInterceptor that logs the complete request and response envelope of SoapMessage messages. By default, request, response and fault messages are logged, but this behaviour can be changed using the logRequest, logResponse, logFault properties.
If you only need to see the payload, rather than the entire SOAP envelope, then there's PayloadLoggingInterceptor.