I was wondering if there was a way to delegate to another route in RestEasy.
I.e., something along the lines of, begin in a method inside an RS:
#Path("/api")
public class Foo {
#POST
#Path("/foo")
public Response foo() {
return RestEasy.delegate("GET", "/api/bar");
}
}
Where delegate would return the exact same response as if I had made an HTTP GET request to api/bar, that is, will fall through the proper RS that handles that route, ideally refilling all the necessary request information (headers, params, payload).
I don't want an HTTP redirect as I want it to be transparent to the api user.
I see from the docs/source that org.jboss.resteasy.spi.HttpRequest interface you have access to has a forward method.
It takes a string which would be the path to your other endpoint, but it doesn't let you change the method type (post to a get). But then again neither does the RequestDispatcher forward method you have access to. You aren't allowed to modify the request or the response.
See here:
So really all you can do is directly call your service method or, use an HTTP client to call other REST endpoint inside foo and then stream that back to the client.
Related
I am working on a Jersey service client for one of my services and am having trouble determining the best way to pass a null entity through the client's put. On the service side of things this is my endpoint:
#PUT
#Path("/rule/disable/key/{key}")
#Produces(MediaType.APPLICATION_JSON)
public Response disableRuleByKey(#PathParam("key") String key)
throws Exception {
try {
DAL.getWriter().disableRuleByKey(key);
return Response.ok().build();
} catch (BlahException bla) {
throw de;
}
Basically all the method does in the backend is flip a toggle for other parts of the application to use. I'm not sure if put is the correct call to use here (but this was written by a teammate). I know it doesn't even have a JSON payload.
Anyways, on the client side I have this generic putItem() code for all of my clients to use via extends:
public static <T> boolean putItem(Client client, String uri, T item)
throws InterruptedException,
ExecutionException {
Invocation putConfig = client.target(uri).request()
.buildPut(Entity.entity(item, MediaType.APPLICATION_JSON));
Future<Response> asyncResponse = putConfig.submit();
Response response = asyncResponse.get();
return response.getStatus() == Status.OK.getStatusCode();
}
This PUTs into the database fine with a JSON payload, but since the method above doesn't specifically have a payload I was wondering what the best course of action would be. Would modifying the Invocation's .buildPut() to have null in it be okay since I am not passing in a payload.
I am open to modifying the endpoint too but this is what I currently have and can't figure out the best way to send this value to the backend. Should I just modify the endpoint to consume a JSON object rather than passing the key as a #PathParam?
When replacing the state of a resource with a PUT request, you should send the new representation in the request payload.
Have a look the the RFC 7231, the current reference for semantics and content in HTTP/1.1:
4.3.4. PUT
The PUT method requests that the state of the target resource be created or replaced with the state defined by the representation enclosed in the request message payload. [...]
Say I have a Dropwizard/Jersey resource defined like so:
// Pseudo-code
#Path("/fizz")
#Produces(MediaType.APPLICATION_JSON)
class FizzResource {
FizzDao fizzDao
#GET
List<Fizz> getFizzesByType(#PathParam("type") String type) {
// Do some stuff up here
return fizzDao.getFizzesByType(type)
}
#POST
Widget determineWidgetByFoobaz(#PathParam("foobaz") String foobaz) {
// Do some stuff
List<Fizz> fizzes = getFizzesByType(foobaz.type)
Widget w = new Widget(fizzes, true, blah)
// Do some more stuff
return w
}
}
What happens when I call one endpoint (getFizzesByType) from inside another endpoint (determineWidgetByFoobaz)?
Does the framework know to just make a Java method call? Or is an actual network call (to localhost/loopback/etc.) made? If a network call is made, does the framework provide any way to configure it so that just a local Java method invocation is called instead?
If you access the endpoint as a method (i.e. this.getFizzesByType(type)) then it will be called like any other Java method. If you access it via a URI (e.g. ClientBuilder.newClient().target("http://localhost/fizz/" + type).request().get()) then it will be accessed as a network resource.
The getFizzesByType call inside determineWidgetByFoobaz is just another local method call. There's nothing special in those methods, and you can also call them safely in, let's say, a unit test.
What Jersey does on it's bootstrapping process is to scan for classes annotated with #Path and then bind each method annotated with an HTTP method to it's endpoint (if any). That way, when someone fires a GET to /fizz, in a nutshell Jersey gets a FizzResource instance, call it's getFizzesByType method, serializes the returned object to JSON, creates the appropriate HTTP response and sends it back to the client.
I'm trying to get access to the HTTP headers that are injected by Rest Assured. Spring's Mock MVC gives you access to pretty much everything via the MvcResult, and you can use this result to log pretty much anything you would like about the request and response. The only way I can see how to do this is in RestAssured is with a Filter. However, it gives you limited access to the request (you just get the RequestSpecification). I understand that it might be tricky to get access to headers that are added by the HttpClient, but it doesn't look like you can even get access to headers that are added by Rest Assured itself. For example, I can't see any OAuth related headers, nor content-type or content-length. The only headers that appear are those that were manually added using, for example, .contentType(ContentType.XML)
Is there any other way to get access to those headers? I don't need to modify the request, I just want to be able to log all of it and the headers that are injected by Rest Assured.
I found that it's possible to register your own HttpClientFactory with RestAssured:
RestAssured.config().httpClient(
HttpClientConfig.httpClientConfig().httpClientFactory(
new CustomHttpClientFactory())
So I created a new factory that returns an HTTP client into which I inject some request and response interceptors.
public class CustomHttpClientFactory extends HttpClientConfig.HttpClientFactory {
#Override
public HttpClient createHttpClient() {
DefaultHttpClient client = new DefaultHttpClient();
client.addRequestInterceptor((request, ctx) -> {
// do what you will
});
client.addResponseInterceptor((response, ctx) -> {
// do what you will
});
return client;
}
}
This gives you almost full access to manipulate the request and response. One thing to remember is that if you're going to read from the response's entity, you should first wrap it in a BufferedHttpEntity to make it re-readable:
if (response.getEntity() != null && !response.getEntity().isRepeatable()) {
response.setEntity(new BufferedHttpEntity(response.getEntity()));
}
Another problem I ran into is when trying to see the OAuth related information. When using RestAssured's OAuth functionality, it adds its own OAuthSigner interceptor to the HTTP client right before executing the request. This means that it will always be the last interceptor to be called and any interceptor you may have already injected will be called before the request ever gets signed. Because I don't really need to see the signature for now, I didn't investigate this further and I'm leaving it as an exercise for the reader. ;)
I have a POJO resource which defines some HTTP endpoints and returns Dropwizard Views. Some of these endpoints simply perform an action (eg. update the db) and then forward the user to another endpoint. For example, the user is at the location GET /foo and submits a form. This directs them to the endpoint POST /foo/submit, their submission is processed, and then forwards them to GET /foo/done. This prevents resubmission of the form if they refresh the page, for example. This forwarding is currently accomplished with Jersey's Response.seeOther() method (returning a Response instead of a View).
What I would like to be able to do is, when the handling method handles their submission, generate some sort of message (error message, warning, successful, etc) and pass that message to the page we forward to. For example, at GET /foo/done, I would like it to say at the top of the page, "Submission complete!" or "Submission failed because...".
I've done some searching around and a lot of people are suggesting to throw a WebApplicationException - except not all of my cases are errors. Sometimes I'd like to just show a confirmation of a successful action. But I can't figure out how to get the receiving method to receive a message. I've done it before in Python by having the handling method accept an optional dictionary but unfortunately I'm on Java 7 so I don't have the ability to give methods optional parameters with default values.
Thanks for your help.
Redirects will simply send GET requests. GET request should not have any body. To send arbitrary data with GET requests, simply send it in the query string (of course there should be no confidential information here). For example
#Path("foo")
public class FooResource {
#GET
#Path("done")
public String getDone(#QueryParam("message") String message) {
return message;
}
#POST
#Path("submit")
public Response postData() {
String message = UriComponent.encode(
"You have perefected submitting!",
UriComponent.Type.QUERY_PARAM_SPACE_ENCODED);
return Response.seeOther(URI.create("/api/foo/done?message=" + message)).build();
}
}
I am trying to implement a web service that proxies another service that I want to hide from external users of the API. Basically I want to play the middle man to have ability to add functionality to the hidden api which is solr.
I have to following code:
#POST
#Path("/update/{collection}")
public Response update(#PathParam("collection") String collection,
#Context Request request) {
//extract URL params
//update URL to target internal web service
//put body from incoming request to outgoing request
//send request and relay response back to original requestor
}
I know that I need to rewrite the URL to point to the internally available service adding the parameters coming from either the URL or the body.
This is where I am confused how can I access the original request body and pass it to the internal web service without having to unmarshall the content? Request object does not seem to give me the methods to performs those actions.
I am looking for Objects I should be using with potential methods that would help me. I would also like to get some documentation if someone knows any I have not really found anything targeting similar or portable behaviour.
Per section 4.2.4 of the JSR-311 spec, all JAX-RS implementations must provide access to the request body as byte[], String, or InputStream.
You can use UriInfo to get information on the query parameters. It would look something like this:
#POST
#Path("/update/{collection}")
public Response update(#PathParam("collection") String collection, #Context UriInfo info, InputStream inputStream)
{
String fullPath = info.getAbsolutePath().toASCIIString();
System.out.println("full request path: " + fullPath);
// query params are also available from a map. query params can be repeated,
// so the Map values are actually Lists. getFirst is a convenience method
// to get the value of the first occurrence of a given query param
String foo = info.getQueryParameters().getFirst("bar");
// do the rewrite...
String newURL = SomeOtherClass.rewrite(fullPath);
// the InputStream will have the body of the request. use your favorite
// HTTP client to make the request to Solr.
String solrResponse = SomeHttpLibrary.post(newURL, inputStream);
// send the response back to the client
return Response.ok(solrResponse).build();
One other thought. It looks like you're simply rewriting the requests and passing through to Solr. There are a few others ways that you could do this.
If you happen to have a web server in front of your Java app server or Servlet container, you could potentially accomplish your task without writing any Java code. Unless the rewrite conditions were extremely complex, my personal preference would be to try doing this with Apache mod_proxy and mod_rewrite.
There are also libraries for Java available that will rewrite URLs after they hit the app server but before they reach your code. For instance, https://code.google.com/p/urlrewritefilter/. With something like that, you'd only need to write a very simple method that invoked Solr because the URL would be rewritten before it hits your REST resource. For the record, I haven't actually tried using that particular library with Jersey.
1/ for the question of the gateway taht will hide the database or index, I would rather use and endpoint that is configured with #Path({regex}) (instead of rebuilding a regexp analyser in your endpoint) .
Use this regex directly in the #path, this is a good practice.
Please take a look at another post that is close to this : #Path and regular expression (Jersey/REST)
for exemple you can have regexp like this one :
#Path("/user/{name : [a-zA-Z][a-zA-Z_0-9]}")
2/ Second point in order to process all the request from one endpoint, you will need to have a dynamic parameter. I would use a MultivaluedMap that gives you the possibility to add params to the request without modifying your endpoint :
#POST
#Path("/search")
#Consumes(MediaType.APPLICATION_FORM_URLENCODED)
#Produces({"application/json"})
public Response search( MultivaluedMap<String, String> params ) {
// perform search operations
return search( params);
}
3/ My 3rd advice is Reuse : make economy and economy make fewer bugs.
it's such a pitty to rewrite a rest api in order to perform solr search. You can hide the params and the endpoint, but could be great to keep the solr uri Rest formatting of the params in order to reuse all the search logic of solr directly in your api. This will make you perform a great economy in code even if you hide your solr instance behind you REST GATEWAY SERVER.
in this case you can imagine :
1. receive a query in search gateway endpoint
2. Transform the query to add your params, controls...
3. execute the REST query on solr (behind your gateway).