SparkJava: Get route path from request - java

If I have a route like foo/:id in my SparkJava server, I want to get that route string in my handler. I can get the pathInfo as in the following:
Spark.get("foo/:id", (request, response) -> {
var matchedRoute = request.pathInfo();
System.out.println(matchedRoute);
})
But if I curl localhost:8080/foo/1, then this will print /foo/1, instead of /foo/:id.
Is it possible to get the route that SparkJava matched the request against?

There's no inherent way to do that because the anonymous function doesn't receive the route itself as an argument. But you could add the route to the request in a 'before' part:
// For every route add these two lines:
String API_PATH_1 = "foo/:id";
before(API_PATH_1, (req, res) -> req.attribute("route", API_PATH_1));
get(API_PATH_1, (req, res) -> {
String route = req.attribute("route");
String matchedRoute = req.pathInfo();
System.out.println("Route: " + route);
System.out.println("Matched Route: " + matchedRoute);
});
It adds a little bit of code but if you're consistent with this pattern with all your routes then in the get() part you can always just call req.attribute("route") which will give you what wanted.

Related

WebFlux expand is not retrieving the second request

I'm trying to use Spring's webflux to create an http endpoint to stream github users using Github's api. I tried to do what is described here and here but it seems that the expand is not fetching the second page of results from github's api. What am I doing wrong?
Here's the code I currently have:
#RestController
#RequestMapping("/user")
public class GithubUserController {
private static final String GITHUB_API_URL = "https://api.github.com";
private final WebClient client = WebClient.create(GITHUB_API_URL);
#GetMapping(value = "/search/stream", produces = MediaType.APPLICATION_STREAM_JSON_VALUE)
public Flux<GithubUser> search(
#RequestParam String location,
#RequestParam String language,
#RequestParam String followers) {
return fetchUsers(
uriBuilder ->
uriBuilder
.path("/search/users")
.queryParam(
"q",
String.format(
"location:%s+language:%s+followers:%s", location, language, followers))
.build())
.expand(
response -> {
var links = response.headers().header("link");
Pattern p = Pattern.compile("<(.*)>; rel=\"next\".*");
for (String link : links) {
Matcher m = p.matcher(link);
if (m.matches()) {
return client.get().uri(m.group(1)).exchange();
}
}
return Flux.empty();
})
.flatMap(response -> response.bodyToFlux(GithubUsersResponse.class))
.flatMap(parsedResponse -> Flux.fromIterable(parsedResponse.getItems()))
.log();
}
private Mono<ClientResponse> fetchUsers(Function<UriBuilder, URI> url) {
return client.get().uri(url).exchange();
}
}
I can see that the regex for the second page works because if I add a print inside the if, it gets printed, however if I test this on the browser or on postman I only get the results for the first page of results returned by github's api:
{"login":"chrisbanes","id":"227486"}
{"login":"keyboardsurfer","id":"336005"}
{"login":"lucasr","id":"730395"}
{"login":"hitherejoe","id":"3879281"}
{"login":"StylingAndroid","id":"933874"}
{"login":"rstoyanchev","id":"401908"}
{"login":"RichardWarburton","id":"328174"}
{"login":"slightfoot","id":"906564"}
{"login":"tomwhite","id":"85085"}
{"login":"jstrachan","id":"30140"}
{"login":"wakaleo","id":"55986"}
{"login":"cesarferreira","id":"277426"}
{"login":"kevalpatel2106","id":"20060162"}
{"login":"jodastephen","id":"213212"}
{"login":"caveofprogramming","id":"19751656"}
{"login":"AlmasB","id":"3594742"}
{"login":"scottyab","id":"404105"}
{"login":"makovkastar","id":"1076309"}
{"login":"salaboy","id":"271966"}
{"login":"blundell","id":"655860"}
{"login":"PierfrancescoSoffritti","id":"7457011"}
{"login":"0xddr","id":"4354177"}
{"login":"irsdl","id":"1798313"}
{"login":"andreban","id":"1733592"}
{"login":"TWiStErRob","id":"2906988"}
{"login":"geometer","id":"344328"}
{"login":"neomatrix369","id":"1570917"}
{"login":"nebraslabs","id":"32421477"}
{"login":"lucko","id":"8352868"}
{"login":"isabelcosta","id":"11148726"}
The link header in the Github API provides the URI in an escaped format. The String you pass to client.get().uri() should be unescaped - so it escapes the escaped string, and you end up with a URL that returns nothing.
Instead, you probably want to use something similar to:
if (m.matches()) {
return client.get().uri(URI.create(m.group(1))).exchange();
}
Side note - your regular expression will probably want to account for any number of characters before the "next" link as well otherwise you'll be unable to go past the second page, so you probably want to prepend .* to that:
Pattern p = Pattern.compile(".*<(.*)>; rel=\"next\".*");
Second side note - Github's API is rate limited (heavily rate limited if you're unauthenticated), so you may well run into those rate limits. You'll probably want to handle that situation elegantly somehow, but that's a reasonably big topic that's beyond the scope of this question.

