Spring get all queries params as a string - spring

I have an API gateway that handles all GET requests and forwards them to the correct url like so
#RequestMapping(value = "**", method = RequestMethod.GET)
public #ResponseBody ResponseEntity<String> doGet(HttpServletRequest req) {
String uriString = (String) req.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE);
String targetHost = uriString.split("/")[0];
URI uri = UriComponentsBuilder.fromUriString(targetHost)
.path(uriString)
.build().normalize().encode().toUri();
try {
ClientHttpRequest request = requestFactory.createRequest(uri, HttpMethod.GET);
request.getHeaders().add(HttpHeaders.ACCEPT, "application/json");
ClientHttpResponse response = request.execute();
HttpStatus status = response.getStatusCode();
String json = readBodyAsString(response);
return new ResponseEntity<>(json, status);
} catch (IOException ioe) {
StringBuilder sb = new StringBuilder();
sb.append("{\"message\": \"").append(ioe.getMessage()).append("\"}");
return new ResponseEntity<>(sb.toString(), HttpStatus.INTERNAL_SERVER_ERROR);
}
}
This works really well for all get requests that have any number of paths.
Problem is the
HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE
Only grabs the paths of a given URL and not the Query Params
So if this controller gets a request with /api/path/path/path it works but if it gets /api/path/path/path?query=1?search=2 for example it will only grab /api/path/path/path and then the subsequent request will fail since it required query params.
How can I get the entire path of the wild card match to include any queryParams that might be here
Thanks

Really simple just needed to use req.getQueryString() to get all the query params as a string.
Also important to note that I needed to pass the query as a .query() on the UriComponentsBuilder so that it gets encoded properly.
URI uri = UriComponentsBuilder.fromUriString(targetHost)
.path(uriString)
.query(queryParams)
.build().normalize().encode().toUri();

Related

Spring RestTemplate seems to not be thread-safe wrt headers

I have a Spring web client which posts to a Spring web server (the same URL) using two different basic-auth users. Is it a known issue that I can not use a single RestTemplate for both?
When I use a single RestTemplate, and the requests are nearly simultaneous (in different threads), though I specify different users in the header, the receiving server thinks they're from the same user! Note that the request and the headers (and the body of the post) are newly allocated for each request.
It works fine, when I use a single RestTemplate and put a synchronized() around the call to
response = RestTemplate.exchange(url, method, requestParams, MyResponse.class)
I've also tried creating two RestTemplate instances, one for each user - (each built with a RestTemplateBuilder) that works, too. I'll keep this solution, but it surprises me that it's needed.
Is this a known issue?
(I see stackOverflow answers that a RestTemplate is thread-safe after constructed, but the headers are passed in with the request, not as a setting on the already-constructed RestTemplate...)
====
Here's an example of 2 different calls, using 2 different RestTemplates because there were sometimes problems in using the same:
public OperationStatus getOpStatus(String gufi) {
HttpEntity<String> requestParams = new HttpEntity<>(Utils.createBasicHeader(cfg.getManager(), cfg.getManPass()));
ResponseEntity<OperationStatus> restResponse = null;
try {
restResponse = managerRestTemplate.exchange(
cfg.getNussOpApiPath(), HttpMethod.GET, requestParams, OperationStatus.class);
} catch (RestClientException e) {
...
}
OperationStatus opState = restResponse.getBody();
opState.setHttpStatusCode(String.valueOf(restResponse.getStatusCodeValue()));
return opState;
}
Here was a method to do a post, using the priority to switch rest templates (at the time, the target server recognized the priority by the privileges of the user)
UTMRestResponse doPost(Object objToSend, String url, String msg) throws IOException {
String user = cfg.getOpUser();
String pass = cfg.getOpPass();
RestTemplate restTemplate = opUserRestTemplate;
boolean isPriorityOp = false;
if ( objToSend instanceof OpPost) {
OpPost post = (OpPost) objToSend;
String flightNum = post.getFlightNumber();
isPriorityOp = Boolean.TRUE.equals(post.getPriorityOp()); // null is false
} else if ( objToSend instanceof PositionPost) {
PositionPost post = (PositionPost) objToSend;
isPriorityOp = Boolean.TRUE.equals(post.getPriorityOp()); // null is false
}
if (isPriorityOp) {
user = cfg.getUserEmergency();
pass = cfg.getPassEmergency();
restTemplate = emergRestTemplate;
}
String jsonToSend = CommonsObjectMapper.get().writeValueAsString(objToSend);
HttpEntity<String> requestParams = new HttpEntity<>(jsonToSend, Utils.createBasicHeader(user, pass));
UTMRestResponse restResponse = restTemplate.exchange(
url, HttpMethod.POST, requestParams, UTMRestResponse.class).getBody();
if (restResponse.getHttpStatusCode().startsWith("4")) {
String fmt = "Status:{}, url:{}, jsonSent:{}, response:{}";
logger.error(fmt, restResponse.getHttpStatusCode(), url, jsonToSend, restResponse.getMsg());
}
return restResponse;
}

