How to pass request parameters as-is between REST service calls in a Spring Boot services application? - spring

We are doing an architectural refactoring to convert a monolithic J2EE EJB application to Spring services. In order to do that I'm creating services by breaking the application against the joints of its domain. Currently, I have three of them and each calls another service via Rest.
In this project our ultimate purpose is transforming the application to microservices, but since cloud infrastructure isn't clear and probably won't be possible, we decided to make it this way and thought that since services using Rest, it will be easy to make the transform in future.
Does our approach makes sense? My question stems from this.
I send a request to UserService with a header parameter, userName from Postman.
GET http://localhost:8087/users/userId?userName=12345
UserService calls another service which calls another. Rest call order between services is this:
UserService ---REST--> CustomerService ---REST--> AlarmService
Since I'm doing the work of carrying the common request parameters like this right now, I need to set common header parameters in every method that making Rest requests by taking them from incoming request to outgoing request:
#RequestMapping(value="/users/userId", method = RequestMethod.GET)
public ResponseEntity<Long> getUserId(#RequestHeader("userName") String userName) {
...
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Collections.singletonList
(MediaType.APPLICATION_JSON));
headers.set("userName", userName);
HttpEntity<String> entity = new HttpEntity<>("parameters", headers);
HttpEntity<Long> response =
restTemplate.exchange(CUSTOMER_REST_SERVICE_URI,
HttpMethod.GET, entity, Long.class);
...
}
UserService:
package com.xxx.userservice.impl;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.*;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import java.util.Collections;
import java.util.Map;
#RestController
public class UserController extends AbstractService{
Logger logger = Logger.getLogger(UserController.class.getName());
#Autowired
private RestTemplate restTemplate;
private final String CUSTOMER_REST_SERVICE_HOST = "http://localhost:8085";
private final String CUSTOMER_REST_SERVICE_URI = CUSTOMER_REST_SERVICE_HOST + "/customers/userId";
#RequestMapping(value="/users/userId", method = RequestMethod.GET)
public ResponseEntity<Long> getUserId(#RequestHeader("userName") String userName) {
logger.info(""user service is calling customer service..."");
try {
//do the internal customer service logic
//call other service.
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Collections.singletonList
(MediaType.APPLICATION_JSON));
headers.set("userName", userName);
HttpEntity<String> entity = new HttpEntity<>("parameters", headers);
HttpEntity<Long> response =
restTemplate.exchange(CUSTOMER_REST_SERVICE_URI,
HttpMethod.GET, entity, Long.class);
return ResponseEntity.ok(response.getBody());
} catch (Exception e) {
logger.error("user service could not call customer service: ", e);
throw new RuntimeException(e);
}
finally {
logger.info("customer service called...");
}
}
}
CustomerService:
package com.xxxx.customerservice.impl;
import java.rmi.RemoteException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import com.xxx.interf.CustomerService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
#RestController
public class CustomerController extends AbstractService{
private final String ALARM_REST_SERVICE_HOST = "http://localhost:8086";
private final String ALARM_REST_SERVICE_URI = ALARM_REST_SERVICE_HOST + "/alarms/maxAlarmCount";
#Autowired
private CustomerService customerService;
#Autowired
private RestTemplate restTemplate;
...
#GetMapping(path="/customers/userId", produces = "application/json")
public long getUserId(#RequestHeader(value="Accept") String acceptType) throws RemoteException {
//customer service internal logic.
customerService.getUserId();
//customer service calling alarm service.
return restTemplate.getForObject(ALARM_REST_SERVICE_URI, Long.class);
}
}
AlarmService:
package com.xxx.alarmservice.impl;
import com.xxx.interf.AlarmService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
#RestController
public class PriceAlarmController extends AbstractService{
#Autowired
private AlarmService priceAlarmService;
#RequestMapping("/alarms/maxAlarmCount")
public long getMaxAlarmsPerUser() {
// alarm service internal logic.
return priceAlarmService.getMaxAlarmsPerUser();
}
}
I have tried these config and interceptor files but i can use them just for logging and can't transfer header parameters by using them. Probably because each service has them. And also, this interceptor only works in UserService which first uses RestTemplate to send request. Called service and first request which is coming from Postman doesn't work with it because they doesn't print any log message like UserService does.
CommonModule:
package com.xxx.common.config;
import com.xxx.common.util.HeaderRequestInterceptor;
import org.apache.cxf.common.util.CollectionUtils;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.web.client.RestTemplate;
import java.util.ArrayList;
import java.util.List;
#Configuration
public class RestTemplateConfig {
#Bean
public RestTemplate restTemplate() {
RestTemplate restTemplate = new RestTemplate();
List<ClientHttpRequestInterceptor> interceptors
= restTemplate.getInterceptors();
if (CollectionUtils.isEmpty(interceptors)) {
interceptors = new ArrayList<>();
}
interceptors.add(new HeaderRequestInterceptor());
restTemplate.setInterceptors(interceptors);
return restTemplate;
}
}
ClientHttpRequestInterceptor:
package com.xxx.common.util;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpRequest;
import org.springframework.http.MediaType;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.util.StreamUtils;
import java.io.IOException;
import java.nio.charset.Charset;
public class HeaderRequestInterceptor implements ClientHttpRequestInterceptor {
private final Logger log = LoggerFactory.getLogger(this.getClass());
#Override
public ClientHttpResponse intercept(
HttpRequest request,
byte[] body,
ClientHttpRequestExecution execution) throws IOException
{
log.info("HeaderRequestInterceptor....");
logRequest(request, body);
request.getHeaders().set("Accept", MediaType.APPLICATION_JSON_VALUE);
ClientHttpResponse response = execution.execute(request, body);
logResponse(response);
return response;
}
private void logRequest(HttpRequest request, byte[] body) throws IOException
{
log.info("==========request begin=======================");
}
private void logResponse(ClientHttpResponse response) throws IOException
{
log.info("==========response begin=============");
}
}
How can I manage the passing of common header information like userName by using some kind of interceptors or other mechanism in single place?