Is there any way to return Flux<String> from spring REST end point to the web browser / front end application(i.e Angular 6)?

I want to return Flux to the browser but when i hit the end point it gives me "406 not acceptable" error.
This is for a Apache tomcat server,running spring-boot 5, JAVA 8 .In STS(Spring Tool Suite) IDE.
#RestController
public class CloudFoundry {
#GetMapping(value = "/LogApplication", produces =
MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> logApplication() throws Throwable {
return Flux.just("a", "b", "c", "d");
}
}
when i hit the end point on localhost it should give me stream of string but instead it's giving me "406 not acceptable" error.
MediaType.TEXT_EVENT_STREAM_VALUE is used for Server-Sent-Events that need to be consumed appropriately.
This is what you need to have on front-end side:
// Declare an EventSource
const eventSource = new EventSource('http://server.url/LogApplication');
// Handler for events without an event type specified
eventSource.onmessage = (e) => {
// Do something - event data etc will be in e.data
};
// Handler for events of type 'eventType' only eventSource.addEventListener('eventType', (e) => {
// Do something - event data will be in e.data,
// message will be of type 'eventType'
});
You can find a good explanation of Server-Sent-Events in the following blog post:
A Look at Server-Sent Events

Camel - how to add request parameter(throwExceptionOnFailure) to url?

I hav following route:
from("quartz2:findAll//myGroup/myTimerName?cron=" + pushProperties.getQuartz())
//.setBody().constant("{ \"id\": \"FBJDBFJHSDBFJSBDfi\" }")
.to("mongodb:mongoBean?database=" + mongoDataConfiguration.getDatabase()
+ "&operation=findAll&collection=" + mongoDataConfiguration.getDataPointCollection())
.process(exchange -> {
exchange.getIn().setBody(objectMapper.writeValueAsString(exchange.getIn().getBody()));
}).streamCaching()
.setHeader(Exchange.HTTP_METHOD, constant(pushProperties.getHttpMethod()))
.setHeader(Exchange.CONTENT_TYPE, constant(MediaType.APPLICATION_JSON_VALUE))
.to(pushProperties.getUrl() + "&throwExceptionOnFailure=false").streamCaching()
As you can see I use throwExceptionOnFailure=false
and I take my url from configuration. But we found out that it works if
pushProperties.getUrl() = localhost:8080/url?action=myaction
and doesn't work in case of
pushProperties.getUrl() = localhost:8080/url
Is there universla way in camel to add request parameter to URL?
something like:
private String buildUrl() {
String url = pushProperties.getUrl();
return url + (url.contains("?") ? "&" : "?") + "throwExceptionOnFailure=false";
}
inside Camel api
That is because in case of localhost:8080/url, after appending it becomes like this
localhost:8080/url&throwExceptionOnFailure=false
which is wrong
It should be
localhost:8080/url?throwExceptionOnFailure=false,
In the first case it works you already have a requestpatam(?action=myaction) so the next one can be added with ampersand(&)
I think you have to add your own logic to compose the endpoint to the http component at the runtime. This is because the CamelContext will process it during the route itself. The parameter throwExceptionOnFailure is a property from the http component.
I don't think that adding the parameter via .setHeader(Exchange.HTTP_QUERY, constant("throwExceptionOnFailure=false")) shoud work because these parameters will be evaluated after the http component get processed, e.g. into the URL destination. Please, take a look at "How to use a dynamic URI in to()":
.toD(pushProperties.getUrl() + "&throwExceptionOnFailure=false")
You could use the simple expression to write a logic to do what you want based on the result of pushProperties.getUrl().
I don't like how Camel configure HTTP component in this case, but this is what it is.
What I suggest is to create a map of config, and append your args to it, and do a manual join with "&", then append it to the main url.
I do it like:
public class MyProcessor {
/**
* Part of Camel HTTP component config are done with URL query parameters.
*/
private static final Map<String, String> COMMON_QUERY_PARAMS = Map.of(
// do not throw HttpOperationFailedException; we handle them ourselves
"throwExceptionOnFailure", "false"
);
#Handler
void configure(Exchange exchange, ...) {
...
Map<String, String> queryParams = new HashMap<>();
queryParams.put("foo", "bar");
message.setHeader(Exchange.HTTP_QUERY, mergeAndJoin(queryParams));
...
}
private String mergeAndJoin(Map<String, String> queryParams) {
// make sure HTTP config params put after event params
return Stream.concat(queryParams.entrySet().stream(), COMMON_QUERY_PARAMS.entrySet().stream())
.map(entry -> entry.getKey() + "=" + entry.getValue())
.collect(Collectors.joining("&"));
}
}
Note that toD needs optimization but in that case, HTTP_QUERY cannot be used.
When the optimised component is in use, then you cannot use the headers Exchange.HTTP_PATH and Exchange.HTTP_QUERY to provide dynamic values to override the uri in toD. If you want to use these headers, then use the plain to DSL instead. In other words these headers are used internally by toD to carry the dynamic details of the endpoint.
https://camel.apache.org/components/3.20.x/eips/toD-eip.html