Angular 4 and Spring Rest: How to post FormData containing File and model object in a single request

I would like to send a File object along with custom model object in a single request.
let formData:FormData = new FormData();
let file = this.fileList[0];
formData.append('file', file, file.name);
formData.append('address', JSON.stringify(customObj));
...
this.http.post(fileServeUrl, formData)
My backend is in Spring Rest as below
#RequestMapping(value = "/fileServe",
produces = {"application/json"},
consumes = {MediaType.APPLICATION_JSON_VALUE, MediaType.MULTIPART_FORM_DATA_VALUE},
method = RequestMethod.POST)
ResponseEntity<Image> uploadFile(#RequestPart("file") MultipartFile imageData, #RequestPart("address") Address address) throws IOException {...}
I was able to receive the data if I pass simple String along with File though.
formData.append('file', file, file.name);
formData.append('address', addressText);
Backend
#RequestMapping(value = "/fileServe",
produces = {"application/json"},
consumes = {MediaType.APPLICATION_JSON_VALUE, MediaType.MULTIPART_FORM_DATA_VALUE},
method = RequestMethod.POST)
ResponseEntity<Image> uploadFile(#RequestPart("file") MultipartFile imageData, #RequestPart("address") String addressText) throws IOException {...}
I tried #RequestBody for my custom object but even that didn't work. Any advise please.
The problem with #Requestbody and #RequestPart annotation is that spring use the HttpMessageConverter to take convert the incoming json message into the your object. As you send form data with a file and a text value spring can not convert it into your object. I am afraid you have to pass the value of address seperatetly.
#RequestMapping(value = "/fileupload", headers = ("content-type=multipart/*"), method = RequestMethod.POST)
public ResponseEntity<AjaxResponseBody> upload(#RequestParam("file") MultipartFile file, #RequestParam String name, #RequestParam String postCode) {
AjaxResponseBody result = new AjaxResponseBody();
HttpHeaders headers = new HttpHeaders();
if (!file.isEmpty()) {
try {
Address address = new Address();
address.setName(name);
result.setMsg("ok");
return new ResponseEntity<AjaxResponseBody>(result, headers, HttpStatus.OK);
} catch (Exception e) {
return new ResponseEntity<AjaxResponseBody>(HttpStatus.BAD_REQUEST);
}
} else {
return new ResponseEntity<AjaxResponseBody>(HttpStatus.BAD_REQUEST);
}
}
Expept if you find a way your client app send a file with MimeType of image/jpg and and an address of application/json which allow spring to parse the json and map to your Address object which i couldn't do it.

RestTemplate gives 400 Bad Request Error on a Get Request

When I try to make a get request with Spring's RestTemplate, it gives 400 BAD Request. I can call the same url from javascript successfully with the headers below :
But the code below does not work. What might be the cause?
public Entity getEntityByUri(String uri) {
String req = "http://live.dbpedia.org/sparql?query=DESCRIBE%20%3Chttp://dbpedia.org/resource/Concept_learning%3E&format=application%2Fjson-ld";
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.ALL));
HttpEntity<String> httpEntity = new HttpEntity<String>(headers);
new RestTemplate().exchange(req, HttpMethod.GET, httpEntity, Map.class);
Entity entity = new Entity();
return entity;
}
Your url is already encoded. Popular browsers such as Chrome are capable of understanding and responding appropriately. However, it's not the same case with RestTemplate.
I had to decode your uri here and the decoded uri is DESCRIBE <http://dbpedia.org/resource/Concept_learning>
Having checked the browser console, I got to know you have two query strings passed in the url, they are query and format holding values DESCRIBE <http://dbpedia.org/resource/Concept_learning> and application/json-ld respectively.
I assume Entity class is the pojo class of json response.
Have created Entity as from your json response:
public class Entity {
private String value;
private String type;
// getters and setters omitted for brevity
}
Finally in your getEntityByUri method have got the instance of UriComponentsBuilder which handles uri encoding and query params.
To sum up, your getEntityByUri looks below.
public HttpEntity<Entity> getEntityByUri() {
String req = "http://live.dbpedia.org/sparql";
UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(req)
.queryParam("query",
"DESCRIBE <http://dbpedia.org/resource/Concept_learning>")
.queryParam("format", "application/json-ld");
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.ALL));
HttpEntity<String> httpEntity = new HttpEntity<String>(headers);
return new RestTemplate().exchange(builder.build().encode().toUri(), HttpMethod.GET, httpEntity, Entity.class);
}
The above method didn't throw HTTP400 as the required query params have been passed in builder object.
Hope this helps and good luck!
Anyone getting same error make sure your URL is decoded means no percent symbols in url (if space in param values).
This worked for me
try {
requestURL = URLDecoder.decode("http://api.com?p=1&groups=3212&affected-since=2019-06-06T14%3A11%3A14.880&detail=full&after-id=43536", "UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
Maybe
headers.setAccept(Arrays.asList(MediaType.ALL));
generates a malformed "Accept" header field? (FWIW, why do you send it at all???)

Getting 400 Bad Request during POST string in spring mvc

i have a rest api that accept a String in POST and return an object,
this is the method:
#RequestMapping(method = RequestMethod.POST, value = "/aValue", headers = "Accept=application/json")
public #ResponseBody
MyObject getMyObject(#RequestBody String string) {
MyObject response = myService.getMyObject(string);
return response;
}
now when i call the api from another service for example, if I do POST like this it gave me always 400 Bad Request:
List<Object> providers = new ArrayList<Object>();
providers.add(jsonProvider);
WebClient client = WebClient.create(baseUrl + myAPI, providers);
client.type(MediaType.APPLICATION_JSON);
client.accept(MediaType.APPLICATION_JSON);
MyObject response = client.post(userId, MyObject.class);
return response;
instead of the working solution i used which is this one:
MyObject response = client.post("\"" + userId + "\"", MyObject.class);
someone could help me ? thanks guys
You're having an issue 'cause what you're posting is not a valid JSON, yet you indicate that it is in your client-side code. As you seem to pass just a simple string property userId you can simply change your mapping to receive plain text by adding consumes = "text/plain",
#RequestMapping(method = RequestMethod.POST, value = "/aValue", headers = "Accept=application/json", consumes = "text/plain")
public #ResponseBody
MyObject getMyObject(#RequestBody String string) {
and have your client send plain text, so
client.type(MediaType.TEXT_PLAIN);

How to send GET request with headers by Spring

It will call another REST API with a GET request.
#RequestMapping(value = "xxxx/{id}", method = RequestMethod.GET)
public #ResponseBody GetObjet GET( #PathVariable("id") String id,
#RequestHeader(value="X-Auth-Token") String Token) {
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.add("X-Auth-Token", Token);
HttpEntity entity = new HttpEntity(headers);
ResponseEntity<GetObjet> response = restTemplate.exchange(url, HttpMethod.GET, entity, GetObjet.class);
return response.getBody();
}
Always 400 Error. It means that bad request or some errors in the request body. But this is GET so the resquest bodys is always empty. So this way to add header may be not right. Any ideas?
You can obtain the headers including the notation #RequestHeader in your method
public void displayHeaderInfo(#RequestHeader("Accept-Encoding") String encoding,
#RequestHeader("Keep-Alive") long keepAlive) {
}
o
You can read more about the request here
And the other way to abtain the URL is:
#RequestMapping(value = "/restURL")
public String serveRest(#RequestBody String body, #RequestHeader HttpHeaders headers){
//Use headers to get the information about all the request headers
long contentLength = headers.getContentLength();
...
StreamSource source = new StreamSource(new StringReader(body));
YourObject obj = (YourObject) jaxb2Mashaller.unmarshal(source);
...
}
Try using:
RestTemplate.getForEntity(url, GetObject.class);
You have some methods to request data from a rest API, such as getForEntity and getForObject, use the one you needed.

Resources