In your HeaderRequestInterceptor's intercept method, you can access the current http request and its headers (userId in your case) in the following way:
#Override
public ClientHttpResponse intercept(HttpRequest request..
...
HttpServletRequest httpServletRequest = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
String userId = httpServletRequest.getHeader("userId");
request.getHeaders().set("userId", userId);

Related

Spring Boot TestRestTemplate return null body

Havin a quite very simple test class followed by a video tut:
#SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class TestClassTest {
#LocalServerPort
private int port;
#Autowired
private TestRestTemplate testRestTemmplate;
#Test
public void test_one() {
String url = "http://localhost:" + port + "/v1/read";
UriComponents builder = UriComponentsBuilder.fromHttpUrl(url).build();
HttpHeaders headers = new HttpHeaders();
headers.set("accept", "application/json");
HttpEntity<String> requestEntity = new HttpEntity<>(null, headers);
ResponseEntity<String> response = testRestTemmplate.exchange(builder.toString(), HttpMethod.GET, requestEntity, String.class);
System.out.println(response.getBody());
assertEquals(HttpStatus.OK, response.getStatusCode());
}
}
The underlying controller class for test looks like:
#RestController
public class TestController {
#RequestMapping(value = "/v1/read", method = RequestMethod.GET, produces = "application/json")
public ResponseEntity<String> data() {
return ResponseEntity.status(HttpStatus.OK).header("status", "completed").body("fertig!");
}
}
Some issues i wondering with:
Running the test will evaluate to green thats fine but if i change the url in the test it will be also OK
The body from response is null and
the header i set within the ResponseEntity is no part of the response from testResttemplate.
Where are my misunderstandings?
Edit: here are my imports:
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.boot.test.web.server.LocalServerPort;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.web.util.UriComponents;
import org.springframework.web.util.UriComponentsBuilder;
As seen this are only the "standard" import coming from spring dependencies. Nothing exotics.

Spring Boot: "relaying" basic auth from REST controller to RestTemplate

