Update xml file path with rest call - java

I want to program an api rest call to update a String of a class, which contains the filename of an xml file.
I am trying to do it with a GET call... But there may be a more suitable option.
This is a url sample: http://localhost/changeXML?configFile=Configuration.xml
#RequestMapping(value = "/changeXML",params= {"configFile"}, produces = { MediaType.APPLICATION_XML_VALUE},
headers = "Accept=application/xml",method = RequestMethod.GET)
public ResponseEntity<?> updateConfigFile(#RequestParam("configFile") String file) {
File f = new File(file);
System.out.println(f);
if(f.exists() && !f.isDirectory()) { //file is updated if and only if it exisits
System.out.println("FICHERO SI QUE EXISTEEEEE");
this.configFile=file;
return new ResponseEntity<String>("XML configuration file has been updated to: "+file, HttpStatus.OK);
}
System.out.println("PETITION");
//otherwise path is not going to be updated
return new ResponseEntity<String>("Unexisting XML", HttpStatus.OK);
}
All I want is the attribute configFile updated. But still, all I ever fin is the following error:
This page contains the following errors:
error on line 1 at column 1: Document is empty
Below is a rendering of the page up to the first error.
My xml I can assure it to be fine and... even if I put this other url, http://localhost/changeXML?configFile=c%C3%B1dlvm%C3%B1ldfmv
I still have the same error.
Could someone provide some info about this? Thanks in advance!

Within your #RequestMapping annotation, you've put the MediaType.APPLICATION_XML_VALUE value for the produces parameter. This means that you tell your browser that the response will contain XML.
However, if you take a look at the responses, you're returning plain text in stead. Your browser probably tries to parse this as XML, but can't, and throws an error.
The solution is to tell your browser that you're returning plain text, which is the text/plain media type, or MediaType.TEXT_PLAIN in Spring:
#RequestMapping(
value = "/changeXML",
params= {"configFile"},
produces = {MediaType.TEXT_PLAIN}, // Change this
headers = "Accept=application/xml",
method = RequestMethod.GET)
In this case, you probably can leave away the produces parameter entirely, as Spring will be able to automatically resolve this. Even more, the headers and params parameters aren't necessary either in this case, so you could just write:
#RequestMapping(value = "/changeXML", method = RequestMethod.GET)
Or even shorter:
#GetMapping("/changeXML")

Related

SonnarQube's issue: change code to not construct the URL from user-controlled data

I facing a SonarQube bug and am not able to figure out whats the issue. SonnarQube's issue is, change this code to not construct the URL from user-controlled data.
#Value("${...}")
String apiKey;
#Value("${...}")
String apiUrl;
public Response apiResponse(String location) {
HttpHeaders headers = new HttpHeaders();
headers.add("x-apikey", apiKey);
HttpEntity<Object> entity = new HttpEntity<>(headers);
String url = apiUrl + location; // SonarQube issue: tainted value is propagated
Response response = null;
try {
ResponseEntity<Response> responseEntity = restTemplate.exchange(url, HttpMethod.GET, entity, Response.class); // SonarQube issue: Tainted value is used to perform a security- sensitive operation.
response = responseEntity.getBody();
} catch(Exception){
// doesn't throw anything
}
return response;
}
#Cacheable(...)
Response cacheResponse(String location, String tokenKey) {
return apiResponse(location); // SonarQube issue: tainted value is propagated
}
This fixed the issue, but why is that so? and how can I apply this in the above code?
String url = apiUrl + location; // SonarQube issue: tainted
Instead, I just tried hardcoding the value of location and fixed the issue.
String url = apiUrl + "location";
So weird...
I added validation for the Location variable and this solved the issue
if(!location.matches(...)) {
throw error.....
}
String url = apiUrl + location;
What SonarQube is trying to tell you is that you are exposing your logic to input from the clients. A better solution would be to refactor your code to not depend on a specific header from the client to perform some action. Its hard to suggest sample code without seeing a little more of the codebase.
You are using input from the client/user (namly in the variable location) to construct an URL. So if the client/user supplies an malicious value to location he could form an invalid URL.
In the second example String url = apiUrl + "location"; you are not using user input, as "location" is a hard coded String.
I don't know what you try to achieve with the code. But maybe it's better to hold a list of possible URLs and the user supplies and enum value that maps to an URL.
String url = "https://someurl/%s";
url = String.format(url,location);
sendRequest(url);
Maybe this approach won't give error.

