We have a proxy API set up where we accept any path after our proxy (i.e. /proxy/some/path) and then everything after the proxy path should be forwarded on to another API using RESTeasy (so it would call /some/path in my example).
This is necessary for us for several reasons including avoiding CORS issues and it works most of the time for what we need. However, some routes require query parameters some or all of the time, and because we are handling all routes the same, this causes a RESTeasy error today since the URL gets URL encoded to something like /some/path%3ffilter=value.
Our endpoint today looks like:
#Path("proxy/{someUri : .*}")
#GET
public Response proxyGet(
#PathParam("someUri") String someUri,
#HeaderParam(COOKIE) String cookieHeader,
#Context HttpServletRequest request
) {
return prepareProxyResponse(someUri, cookieHeader, request, null);
}
Our RESTeasy interface looks like:
#GET
#Path("/{requestPath}")
Response proxyGet(
#PathParam("requestPath") String requestPath,
#HeaderParam(COOKIE) String sessionCookie,
#HeaderParam(CONTENT_TYPE) String contentType
);
The code that calls this looks like:
return client.proxyGet(fullPath, cookieHeader, contentTypeHeader);
Prior to understanding this issue, we just took the request URI and added the query string to it so the requestPath above looked like some/path?filter=value which gets mangled. Removing the query string avoids that, however, it also means we cannot pass any query params through.
Is there a way to accept any query params and submit them to the other API using RESTeasy? I know we can break these out where needed to manually specify query params with #QueryParam but since there are many (and more getting added regularly) this will be a lot of effort. I think accomplishing what we are trying to do is not possible based on my research in other questions and the docs, but wanted to double-check before we embarked on a long process to convert. Thanks!
Related
First of all, give thanks for reading my question and try to help me and apologize for my English.
I'm new with Spring and I have this message:
A servlet request to the URI
http://localhost:8080/backend/v1/streetviewer/search-street?url=backend2?busqueda=name%20street&idioma=es-es%26cantidad=10
contains form parameters in the request body but the request body has been consumed by the servlet or a servlet filter accessing the request parameters. Only resource methods using #FormParam will work as expected. Resource methods consuming the request body by other means will not work as expected.
My backend send a request to backend2 with one parameter (url), but that url contains 3 parameters. I understand that is reason why say that.
But I was reading that #FormParam is used for POST requests and I'm using #QueryParam.
#GET
#Path(ApiPath.PATH_BACKEND2)
public String getDataFromProdServer(#QueryParam(ApiParam.PARAM_URL) final String externalUrl ) {
return mapService.ServerRequest(externalUrl);
}
How can solve it??
To be said you are actually using JAX-RS Implementation, from backend2 I asume it is a separate service so I suggest you to use Spring Implementations for consuming the API. #RequestMapping/#GetMapping and so..
Coming to the question (With Spring Implementation)
#GetMapping(ApiPath.PATH_BACKEND2)
public String getDataFromProdServer(#RequestParam(ApiParam.PARAM_UR) final String externalUrl) {
return mapService.serverRequest(externalUrl);
}
I've been using a generic map for query parameters in a spring-boot (4.3.3.RELEASE) application, which has been live and taking traffic for a while now. This application has dozens of different endpoints and recently I've seen that a number requests result in errors due to improperly formatted query parameters.
I've been seeing a significant number of the requests are coming through like http://web.com/url/path?param1=1?param2=2. Unfortunately, I don't control or know why these urls are showing up, and they are all resulting in error pages for the users using them.
Can spring be customized to parse maps of query parameters using both '&' and '?' as separators?
I suspect this can be supported with a custom interceptor to inspect modify the incoming urls, but I wanted to know if there is an easier solution.
This is an example the one endpoint
#RequestMapping(value = "/url/path", method = {GET, POST})
public ResponseEntity<Void> handleRequest(
#RequestHeader HttpHeaders requestHeaders,
#RequestParam MultiValueMap<String, String> requestParams) throws Exception
{
....
}
I am currently working on a monitoring application using Spring Cloud Sleuth. Currently I try to collect as much information about my requests as possible.
To keep the approach as scalable as possible I use GenericFilterBeans and HandlerInterceptorAdapter to access information from the requests sent to the REST-API.
I am struggling with getting parameters of a REST-call where the parameters are mapped from the URL like in the following code snippet:
#RequestMapping(
value = {"/{service}/{route_id}/book", "/accounting-core-service/{service}/{route_id}/book"},
method = RequestMethod.GET)
#ResponseBody
public ModelAndView book(#PathVariable(value="service") String serviceName,
#PathVariable(value = "route_id") int routeId,
HttpServletResponse response) {
/*Do something*/
}
The question is not, whether it is good practice or not to write it like so. The question is whether there is an approach similar to Filter or Interceptor (or the proper use of them) to access those parameters.
A requirement is, that it can be applied easily to an application by adding very few lines of code. Annotating every Method call manually or manually inserting the code to write the parameters into the trace from within the method is not feasible for me.
If you need more information feel free to ask. I will provide you with all information you need to help me with my problem.
Although not officially supported (as it's not written in the reference documentation), Spring MVC holds that information as request attributes.
You could create your own HandlerInterceptor, ordered right after the Sleuth one, and get that information from the request like this:
// "/{service}/{route_id}/book"
String matchingPattern = (String) request
.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE);
// "service" => "fooService", "route_id" => "42"
Map<String, String> templateVariables = (Map<String, String>) request
.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
Note, the template variables are already decoded at that point, which is probably what you want anyway.
You can create a Filter that intercept all the requests.
For each request you can retrieve also this informations:
query parameters
body of request
url
header parameters
You can save all this data as you need.
This is the best way you can do that.
If you need to group all urls like /{service}/{route_id}/book in a "family" of urls you can do that splitting the url and check if it is part of the family, but when a new family is added in your code you need to update the filter (or configure something in an external file or database).
I am using the #RequestParam annotation to instruct Spring MVC to inject a "web request parameter" (as the Javadoc calls them) into a method call in my code:
#RequestMapping(path="/signup-submit", method=RequestMethod.POST)
public String signupSubmit(#RequestParam(value="originURL", required=true) String originURL ...) {
...
I am bothered by the fact that this annotation apparently works both for POST parameters and for URL query parameters. I.e. looking at the above code it is not possible to say whether the originURL is a POST body parameter or is a URL query parameter. Is there an annotation I can use to explicitly get it from either one or the other?
This is why I also place "web request parameter" in quotation marks as I don't think this is a technical term and I guess it is used in the loose sense of "some parameter either passed in the POST method body or as a query parameter in the URL".
Contrary to what cularis said there can be both in the parameter map.
The best way I see is to proxy the parameterMap and for each parameter retrieval check if queryString contains "&?=".
Note that parameterName needs to be URL encoded before this check can be made, as Qerub pointed out.
That saves you the parsing and still gives you only URL parameters.
The way can be getting query string..
public List<Topic> getTopics(HttpServletRequest req) {
String queryString = req.getQueryString();
.....
About "Web-Request-Parameter" (#RequestHeader), you are right as it consists of both params i.e. either are part of query or a part of request body.
Not the exact solution to your problem. But seems like there's an easy hack for it. You can use #RequestBody. This only looks for request body. If param is not found in request body, you can be sure that it is from request param.
Trying to allow forward slashes using JAX-RS #PathParam. What happens is that it takes the backend of the value and not the whole value. Example below
METHOD CODE
#PUT
#POST
#Path("/temp/{keyValue:.+}")
#Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
public void setValues(#PathParam("keyValue") PathSegment myvalue) {
for (String key : myvalue.getMatrixParameters().keySet()) {
System.out.println(batch)
}
TEST URL
https://localhost:8443/*path*/temp/keyValue;key1=THIS/SUCKS
RESULT
batch = SUCKS
DESIRED RESULT
batch = key1=THIS/SUCKS
I have tried changing the regex in the method signature and I can not make this work. If things are url encoded, I am able to get it pass through with the %2F. However, if I change my Apache httpd to AllowEncodedSlashes On, it would break the rest of my site. Any ideas on what it could be?
I am running httpd -> tomcat -> restService (JAX-RS)
I couldn't get this to work correctly, so I rewrote it so that it accepts a string that is converted to a JSON object. Sending parameters in a URL string is a bad idea because URLs have a character limit. I am now sending it as data in an AJAX call.