I'm working with two Spring Boot applications, let's call them ServiceA and ServiceB, both exposing a REST API.
ServiceA is called by end users from the browser via a frontend app (we use #RestController classes). On some calls, ServiceA has to call ServiceB (using RestTemplate). We've got authentication and authorization sorted out for our target environment, but for testing locally we are relying on Basic Auth instead, and that's where we're hitting a snag: we would like ServiceA to re-use the Basic Auth credentials the user provided when calling Service B.
Is there an easy way to pass the Basic Auth credentials used on the call to our REST controller to the RestTemplate call?
Quick and dirty solution
The easiest way to do this would be:
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
#RestController
class SomeController {
private final RestTemplate restTemplate = new RestTemplate();
#PostMapping("/delegate/call")
public void callOtherService(#RequestHeader(HttpHeaders.AUTHORIZATION) String authorization) {
HttpHeaders headers = new HttpHeaders();
headers.add(HttpHeaders.AUTHORIZATION, authorization);
restTemplate.postForEntity("other-service.com/actual/call", new HttpEntity<Void>(null, headers), Void.class);
// handling the response etc...
}
}
Using interceptors and RestTemplateCustomizer
I didn't want to change to add an extra parameter on each controller method, and I wanted a way to enable or disable this behavior depending on the environment, so here is a slightly more complicated solution that can be enabled using Spring profiles, and doesn't touch the controllers:
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpRequest;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class BasicAuthPropagationInterceptor implements HandlerInterceptor, ClientHttpRequestInterceptor {
private final ThreadLocal<String> cachedHeader = new ThreadLocal<>();
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
final String header = request.getHeader(HttpHeaders.AUTHORIZATION);
cachedHeader.set(header);
return true;
}
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
cachedHeader.remove();
}
#Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
String ch = cachedHeader.get();
if (!request.getHeaders().containsKey(HttpHeaders.AUTHORIZATION) && ch != null) {
request.getHeaders().add(HttpHeaders.AUTHORIZATION, ch);
}
return execution.execute(request, body);
}
}
This stores the received header in a ThreadLocal and adds it with an interceptor for RestTemplate.
This can then be configured as such:
import org.springframework.boot.web.client.RestTemplateCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
#Configuration
#Profile("LOCAL")
class LocalConfiguration implements WebMvcConfigurer {
private final BasicAuthPropagationInterceptor basicAuthPropagationInterceptor
= new BasicAuthPropagationInterceptor();
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(basicAuthPropagationInterceptor);
}
#Bean
RestTemplateCustomizer restTemplateCustomizer() {
return restTemplate -> restTemplate.getInterceptors().add(basicAuthPropagationInterceptor);
}
}
RestTemplate obtained by using the default RestTemplateBuilder bean will then automatically set the Authorization HTTP header if it's available in the current thread.

overridden handleMethodArgumentNotValid method of ResponseEntityExceptionHandler not called