Passing custom object from client to REST endpoint using Spring Web

I'm making a client-server application that sends a matrix to a server, where its determinant is computed and then sent back to the client. I've made this wrapper class:
public class MatrixDTO { // with getters and setters
private double[][] matrix;
private double determinant;
}
And I've also implemented the server logic for obtaining the determinant from the MatrixDTO object.
I've added this RestController in the server:
#RestController
public class MatrixController {
#RequestMapping(value = "/", method = RequestMethod.POST)
public MatrixDTO postMapping(#RequestParam MatrixDTO matrixDTO) {
// code to compute determinant ommitted
matrixDTO.setDeterminant(determinant);
return matrixDTO;
}
Then in the client I've added this method of sending the request:
final String uri = "http://localhost:8080/?matrixDTO={matrixDTOparam}";
// initialized wrapper object only with matrix data
MatrixDTO input = new MatrixDTO(data);
Map<String, MatrixDTO> params = new HashMap<>();
params.put("matrixDTOparam", input);
RestTemplate restTemplate = new RestTemplate();
result = restTemplate.postForObject(uri, input, MatrixDTO.class, params);
// now I should be able to extract the determinant with result.getDeterminant()
Many hours were lost trying to get this simple code to work. The error is:
Failed to convert value of type 'java.lang.String' to required type 'MatrixDTO';
nested exception is java.lang.IllegalStateException: Cannot convert value of type 'java.lang.String' to required type 'MatrixDTO':
no matching editors or conversion strategy found]
My question is the following: should I choose another approach for my problem, and if not, is there an easy way to make the code work? I'm looking for a simple implementation and not a lot of configuration to be done. Thanks.
So far, my error seems to be using #RequestParam instead of #RequestBody in the controller.
By changing that and using this code in the client:
final String uri = "http://localhost:8080/";
MatrixDTO input = new MatrixDTO(data);
RestTemplate restTemplate = new RestTemplate();
result = restTemplate.postForObject(uri, input, MatrixDTO.class);
it appears to work well enough.

Problem with RestTemplate when including URL addresses in REST API request

