How do I add attributes to http request to spring controller? - java

I would like to test the spring controller below, which reads http request attributes and acts on them. I am able to trigger the controller code below by typing localhost:8080/someURL into my web browser. But the result is {"id":1,"content":"null and null and null"}, which indicate null values in the named request attributes. How do I send a request to a named url like localhost:8080/someURL which contains values for the specified request attributes, so that I can confirm that the receiving controller code works properly?
Here is the code for the controller:
import java.util.concurrent.atomic.AtomicLong;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestPa ram;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;
#Controller
public class SomeController {
private final AtomicLong counter = new AtomicLong();
#RequestMapping(value = "/someURL", method=RequestMethod.GET)
public #ResponseBody Greeting receiveSMS(HttpServletRequest req){
String att1 = (String) req.getAttribute("att1");
String att2 = (String) req.getAttribute("att2");
String att3 = (String) req.getAttribute("att3");
String total = att1 + " and " + att2 + " and " + att3;
return new Greeting(counter.incrementAndGet(), String.format(total));
}
}
Note: I am trying to recreate in Spring the PHP functionality that is given in the script at the following link. I have not written this kind of code below, if I am framing the question poorly, I would appreciate advice for reframing it. Along with a link to any example solution such as a JUNIT or other means by which to recreate the request.

Request attributes are server-side only constructs. Try using request parameters instead:
#RequestMapping(value = "/someURL", method = RequestMethod.GET)
public #ResponseBody Greeting receiveSMS(#RequestParam("att1") String att1, #RequestParam("att2") String att2, #RequestParam("att3") String att3){
String total = String.format("%s and %s and %s", att1, att2, att3);
return new Greeting(counter.incrementAndGet(), total);
}
Then send a request of the form:
http://localhost:8080/someURL?att1=value1&att2=value2&att3=value3
And you should be able to read the values that you are trying to pass.

Checkout Spring MVC Test framework - instead of manually fire some URLs write unit tests instead.
Regarding your note
Yes, that's parameters. In php you have $_GET and $_POST or (if you don't care about the method) simply $_REQUEST for accessing the request parameters. Recode from getAttribute() to getParameter() or put them in your method signature using #RequestParam annotation.
#RequestMapping(value = "/receiveSMS", method=RequestMethod.GET)
public #ResponseBody Greeting receiveSMS(#RequestParam("from") String from,
#RequestParam("to") String to, #RequestParam("body") String body){
}
Now you can try http://localhost:8080/yourapp/receiveSMS?from=me&to=you&body=stackoverflow
Sidenote:
If you want that info send from the client, you should use getParameter() calls instead.

test something like :
http://localhost:8080/someURL?att1=value1&att2=value2&att3=value3
It will show you the value1, value2 and value3 passed into the URL...

Related

Can't figure out how to change Prometheus content type header

So my metrics all appear in one line at my end-point, not in new line per metric.
I use micrometer, spring, prometheus and scala.
My controller:
#RequestMapping(Array(""))
class MetricsController #Inject() (prometheusRegistry: PrometheusMeterRegistry) {
#RequestMapping(value = Array("/metrics"), method = Array(RequestMethod.GET))
def metricses(): String = {
prometheusRegistry.scrape()
}
}
Should it be enough to change the way I write the metrics them selves?
I have tried to add scrape(TextFormat.CONTENT_TYPE_004) but that changed nothing.
Does it have to do with the HTTP response header?
Would it work to add:
.putHeader(HttpHeaders.CONTENT_TYPE, TextFormat.CONTENT_TYPE_004)
.end(registry.scrape());
If so how would I do that in my case?
Thanks
Prometheus (or other compatible backends) will send you an Accept header that you should not ignore (please read about content negotiation) but if you want to ignore it:
#GetMapping(path = "/metrics", produces = MediaType.TEXT_PLAIN_VALUE)
#ResponseBody String metrics() {
return registry.scrape();
}
If you don't want to ignore it, TextFormat has a chooseContentType method that you can utilize to get the content type based on the Accept header:
#GetMapping(path = "/metrics")
#ResponseBody ResponseEntity<String> metrics(#RequestHeader("accept") String accept) {
String contentType = TextFormat.chooseContentType(accept);
return ResponseEntity
.ok()
.contentType(MediaType.valueOf(contentType))
.body(registry.scrape(contentType));
}
Or you can also set-up content negotiation: https://www.baeldung.com/spring-mvc-content-negotiation-json-xml