I am trying to have a custom validator and also an ExceptionHandler for my spring boot rest service and when I added ExceptionHandler, the validation errors are not being sent to the UI. So I tried to override handleMethodArgumentNotValid method and that does not work either. Can someone give some insight into this?
This is how I have configured my validation class in the controller -
package services.rest.controller;
import java.io.IOException;
import javax.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import lombok.extern.slf4j.Slf4j;
import services.rest.model.TestInput;
import services.rest.validator.DataValidator;
#RestController
#RequestMapping("/test")
#Slf4j
public class RestResource {
#Autowired
private DataValidator validator;
#PostMapping("/create")
public String create(#Valid final TestInput input) throws IOException {
log.debug(input.toString());
return "Success";
}
#InitBinder()
public void init(final WebDataBinder binder) {
binder.addValidators(validator);
}
}
This is my ExceptionHandler code -
package services.rest.exceptionhandler;
import java.util.ArrayList;
import java.util.List;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
#SuppressWarnings({ "unchecked", "rawtypes" })
#ControllerAdvice
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {
#ExceptionHandler(Exception.class)
public final ResponseEntity<Object> handleAllExceptions(final Exception ex, final WebRequest request) {
System.out.println("All exceptions Method getting executed!!!!");
final List<String> details = new ArrayList<>();
details.add(ex.getLocalizedMessage());
return new ResponseEntity("Server Error", HttpStatus.INTERNAL_SERVER_ERROR);
}
#Override
protected ResponseEntity<Object> handleMethodArgumentNotValid(final MethodArgumentNotValidException ex,
final HttpHeaders headers, final HttpStatus status, final WebRequest request) {
System.out.println("Validation Error Method getting executed!!!!");
final List<String> details = new ArrayList<>();
for (final ObjectError error : ex.getBindingResult().getAllErrors()) {
details.add(error.getDefaultMessage());
}
return new ResponseEntity("Validation Error", HttpStatus.BAD_REQUEST);
}
}
Initially did not override "handleMethodArgumentNotValid" method. Now after overriding it too, it does not work
Did you check the stack trace, it can be possible that instead of MethodArgumentNotValid exception, ConstraintViolation exception is getting raised. Spring doen not provide any default handler for that.
I tested your example and seems to work. Would be helpful if you would also post TestInput and DataValidator.
Doesn't work doesn't say precisely what happened, my guess is that you just received a 400 status code. If that is the case it might be just because the validation is trigger before but you did not override ResponseEntity<Object> handleBindException(final BindException ex, final HttpHeaders headers, final HttpStatus status, final WebRequest request)
The following approach worked for me:
import java.util.ArrayList;
import java.util.List;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.context.request.WebRequest;
#Order(Ordered.HIGHEST_PRECEDENCE)
#ControllerAdvice
public class GlobalExceptionHandler {
#ExceptionHandler(Exception.class)
public ResponseEntity<Object> handleAllExceptions(final Exception ex, final WebRequest request) {
System.out.println("All exceptions Method getting executed!!!!");
final List<String> details = new ArrayList<>();
details.add(ex.getLocalizedMessage());
return new ResponseEntity("Server Error", HttpStatus.INTERNAL_SERVER_ERROR);
}
#ExceptionHandler(MissingServletRequestParameterException.class)
protected ResponseEntity<Object> handleMethodArgumentNotValid(final Exception ex, final WebRequest request) {
System.out.println("Validation Error Method getting executed!!!!");
final List<String> details = new ArrayList<>();
details.add(ex.getLocalizedMessage());
return new ResponseEntity("Validation Error", HttpStatus.BAD_REQUEST);
}
}
Basically what I did was:
Not extending from ResponseEntityExceptionHandler class.
Put the #Order(Ordered.HIGHEST_PRECEDENCE).
Create a handler for the exception MissingServletRequestParameterException.
Hope this help you
Define your exception handler pakcage in #ComponentScan in App class.
#SpringBootApplication
#ComponentScan(basePackages = { "services.rest.exceptionhandler" })
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}

Unable to Mock RestTemplate.exchange