I'm trying to obtain data from Botify's REST API to use it inside a project, which is also a REST API. I'm using an instance of Spring's RestTemplate class to make the actual requests to Botify, specifically the .exchange method as I need to pass Botify's key as a header parameter.
My problem comes when I need to call to a method of the endpoint which takes a URL as a part of the request's URI (not a parameter). Documentation of this endpoint is in https://developers.botify.com/api/reference/#!/Analysis/getUrlDetail
Basically the structure of the requests is like this:
/analyses/{username}/{project_slug}/{analysis_slug}/urls/{url}
The last part of that URI is a URL address, which needs to be encoded in UTF-8 to make it possible to separate it from the actual request.
The problem is (I believe) that the .exchange method always encodes the request, so what I try to send like this:
/analyses/myusername/myprojectname/myprojectslug/urls/https%3A%2F%2Fwww.example.com
...ends up like this:
/analyses/myusername/myprojectname/myprojectslug/urls/https%253A%252F%252Fwww.example.com'
Which obviously doesn't work. This is an excerpt from the method that makes the call to Botify:
public String callBotifyEndpoint(String reportType, String parameters) throws UnsupportedEncodingException {
String request = this.baseUri + "/analyses/myusername/myprojectname/myprojectslug/urls/https%3A%2F%2Fwww.example.com"
HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", "Token " + this.apiKey);
HttpEntity<String> entity = new HttpEntity<>(headers);
UriComponentsBuilder botifyQueryBuilder = UriComponentsBuilder.fromUriString(request);
String queryStringBuild = botifyQueryBuilder.build(true).toUriString();
String botifyResult = null;
try {
System.out.println("Calling Botify API: " + queryStringBuild);
ResponseEntity<String> response = botifyTemplate.exchange(queryStringBuild, HttpMethod.GET, entity, String.class);
if(response.hasBody()) {
botifyResult = response.getBody();
}
} catch(RestClientException ex) {
ex.printStackTrace();
}
try {
} catch (Exception e) {
// TODO: handle exception
}
return botifyResult;
}
In this line:
botifyQueryBuilder.build(true).toUriString();
The "true" parameter indicates whether the data is already encoded or not. I've tried to disable it but the result is the same.
I've removed actual request generation process (along with my user and project's name) to simplify things, but this should return a response from Botify with the existing data for that URL.
Instead, it returns a 400 bad request error (which makes sense, because the URL is not correct).
I'm feeling like this may be a bug in RestTemplate's .exchange method, but maybe I'm not using it properly. Any suggestions?
Don't encode prematurly as you do here:
String request = this.baseUri + "/analyses/myusername/myprojectname/myprojectslug/urls/https%3A%2F%2Fwww.example.com";
Use parameter placeholders feature in RestTemplate instead of text concatenation.
Refer to:
Spring RestTemplate GET with parameters

How to properly escape a URL to be used in RestTemplate that has flower brackets (JSON) [duplicate]