Can we call external API without API ID?

I am new to API design, I am working on one project where I need to call currency exchange API from National Bank of Poland http://api.nbp.pl but I do not see any indication where I can find API ID. This development is on Spring Boot if I am trying to run the application without API ID it is throwing 404 error.
Here is the piece of code that I have written.
#RequestMapping(method = RequestMethod.GET, value = "/exchangerates/rates/{table}/{code}")
public #ResponseBody Object getAllCurriencyExchangeRates(#PathVariable String table, #PathVariable String code) {
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
ResponseEntity<Object> response =
restTemplate.getForEntity("http://api.nbp.pl/api/" +table+ "," +code+ Object.class, null, headers);
return response;
}
Actual query http://api.nbp.pl/api/exchangerates/rates/a/chf/
So, my question is can we call an external API without API ID?
First things first, you are trying to reach wrong API. That is why you are getting 404 not found. 404 means there is no url like you are calling.
Check your restTemplate carefully,
restTemplate.getForEntity("http://api.nbp.pl/api/" + table+ "," +code+ Object.class, null, headers);
You are doing wrong when concatenate strings.
It should look something like this;
restTemplate.getForEntity("http://api.nbp.pl/api/exchangerates/rates/"+table+"/"+code, Object.class, null, headers);
And a hint for API developers, firstly you should play with api using Postman and then write code with api.
Try this - I have tested it - it works. Please keep in mind this is just a test implementation. Things inside main method have to be copied into your getAllCurriencyExchangeRates method.
And for sure replace "a" and "chf" through variables. I assume table and code are the variables you want to use. I used String because I don't know which type of object you want to return. You can use your own pojo for sure instead of String.
package scripts;
import java.net.URI;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestOperations;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;
/**
* author: flohall
* date: 08.12.19
*/
public class Test {
public static void main(final String[] args){
final String url = "http://api.nbp.pl/api/exchangerates/rates";
final URI uri = UriComponentsBuilder.fromHttpUrl(url).path("/").path("a").path("/").path("chf").build().toUri();
System.out.println(uri);
final RestOperations restTemplate = new RestTemplate();
final ResponseEntity<String> result = restTemplate.getForEntity(uri, String.class);
System.out.println(result.getBody());
}
}
Try with this
ResponseEntity<Object> response =
restTemplate.getForEntity("http://api.nbp.pl/api/exchangerates/rates/" + table + "/" + code, Object.class, headers);

Form application encoded value is not working in spring rest

I have the below post request and of which below is the controller code
#RestController
#RequestMapping(/flow", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
#Override
#ResponseStatus(HttpStatus.OK)
#PostMapping("{abcCode}/token")
public TokenResponse createToken(#PathVariable("abcCode") String abcCode,
#RequestParam("grant_type") String grantType,
#RequestParam String code,
#RequestParam("redirect_uri") String redirectUri,
#RequestParam String clientId) {
LOG.info(
"Received call for createIdToken for abcCode: {} , clientId: {} , grantType: {} ,code: {} , redirectUri: {}",
abcCode, clientId, grantType, code, redirectUri);
}
Now the problem is that when I test the same above controller through postman by choosing the body type as application form-encoded then it is working fine but when I choose the body type as none in postman and just pass the above request parameters as query one then also it works which ideally it should not please advise how can I overcome from the same
http://localhost:19080/testService/flow/token?grant_type=authorization_code&code=3272&redirect_uri=http://www.abchui.com&clientId=ATS
it should not work for the above URL
From spring sources:
public static final String APPLICATION_FORM_URLENCODED_VALUE = "application/x-www-form-urlencoded";
According to docs, when using url-form-encoded data pass as query params.
Try to change form mime type.

Getting current logged user in spring + angular

