I am new to testing world of spring boot application.
I want to do an integration testing on my Spring boot application. But i am getting following exception.
org.springframework.web.client.HttpClientErrorException$BadRequest: 400 null
I am adding an employee and department into the in-memory database with bi-directional relationship.
public void testPostEmployee() throws Exception
{
System.out.println("Inside Post Employee method");
httpHeaders.setContentType(MediaType.APPLICATION_JSON);
EmployeeTestDTO employeeTestDTO = new EmployeeTestDTO();
DepartmentTest departmentTest = new DepartmentTest(1,"Sales");
employeeTestDTO.setName("ABC");
employeeTestDTO.setAge(20);
employeeTestDTO.setSalary(1200.1);
employeeTestDTO.setDepartmentTest(departmentTest);
ObjectMapper objectMapper = new ObjectMapper();
String data = objectMapper.writeValueAsString(employeeTestDTO);
System.out.println(data);
HttpEntity<String> httpEntity = new HttpEntity<String>(data,httpHeaders);
ResponseEntity<?> postResponse = restTemplate.postForEntity(createURLWithPort("/employee"),
httpEntity,String.class);
Assert.assertEquals(201, postResponse.getStatusCodeValue());
}
This is my new edit. As per previously stated suggestion i tried to implement all of them but neither of them succeded. It gives the bad request 400 null exception. Please suggest me how to solve it
You should change ContentType from APPLICATION_FORM_URLENCODED to APPLICATION_JSON.
httpHeaders.setContentType(MediaType.APPLICATION_JSON);
Also you need to add RestController:
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;
#RestController
public class Controller {
#PostMapping("/employee")
#ResponseStatus(HttpStatus.CREATED)
public void getEmploee(#RequestBody EmployeeTestDTO employee) {
System.out.println(employee);
}
}
Related
I am trying to send a body in a post request in a springboot application using rest template. Here is the controller:(I removed #RequestBody because I used application/x-www-form-urlencoded header)
#RestController
#CrossOrigin
#RequestMapping("/api")
public class SentimentParserController {
#Autowired
private SentimentParserService sentimentParserService;
#RequestMapping(value = "/something", method = RequestMethod.POST, consumes="application/x-www-form-urlencoded")
public ResponseEntity<mcResponse>getTheSentiments( mcSentimentRequestDTO sentimentRequestDTO){
return sentimentParserService.getSentimentsMc(sentimentRequestDTO);
}
}
I want to send the sentimentRequestDTO object(lang, key, and text) as the body in a post request to get the mcResponse:
public mcResponse parseTheSentiments(String text, Languages lang, String key) throws Exception {
RestTemplate restTemplate = new RestTemplate();
String request = "http://localhost:8080";
mcSentimentRequestDTO mSentiments =new mcSentimentRequestDTO(key,"EN",text);
HttpHeaders headers = new HttpHeaders();
headers.add("content-type", "application/x-www-form-urlencoded");
MultiValueMap<String, String> map= new LinkedMultiValueMap<String, String>();
map.add("key", key);
map.add("txt", text);
map.add("lang", Languages.ENGLISH.toString());
HttpEntity<MultiValueMap<String, String>> request1 = new HttpEntity<MultiValueMap<String, String>>(map, headers);
mcResponse response = restTemplate.postForObject(request, request1 , mcResponse.class );
return response;
}
However, I am getting the following error: 404 null.
Can you please help me? Thanks in advance
and here is the service class:
public ResponseEntity<mcResponse> getSentimentsMc(mcSentimentRequestDTO sentimentRequestDTO){
ResponseEntity<mcResponse> dto = null;
try {
dto = sentimentConverter.getTheSentiments(mcsParser.parseTheSentiments(sentimentRequestDTO.getText(),
Languages.ENGLISH, sentimentRequestDTO.getKey()));
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return dto;
}
Looks like variable request should be
String request = "http://localhost:8080/something";
Also if controller class has prefix, this prefix also should be in request.
I mean if your class looks like this
#RestController
#RequestMapping("/myApi")
public class CertificateController {
....
#RequestMapping(value = "/something", method = RequestMethod.POST)
public ResponseEntity<mcResponse>getTheSentiments( mcSentimentRequestDTO sentimentRequestDTO){
return sentimentParserService.getSentimentsMc(sentimentRequestDTO);
}
Then request should be
String request = "http://localhost:8080/myApi/something";
It sounds like the controller isn't getting included in the spring context. If you just have an app annotated with #SpringBootApplication, then make sure that your controller is in a package that is the same as or lower than your annotated application.
To check the controller is being picked up you can add the following logging options to your application.properties
logging.level.org.springframework.beans=debug
logging.level.org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping=trace
When your server starts up you should see something like the following in the log
1. To show the controller is in the spring-context
DefaultListableBeanFactory : Creating shared instance of singleton bean 'sentimentParserController'
2. To show the mapping for the /api/something url
RequestMappingHandlerMapping : Mapped 1 handler method(s) for class SentimentParserController: {public org.springframework.http.ResponseEntity SentimentParserController.getTheSentiments(mcSentimentRequestDTO)={[/api/something],methods=[POST]}}
If you see both of these, then what you say you're doing should work. Just make sure you are sending the request to /api/something and the server is running on port 8080.
I have a spring boot application that needs a controller that can handle the following request:
The request is sent by another service through the Post method..
Headers
accept-encoding: gzip,deflate
user-agent: Apache-HttpClient/4.3.6 (java 1.5)
connection: Keep-Alive
host: webhook.site
content-type: application/x-www-form-urlencoded
content-length: 558
Query strings:(empty)
Form values
BillNumber: 41492032464
BillValue: 600000.0
Description: Description
I have this controller, but my application returns an HTTP Error 406:
#RequestMapping(value = "/bills", method = RequestMethod.POST, headers = "Accept=application/x-www-form-urlencoded")
#ResponseBody
#Transactional
public void createBill(UriComponentsBuilder uriComponentsBuilder, final HttpServletRequest request,
final HttpServletResponse response) throws IOException {
}
How should this controller be implemented in my spring boot app?
Many Thanks!
It's not very clear for me but if you use spring boot you can of course create a controller , service and repository or dao.
Indeed, your controller will call your service witch will call the repository.
Let's suppose that you have a client witch call your api.
So the call will look like :
// Suppose that is a spring boot project
Class A {
#Autowired
RestTemplate restTemplate;
public void create(){
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
headers.setContentType((MediaType.APPLICATION_JSON));
headers.add("X-yourCustom-context", "yourCustom");
UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(your_service_url)
.queryParam("params1", "value".queryParam("params2", value2));
HttpEntity<?> entity = new HttpEntity<>(headers);
restTemplate.exchange(builder.build().encode().toUri(), HttpMethod.POST, entity, null); // if you want to return an objectr you put it rather than the null
}
}
Th service api :
#RestController
public class YourController {
#Autowired
private YourService service;
#Autowired
private ObjectMapper objectMapper;
#PostMapping(value = "/bills")
//#ResponseBody if you do not return any think you can not use it
// #CrossOrigin if you want to call your reste from an external project like javascript or angular
//#Transactional you can put it on the top of your service methode
public void createBill(#RequestParam(value = "params1", required = true) String params1,
#RequestParam(value = "params2", required = true) String params2,
#RequestHeader(value = "X-yourCustom-context", required = true) String yourContxt) throws IOException {
// You can then convert your x-context to an object that you have already by using objectMapper.readValue
// Finaly you call you service to create the bill and you passe as params what you get fron the client
}
}
I hope it meets your needs :)
Hi I would like to my backend (spring-data-rest) application to generate some sample data and notify frontend. However the repository event handler is REST only so I tried to write a restTemplate but failed.
#Scheduled(fixedRate = 5000)
public void addCounter() throws Exception {
String url = String.format("http://localhost:%d/%s/counters", 8080, api);
Counter counterExpected = new Counter('xxx', random.nextInt(100));
ObjectMapper mapper = new ObjectMapper();
String jsonString = mapper.writeValueAsString(counterExpected);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<String> entity = new HttpEntity<String>(jsonString, headers);
restTemplate.postForObject(url, entity, String.class);
}
Error:
Description:
Field restTemplate in ScheduledTask required a bean of type 'org.springframework.boot.test.web.client.TestRestTemplate' that could not be found.
Action:
Consider defining a bean of type 'org.springframework.boot.test.web.client.TestRestTemplate' in your configuration.
This error makes sense because I am using TestTestTemplate in my runtime application instead of test scope.
My questions are:
Is it possible to change the addCounter() method to something simpler just like:
counterRepository.save(newCounter);
/* Raise AfterCreate event */
If yes, then how?
If not then is there any other way to do a HTTP post instead of using restTemplate?
My bad. I should be using
import org.springframework.web.client.RestTemplate;
instead of
import org.springframework.boot.test.web.client.TestRestTemplate;
I am trying to do DELETE with request body but I keep getting 400 (bad request) error. When I do it in the swagger/postman, it is successfully deleting the record. But from the Java code I can't do that
The external API is designed in a way that it needs body along with URL. It can't be changed. please let me know how can I delete that entry with request body
public Person delete(Person person, String url, Map<String, String> uriVariables) throws JsonProcessingException {
RestTemplate restTemplate = new RestTemplate();
CustomObjectMapper mapper = new CustomObjectMapper();
HttpEntity<Person> requestEntity = new HttpEntity<Person>(person);
try {
ResponseEntity<Person> responseEntity = restTemplate.exchange(url, HttpMethod.DELETE, requestEntity, Person.class, uriVariables);
return responseEntity.getBody();
} catch (RestClientException e) {
System.out.println(mapper.writeValueAsString(person));
throw e;
}
}
when it goes to exception, I will get the JSON request in JSON format and the same works fine in Swagger/postman
I did some googling and found that restTemplate have problem deleting when there is request body. this article wasn't helpful https://jira.spring.io/browse/SPR-12361 is there any way to get it work
Another way to fix this is to use restTemplate.exchange, here's an example:
try {
String jsonPayload = GSON.toJson(request);
HttpEntity<String> entity = new HttpEntity<String>(jsonPayload.toString(),headers());
ResponseEntity resp = restTemplate.exchange(url, HttpMethod.DELETE, entity, String.class);
} catch (HttpClientErrorException e) {
/* Handle error */
}
The nice thing about this solution is that you can use it with all HttpMethod types.
Issue exists for Spring version 4.0.x or earlier.
In later version it has been fixed.
This might be a late answer, but in one of my project I solved this issue via a custom ClientHttpRequestFactory to RestTemplate
If no factory is provided to RestTemplate, it uses default implementation SimpleClientHttpRequestFactory
In SimpleClientHttpRequestFactory class, DELETE method is not allowed with request body.
if ("PUT".equals(httpMethod) || "POST".equals(httpMethod) ||
"PATCH".equals(httpMethod)) {
connection.setDoOutput(true);
}
else {
connection.setDoOutput(false);
}
Just write your own implementation as
import java.io.IOException;
import java.net.HttpURLConnection;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
public class CustomClientHttpRequestFactory extends SimpleClientHttpRequestFactory {
#Override
protected void prepareConnection(HttpURLConnection connection,
String httpMethod) throws IOException {
super.prepareConnection(connection, httpMethod);
if("DELETE".equals(httpMethod)) {
connection.setDoOutput(true);
}
}
}
After that create your RestTemplate object as
RestTemplate template = new RestTemplate(
new CustomClientHttpRequestFactory());
Fix in later versions of (4.1.x or above) SimpleClientHttpRequestFactory class
if ("POST".equals(httpMethod) || "PUT".equals(httpMethod) ||
"PATCH".equals(httpMethod) || "DELETE".equals(httpMethod)) {
connection.setDoOutput(true);
}
else {
connection.setDoOutput(false);
}
This is my scenario: I have two applications, both use Spring MVC. I have a Filter class in one of them and a Controller class in the other one. The requirement is a little weird, when a request is made to the application that contains the filter, this filter should invoke the controller in the another application to "keep alive" the session so I need invoking from filter class a controller class and pass as parameter the JSESSIONID. Any suggestions?
Thanks!
At the end, I achieve my goal using AsyncRestTemplate, the call to the controller from filter was something like this:
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.add("Cookie", "JSESSIONID=" + sessionId);
HttpEntity<String> httpEntity = new HttpEntity<String>(null, httpHeaders);
String controllerUrl = CONTROLLER_URL;
ListenableFuture<ResponseEntity<String>> responseEntityListenableFuture = restTemplate.exchange(controllerUrl, HttpMethod.GET, httpEntity, String.class);
responseEntityListenableFuture.addCallback(new ListenableFutureCallback<ResponseEntity<String>>() {
#Override
public void onSuccess(ResponseEntity<String> stringResponseEntity) {
// Nothing to do
}
#Override
public void onFailure(Throwable throwable) {
// Handle your error
}
});