Spring web application configuration contains Jackson ObjectMapper configured like this
objectMapper.disable(ADJUST_DATES_TO_CONTEXT_TIME_ZONE)
objectMapper.registerModule(new JavaTimeModule())
JavaTimeModule is added to handle deserialisation of ZonedDateTime. There are two endpoint which handle a POJO which contains ZonedDateTime. The POJO is like this:
class MyRequest {
ZonedDateTime from
ZonedDateTime to
}
and controller with endpoints is:
#Slf4j
#RestController
class MyController {
#GetMapping('/pojo')
void getPojo(MyRequest myRequest) {
log.debug("Request received: $myRequest")
}
#PostMapping('/pojo')
void postPojo(#RequestBody MyRequest myRequest) {
log.debug("Request received: $myRequest")
}
}
When I send POST /pojo with body
{"from": "2017-03-15T00:00:00Z", "to": "2017-03-16T00:00:00Z"}
The response is 200 and deserialisation is successful.
Contrary, when I send
GET /pojo?from=2017-03-15T00:00:00Z&to=2017-03-15T00:00:00Z
The 400 Bad Request is received with error
Failed to convert from type [java.lang.String] to type [java.time.ZonedDateTime] for value '2017-03-15T00:00:00Z'
This make sense, since in GET request, I'm not sending JSON and therefore JSON object mapper is not called.
Is there a way to use objectMapper for GET requests also, so query parameters are converted into POJO object?
By the way, I know that it can be deserialised for GET endpoint like below, but I want to use same converter for GET and POST endpoint
#DateTimeFormat(iso = ISO.DATE_TIME)
ZonedDateTime from
#DateTimeFormat(iso = ISO.DATE_TIME)
ZonedDateTime to
Injecting objectMapper and converting query parameters map into object solves the problem
#Slf4j
#RestController
class MyController {
#Autowired
private ObjectMapper objectMapper
#GetMapping('/pojo')
void getPojo(#RequestParam Map<String, String> allRequestParams) {
MyRequest request = objectMapper.convertValue(allRequestParams, MyRequest)
log.debug("Request received: $myRequest")
}
...
Related
I do not know why the MvcResult returns an empty JSON for a GET request when the httpStatus is Ok. Moreover, through Postman I can tell that the data is indeed there and should be retrievable. I want to use the JSON data and map it to objects using the ObjectMapper's readValue() method. But in order to do that, I obviously need to be able to retrieve it first... help
The test does not fail, but when I try to System.out.println(json) - there is an empty line in the console where presumably the printed JSON should be. (I do not have the same issue when doing unit tests.)
`#SpringBootTest
#AutoConfigureMockMvc (addFilters = false) //in order to disable security: error 401
public class IntegrationTests {
#Autowired
private MockMvc mockMvc;
#Autowired
private ObjectMapper objectMapper;
#Test
public void test() {
String url = "/url";
MvcResult result = mockMvc.perform(MockMvcRequestBuilders.get(url))
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(MockMvcResultMatchers.content().contentType(MediaType.APPLICATION_JSON))
.andReturn();
String json = result.getResponse().getContentAsString();
System.out.println(json);
}`
I am trying to access httpServletRequest inside a component class. I tried it in several ways.
#Component
public class MyService{
#Resource
WebServiceContext wsCtxt;
public void myWebMethod(){
MessageContext msgCtxt = wsCtxt.getMessageContext();
HttpServletRequest req = (
(HttpServletRequest)msgCtxt.get(MessageContext.SERVLET_REQUEST);
String clientIP = req.getRemoteAddr();
}
This didn't work for me. because WebServiceContext is always null. Then I tried same code inside Web service class. Then that code is working. My Requirement it to get HttpServletRequest inside component class. (ultimately What i am trying to do it get client host from request header).
It this possible to do ?. Are there any alternatives for this ?
Method #1
Have you tried passing the request object into your component by passing it in as an argument to your service method, and from your service to your component method?
// in your controller... Spring provides the request object
public String myController(HttpServletRequest request, ...) {
//...
myService.myServiceMethod(request,...);
}
// in your service...
public void myServiceMethod(HttpServletRequest request, ...) {
//...
myComponent.myWebMethod(request,...);
}
// in your component
public String myWebMethod(HttpServletRequest request, ...) {
// use the raw request object
}
Method #2
Also, DispatcherServlet exposes the request object by wrapping it in a ServletRequestAttributes object, which in turn is stored in a ThreadLocal variable. The actual storing takes place in RequestContextHolder and its static methods. You can access it as follows:
public void myWebMethod(){
//...
RequestAttributes reqAttr = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes servlReqAttr = (ServletRequestAttributes)reqAttr;
HttpServletRequest req = servlReqAttr.getRequest();
//...
}
Although a little verbose, you can see what's going on.
You could also condense it:
((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest();
I hope this helps!
I am trying to test my controller endpoint and my requestbody annotated with #Valid annotation. My Testclass looks like the follow:
#RunWith(SpringRunner.class)
#WebMvcTest(value = BalanceInquiryController.class, secure = false)
public class BalanceInquiryControllerTest {
#Autowired
private MockMvc mockMvc;
#MockBean
private BalanceInquiryController balanceInquiryController;
#Test
public void testGetBalanceInquiry() throws Exception {
RequestBuilder requestBuilder = MockMvcRequestBuilders
.post("/com/balanceInquiry")
.accept(MediaType.APPLICATION_JSON)
.content("{\"comGiftCard\":{\"cardNumber\":\"1234567890\",\"pinNumber\":\"0123\"},\"comMerchant\":\"MERCHANT1\"}")
.contentType(MediaType.APPLICATION_JSON);
MvcResult mvcResult = mockMvc.perform(requestBuilder).andReturn();
MockHttpServletResponse response = mvcResult.getResponse();
assertEquals(HttpStatus.OK.value(), response.getStatus());
}
}
My Controller - #PostMapping looks like that:
#PostMapping(value = "/com/balanceInquiry")
public ResponseEntity<?> getBalanceInquiry(#Valid #RequestBody BalanceInquiryModel balanceInquiry, Errors errors) {
if (errors.hasErrors()) {
return new ResponseEntity<String>("Validation error", HttpStatus.BAD_REQUEST);
}
//do any stuff...
return new ResponseEntity<BalanceInquiryResponse>(balanceInquiryResponse, HttpStatus.OK);
}
My BalanceInquiryModel is annotated with #Valid and has some hibernate and custom validations behind. Those validations are all ok and already unit tested.
What I like to test is my endpoint where I send a valid json request body expecting a 200 response and also an invalid json request body expecting a 400 response validated by the set #Valid implementation.
For example an unvalid call is to send no pinNumber or length < 4.
I have read some threads and some uses MockMvcBuilders.standaloneSetup() to mock the full controller. But I wont do a full integration test.
Not quite sure how to go on with this situation and if I should go on.
P.S.: At the moment I get always a 200 response no matter if the validation should give an error or not.
Here a gist for more code and the validation classes/models.
Here's one of my example I work on my project
hope it help you out:
I have a global exception handler to handler my MethodArgumentNotValidException and throw it
#RequestMapping(value = "/add", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<User> createUser(#Valid #RequestBody User user) {
User savedUser = userService.save(user);
return new ResponseEntity<User>(savedUser, HttpStatus.CREATED);
}
public void testAdduser() throws Exception{
final User request = new User();
request.setFirstName("Test");
request.setLastName("some description");
mockMvc.perform(post(END_POINT+"/add")
.contentType(MediaType.APPLICATION_JSON)
.content(stringify(request))
).andDo(print()).andExpect(status().isUnprocessableEntity())
;
}
private String stringify(Object object) throws JsonProcessingException {
return new ObjectMapper().writeValueAsString(object);
}
Update:
I think your main problem is that you are using #WebMvcTest in stead of #SpringBootTest.
the different between 2 of them is that:
#SpringBootTest annotation will loads complete application and injects all the beans which is can be slow.
#WebMvcTest - for testing the controller layer. it doesn't inject other bean beside the #RestController
so if you are just testing just pure controller to see u can reach the endpont then you can just use #WebMvcTest which will make your test run faster.
but in your case, you want it to run the spring validation, you will need to use #SpringBootTest
for detailed: https://spring.io/guides/gs/testing-web/
In our api we are using spring Jackson http message converter to automatically convert java object to json. I am enjoying that feature,but what I personally feel is that I've lost control over the response http status code.if I want to return the response with different status codes ,I have the choice of using #responsestatus(httpstatus),but I cannot specify the status dynamically,as annotation is expecting a enum const expression. The other choice is http server response.set status(),but I don't like that.spring's responseentity(jsonstring,statuscode) is a great thing to solve but if I want to use Jackson httpmessageconverter is any way to configure the response status code dynamically.
You can return ResponseEntity<MyObject> from your controller method and it will still use the configured message converters, example:
#RestController
#RequestMapping("/foo")
public class FooController {
#RequestMapping
public ResponseEntity<MyObject> foo() {
MyObject myObject = new MyObject();
// You can dynamically set the status based on your needs
HttpStatus status = HttpStatus.OK;
return new ResponseEntity<>(myObject, status);
}
}
I am writing a REST based web service. I need to return all the responses as JSON format. I have an interceptor to validate my authentication parameters. On authentication failure scenario, I have to return the error response in JSON format.
Currently i am doing
response.setHeader("Content-Type","application/json");
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "{\"error\":\"Missing Authentication Parameters\"}");
The response body is coming as below.
JBoss Web/2.1.3.GA - Error report HTTP Status 401 - {"error":"Missing Authentication Parameters"}type Status reportmessage {"error":"Missing Authentication Parameters"}description This request requires HTTP authentication ({"error":"Missing Authentication Parameters"}).JBoss Web/2.1.3.GA
I need just the JSON string in response. Please help me.
You should probably be using spring-security for this. If you want to do it by hand, an alternative to using sendError on the response is to use spring MVC's #ExceptionHandler along with content negotiation to return JSON.
First define an error class*:
public class Error {
public message;
public exception;
public Error(String message, Exception ex) {
this.message = message;
this.exception = ex;
}
}
And an exception:
public class NotAuthenticatedException extends Exception {
// ...
}
Then in your controller you throw an exception at the appropriate time, catch it with #ExceptionHandler and return a ResponseEntity containing an Error instance and the appropriate error code.
#Controller
public class SimpleController {
#RequestMapping(...)
public String aMethod() {
// ...
throw new NotAuthenticatedException("Missing Authentication Parameters");
}
#ExceptionHandler(NotAuthenticatedException.class)
public ResponseEntity<Error> handleNotAuthenticatedException(
NotAuthenticatedException ex,
HttpServletRequest request) {
return new ResponseEntity<Error>(
new Error(ex.getMessage(), ex),
HttpStatus.UNAUTHORIZED
);
}
}
*use getters/setters to please the java convention gods