I am using Spring Security in my Spring Boot app and i want to get the current logged user from Principal#getName but i have an error of template resolution and it contains the username that i want to get.
This is my controller:
import java.security.Principal;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
#Controller
public class PageController {
#RequestMapping(value = "/api/loggeduser", method = RequestMethod.GET)
public String printWelcome(ModelMap model, Principal principal) {
String name = null;
if (principal !=null) {
name = principal.getName();
}
model.addAttribute("username", name);
return name;
}
}
And this is my AngularJs function to get the logged in user:
app.controller('Usercontr', function($scope, $http) {
$http.get("/api/loggeduser").success(function (data) {
$scope.nom = data;
console.log($scope.nom)
})
});
And here's the error:
Error resolving template "kamel.mili", template might not exist or
might not be accessible by any of the configured Template Resolvers
Kamel.mili is the logged in username. Can you please help me. I am using thymeleaf for just the login page and everything else is html + AngularJs. I don't know why thymeleaf had it's hand on this controller.
Add a #ResponseBody annotation to your controller:
#ResponseBody
#RequestMapping(value = "/api/loggeduser", method = RequestMethod.GET)
public String printWelcome(ModelMap model, Principal principal ) { ... }
When you're using the #Controller stereotype annotation, Spring MVC will try to resolve your String return value to a View, hence the error:
Error resolving template "kamel.mili", template might not exist or
might not be accessible by any of the configured Template Resolvers
If you want to just write the return value to the response body, you can either make your controller a #RestController or annotate specific controllers with #ResponseBody.
This is kinda off topic but since you're using client side view rendering, that ModelMap is pretty useless. You can safely get rid of it.
You need to do like following:
#RequestMapping(value = "/api/loggeduser", produces =MediaType.APPLICATION_JSON_VALUE )
public ResponseEntity<String> findMessagesForUser(Principal principal) {
return new ResponseEntity<String>(principal.getName(), HttpStatus.OK);
}
Or you can use response builders:
#RequestMapping(value = "/api/loggeduser", produces =MediaType.APPLICATION_JSON_VALUE )
public ResponseEntity<String> findMessagesForUser(Principal principal) {
return ResponseEntity.ok(principal.getName());
}
If you have properly configured template resolver, then you should check yours templates. Return string from printWelcome() should match with one of the view created in your view/template folder.In your case, you are returning user name it self, i guess it should be welcome kind of page.

Spring request hangs when HttpServletResponse/HttpServletRequest are added as method parameters

Following the example provided by spring.io for creating a simple REST service, I run into a strange issue.
If I make a request to localhost:8080/greeting the greeting route is called and I receive the expected response:
{"id":1, "content":"Hello, World!"}
If I add a route "/test" and then make an HTTP GET request to localhost:8080/test I get the expected response:
I'm a teapot
The problem arises when I do one of two things. Firstly, if I add HttpServletResponse or HttpServletRequest as a parameter to the test route and make an HTTP GET request to localhost:8080/test, the request hangs, the route is not called/executed, and maybe but not always the following is returned:
BODY: OK STATUS CODE: UNKNOWN 43
The second case is when I try to overcome this by using the #Autowire annotation. If I remove the HttpServletResponse/HttpServletRequest method parameters and instead autowire them in as class members, I get the same behavior.
If I make a request to any other invalid/undefined route e.g. HTTP GET localhost:8080/undefinedroute I receive the expected 404.
package hello;
import java.util.concurrent.atomic.AtomicLong;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
#RestController
public class GreetingController {
private static final String template = "Hello, %s!";
private final AtomicLong counter = new AtomicLong();
//#Autowired
//HttpServletRequest request;
//#Autowired
//HttpServletResponse response;
#RequestMapping("/test")
public String index() {
return HttpStatus.I_AM_A_TEAPOT.getReasonPhrase();
}
//#RequestMapping("/test")
//public String index(HttpServletResponse response) {
//response.setStatus(HttpStatus.I_AM_A_TEAPOT.ordinal());
//return HttpStatus.I_AM_A_TEAPOT.getReasonPhrase();
//}
#RequestMapping("/greeting")
public Greeting greeting(#RequestParam(value="name", defaultValue="World") String name) {
return new Greeting(counter.incrementAndGet(), String.format(template, name));
}
}
You cannot autowire HttpServletRequest or HttpServletResponse, because these objects are created when the server receives and handles an HTTP request. They are not beans in the Spring application context.
The status code of your response is 43 (unknown) because of this line:
response.setStatus(HttpStatus.I_AM_A_TEAPOT.ordinal()); // status 43?
ordinal() gives you the position of enum declaration, not the value of the status code. I_AM_A_TEAPOT is the 43rd enum declared in HttpStatus. The request hangs because 43 is an invalid status code and your browser does not know how to deal with it. You should use:
response.setStatus(HttpStatus.I_AM_A_TEAPOT.value()); // status 418

Categories