I am trying to access the contents of an API and I need to send a URL using RestTemplate.
String url1 = "http://api.example.com/Search?key=52ddafbe3ee659bad97fcce7c53592916a6bfd73&term=&limit=100&sort={\"price\":\"desc\"}";
OutputPage page = restTemplate.getForObject(url1, OutputPage .class);
But, I am getting the following error.
Exception in thread "main" java.lang.IllegalArgumentException: Not enough variable values available to expand '"price"'
at org.springframework.web.util.UriComponents$VarArgsTemplateVariables.getValue(UriComponents.java:284)
at org.springframework.web.util.UriComponents.expandUriComponent(UriComponents.java:220)
at org.springframework.web.util.HierarchicalUriComponents.expandInternal(HierarchicalUriComponents.java:317)
at org.springframework.web.util.HierarchicalUriComponents.expandInternal(HierarchicalUriComponents.java:46)
at org.springframework.web.util.UriComponents.expand(UriComponents.java:162)
at org.springframework.web.util.UriTemplate.expand(UriTemplate.java:119)
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:501)
at org.springframework.web.client.RestTemplate.getForObject(RestTemplate.java:239)
at hello.Application.main(Application.java:26)
If I remove the sort criteria, it is working properly.
I need to parse the JSON using sort criteria.
Any help will be much appreciated.
Thanks
The root cause is that RestTemplate considers curly braces {...} in the given URL as a placeholder for URI variables and tries to replace them based on their name. For example
{pageSize}
would try to get a URI variable called pageSize. These URI variables are specified with some of the other overloaded getForObject methods. You haven't provided any, but your URL expects one, so the method throws an exception.
One solution is to make a String object containing the value
String sort = "{\"price\":\"desc\"}";
and provide a real URI variable in your URL
String url1 = "http://api.example.com/Search?key=52ddafbe3ee659bad97fcce7c53592916a6bfd73&term=&limit=100&sort={sort}";
You would call your getForObject() like so
OutputPage page = restTemplate.getForObject(url1, OutputPage.class, sort);
I strongly suggest you do not send any JSON in a request parameter of a GET request but rather send it in the body of a POST request.
If the solution suggested by sotirios-delimanolis is a little difficult to implement in a scenario, and if the URI string containing curly braces and other characters is guaranteed to be correct, it might be simpler to pass the encoded URI string to a method of RestTemplate that hits the ReST server.
The URI string can be built using UriComponentsBuilder.build(), encoded using UriComponents.encode(), and sent using RestTemplate.exchange() like this:
public ResponseEntity<Object> requestRestServer()
{
HttpEntity<?> entity = new HttpEntity<>(requestHeaders);
UriComponentsBuilder builder = UriComponentsBuilder.fromUriString(rawValidUrl)
.queryParams(
(LinkedMultiValueMap<String, String>) allRequestParams);
UriComponents uriComponents = builder.build().encode();
ResponseEntity<Object> responseEntity = restTemplate.exchange(uriComponents.toUri(), HttpMethod.GET,
entity, String.class);
return responseEntity;
}
Building, encoding, and extracting URI have been seperated out for clarity in the above code snippet.
You can URL encode the parameter values:
String url1 = "http://api.example.com/Search?key=52ddafbe3ee659bad97fcce7c53592916a6bfd73&term=&limit=100&sort=";
org.apache.commons.codec.net.URLCodec codec = new org.apache.commons.codec.net.URLCodec();
url1 = url1 + codec.encode("{\"price\":\"desc\"}");
OutputPage page = restTemplate.getForObject(url1, OutputPage.class);
You can set a specific UriTemplateHandler in your restTemplate. This handler would just ignore uriVariables :
UriTemplateHandler skipVariablePlaceHolderUriTemplateHandler = new UriTemplateHandler() {
#Override
public URI expand(String uriTemplate, Object... uriVariables) {
return retrieveURI(uriTemplate);
}
#Override
public URI expand(String uriTemplate, Map<String, ?> uriVariables) {
return retrieveURI(uriTemplate);
}
private URI retrieveURI(String uriTemplate) {
return UriComponentsBuilder.fromUriString(uriTemplate).build().toUri();
}
};
restTemplate.setUriTemplateHandler(skipVariablePlaceHolderUriTemplateHandler);
You can encode url before using RestTemplate
URLEncoder.encode(data, StandardCharsets.UTF_8.toString());
You can simply append a variable key to the URL and give the value using the restTemplate.getForObject() method.
Example:
String url = "http://example.com/api?key=12345&sort={data}";
String data="{\"price\":\"desc\"}";
OutputPage page = restTemplate.getForObject(url, OutputPage.class, data);

Is it possible to call controller method from a scriptlet in Spring

I need to implement a call to a method that I have in my controller. But I want to call this method from a scriptlet and I don't know how to do it. I am trying to export data that I get from the server to CSV.
This is the scriptlet that I have so far:
<%
String csvDataIn = request.getParameter("exportCSVParam");
String csvFileName = request.getParameter("exportCSVFileName");
if (csvFileName == null || csvFileName == "") csvFileName = "export.csv";
String strHeader = "attachment; filename=" + csvFileName;
String contentType = "application/octet-stream";
response.setContentType(contentType);
response.addHeader("content-disposition",strHeader);
ServletOutputStream ostr = response.getOutputStream();
String data=csvDataIn;//DATA GOES HERE;
ostr.write(data.getBytes("ISO-8859-1"));
ostr.flush();
ostr.close();
%>
Assume that I want to call a method getDataAsCsv() that I have in my controller that returns a String with the CSV data that I want to print in that file. Lines 1 and 2 (csvDataIn, csvFileName) should be deleted since I am not going to send parameters to this jsp. How do you do that?. How do you bind the controller bean with this scriptlet.
I am new to spring and I am still learning about this. Probably the solution is very simple but I am stuck with this.
You can't, because you shouldn't. All this code should go in the controller.
(technically, you can have a JSTL function and call it, or simply call a static method, or even get the controller with WebApplicationContextUtils.getRequiredWebApplicationContext(..).getBean(..), but all these will be ugly)

Categories

Resources