Java Spark Framework - request body lost after middleware

I am using Spark framework for my application.
I have a middleware which checks (among other things) if the body is of JSON format:
// Middleware
before((req, res) -> {
// Method check
if (!req.requestMethod().equals("POST")) {
halt(403, "{\"result\":\"ERR\",\"errMsg\":\"Only POST allowed!\",\"code\":403}");
}
// JSON Check
JSONObject body_json = new JSONObject();
try {
body_json = new JSONObject(req.body());
} catch (JSONException e) {
halt(403, "{\"result\":\"ERR\",\"errMsg\":\"No valid JSON!\",\"code\":403}");
}
// At this point (end of middleware) the request body is still unchanged !
});
Then I have my normal function for processing POST requests:
post("/post_some_data", (req, res) -> {
String body = req.body() // This string is empty !!
int length = req.contentLength(); // This remain unchanged
});
But the request body suddenly becomes empty (other attributes and headers remain unchanged).
Is that a bug or am I doing something wrong ??
There was a bug in the spark framework. Updating the library to the 2.1 version will solve this and all of the similar problems.

How to get SoapUI request and response XML in java

I'm using the SoapUI API as part of an existing java project.
The application should save the request and response XML in an specific report file.
I wonder if it's possible to get those requests and responses via the API.
The method invoking the TestCaseRunner looks like this
protected void checkTestCase(TestCase testCase) {
TestCaseRunner tr = testCase.run(null, false);
for (TestStepResult tcr : tr.getResults()) {
String status = tcr.getStatus();
String time = tcr.getTimeTaken() + "ms";
/* How to get XML messages?
* String request =
* String response =
*/
}
}
Depending on exactly what kind of test steps you have they might be an instance of a MessageExchange. Casting the TestStepResult to a MessageExchange and calling getRequestContent / getResponseContent might do the trick.
String request = ((MessageExchange)tcr).getRequestContent();
String response = ((MessageExchange)tcr).getResponseContent();
I have used the following way to get the response from the API CAll performed:
runner = testRunner.runTestStepByName("Your Test Case name");
// Here we take the response in ms of the API call
timeTaken = runner.response.timeTaken;
// here we get the HTTP response code.
responseCode = runner.getResponseHeaders()."#status#";
// here we get the response content
String response = runner.getResponseContent();
// here we get the API call endpoint -> in case you need to print it out.
String endPoint = runner.getEndpoint();

Categories