As part of TDD i want to be able to test every portion of my SpringBoot rest application. However i am unable to mock external calls.
Application structure
1. Few rest endpoints which internally call external rest endpoints.
2. All calls to external endpoints are orchestrated through a local http client which utilizes RestTemplate as httpClient.
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = WebEnvironment.MOCK, classes = TestDrivenDevelopmentWithJavaApplication.class)
public class TestDrivenDevelopmentWithJavaApplicationTests {
#Autowired
private WebApplicationContext webApplicationContext;
private MockMvc mockMvc;
#MockBean
private RestTemplate client;
#Before
public void setup() throws Exception {
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
Structure1Root category = new Structure1Root();
Category cat = new Category();
cat.setCategoryName("Test1");
cat.setDescription("Test");
category.setD(cat);
Mockito.when(client.exchange(
ArgumentMatchers.eq("https://services.odata.org/V2/Northwind/Northwind.svc/Products(1)?$format=json"),
ArgumentMatchers.eq(HttpMethod.GET), ArgumentMatchers.eq(null),
ArgumentMatchers.eq(Structure1Root.class)))
.thenReturn(new ResponseEntity<Structure1Root>(category, HttpStatus.OK));
}
#Test
public void testendpoint1() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.get("/api/endpoint1?token=1").contentType(MediaType.APPLICATION_JSON))
.andExpect(content().string(org.hamcrest.Matchers.containsString("Test1")));
}
}
Even though i have setup the mock on client.exchange(RestTemplate.exchange), i see response returned by client.exchange is null and not the response specified in thenReturn
Controller Code
#RestController
#RequestMapping(path = Endpoint.base)
public class Endpoint {
public static final String base = "/api";
#Autowired
MyHttpClient<Structure2Root> client;
#Autowired
MyHttpClient<Structure1Root> Cclient;
#GetMapping(path = "/endpoint1")
public ResponseEntity<Structure2Root> callEndpt1(#RequestParam String token) {
Response<Structure2Root> resp = client
.execute("https://services.odata.org/V2/Northwind/Northwind.svc/Products(1)?$format=json", Structure2Root.class);
return new ResponseEntity<Structure2Root>(resp.getResponse(), HttpStatus.OK);
}
#GetMapping(path = "/endpoint2")
public ResponseEntity<Structure1Root> callEndpt2(#RequestParam String token) {
Response<Structure1Root> resp = Cclient.execute(
"https://services.odata.org/V2/Northwind/Northwind.svc/Categories(1)?$format=json", Structure1Root.class);
return new ResponseEntity<Structure1Root>(resp.getResponse(),HttpStatus.OK);
}
}
And finally, local http client code
#Service
public class MyHttpClient<O> {
#Autowired
RestTemplate client;
public MyHttpClient() {
// TODO Auto-generated constructor stub
}
public Response<O> execute(String url, Class<O> generic) {
ResponseEntity<O> resp = client.exchange(url, HttpMethod.GET, null, generic);
return new Response<O>(resp.getStatusCode(), resp.getBody());
}
}
this client.execute is what i intend to intercept in the first code block
However never seems to work and always returns a null.
The Git Repo
Regards,
Veera
You have used the wrong object while mocking. You should be using Structure2Root rather then Structure1Root
The correct test class is below which is working perfectly fine.
package com.demo.samples.tdd;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import com.demo.samples.tdd.responses.Product;
import com.demo.samples.tdd.responses.Structure2Root;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentMatchers;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.context.WebApplicationContext;
import com.demo.samples.tdd.responses.Category;
import com.demo.samples.tdd.responses.Structure1Root;
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = WebEnvironment.MOCK, classes = TestDrivenDevelopmentWithJavaApplication.class)
public class TestDrivenDevelopmentWithJavaApplicationTests {
#Autowired
private WebApplicationContext webApplicationContext;
private MockMvc mockMvc;
#MockBean
private RestTemplate client;
#Before
public void setup() throws Exception {
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
// Structure1Root category = new Structure1Root();
// Category cat = new Category();
// cat.setCategoryName("Test1");
// cat.setDescription("Test");
// category.setD(cat);
//
// Mockito.when(client.exchange(
// ArgumentMatchers.eq("https://services.odata.org/V2/Northwind/Northwind.svc/Products(1)?$format=json"),
// ArgumentMatchers.eq(HttpMethod.GET), ArgumentMatchers.eq(null),
// ArgumentMatchers.eq(Structure1Root.class)))
// .thenReturn(new ResponseEntity<Structure1Root>(category, HttpStatus.OK));
Structure2Root category2 = new Structure2Root();
Product product = new Product();
product.setProductName("Test1");
product.setUnitPrice("1");
category2.setD(product);
Mockito.when(client.exchange(
ArgumentMatchers.eq("https://services.odata.org/V2/Northwind/Northwind.svc/Products(1)?$format=json"),
ArgumentMatchers.eq(HttpMethod.GET), ArgumentMatchers.eq(null),
ArgumentMatchers.eq(Structure2Root.class)))
.thenReturn(new ResponseEntity<Structure2Root>(category2, HttpStatus.OK));
}
#Test
public void testendpoint1() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.get("/api/endpoint1?token=1").contentType(MediaType.APPLICATION_JSON))
.andExpect(content().string(org.hamcrest.Matchers.containsString("Test1")));
}
}

How do I read the post data in a Spring Boot Controller?

I would like to read POST data from a Spring Boot controller.
I have tried all the solutions given here: HttpServletRequest get JSON POST data, but I still am unable to read post data in a Spring Boot servlet.
My code is here:
package com.testmockmvc.testrequest.controller;
import org.apache.commons.io.IOUtils;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
#Controller
public class TestRequestController {
#RequestMapping(path = "/testrequest")
#ResponseBody
public String testGetRequest(HttpServletRequest request) throws IOException {
final byte[] requestContent;
requestContent = IOUtils.toByteArray(request.getReader());
return new String(requestContent, StandardCharsets.UTF_8);
}
}
I have tried using the Collectors as an alternative, and that does not work either. What am I doing wrong?
First, you need to define the RequestMethod as POST.
Second, you can define a #RequestBody annotation in the String parameter
#Controller
public class TestRequestController {
#RequestMapping(path = "/testrequest", method = RequestMethod.POST)
public String testGetRequest(#RequestBody String request) throws IOException {
final byte[] requestContent;
requestContent = IOUtils.toByteArray(request.getReader());
return new String(requestContent, StandardCharsets.UTF_8);
}
}

Resources