So I have been doing some testing for a server and I keep getting this error
"java.lang.AssertionError: No value at JSON path "$.id"
This is the code that I have used. Not sure why it's not reading the jSon correctly, as it doesn't do this with all the other code written.
#Test
public void registerSuccessful() throws Exception {
User testUser1 = new User("test.com", "password", "John", "Doe", "JDoe");
testUser1.setId(1);
given(authService.register(testUser1)).willReturn(testUser1);
String requestBody = objectMapper.writeValueAsString(testUser1);
this.mockMvc.perform(post("/auth/register")
.contentType(MediaType.APPLICATION_JSON)
.content(requestBody))
.andExpect(status().is(201)).andDo(print())
.andExpect(MockMvcResultMatchers.jsonPath("$.id", is(1)))
.andExpect(jsonPath("$.email", is("test.com")))
.andExpect(jsonPath("$.password", is(testUser1.getPassword())))
.andExpect(jsonPath("$.firstName", is(testUser1.getFirstName())))
.andExpect(jsonPath("$.lastName", is(testUser1.getLastName())))
.andExpect(jsonPath("$.username", is(testUser1.getUsername())))
.andExpect(jsonPath("$.imageUrl", is(testUser1.getImageUrl())));
}
Here is the AuthController
#PostMapping("/register")
public ResponseEntity<User> register(#RequestBody RegisterRequest registerRequest) {
User created = new User(
registerRequest.getEmail(),
registerRequest.getPassword(),
registerRequest.getFirstName(),
registerRequest.getLastName(),
registerRequest.getUsername());
return ResponseEntity.status(HttpStatus.CREATED).body(authService.register(created));
}
Related
Consider a scenario where I want to create a record in database with the already existing ID. By doing this, we get "500-internal server error". But I want to customize the message returned to "Id already exist, Cannot create record". Below is the my sample existing code:
Controller class :
#PostMapping(value = "/plans")
public ResponseEntity<ResponseSave> savePostPlanDetails(
#Valid #RequestBody(required = true) Plan plan) throws ParseException {
String planId = plan.getPlanId();
Integer createdId = planDataService.savePlan(plan, planId);
ServiceMessage serviceMessage = ServiceMessage.createCreatedServiceMessage();
return new ResponseEntity<>(ResponseSave.createResponseSave(serviceMessage, createdId), HttpStatus.CREATED);
}
Service class :
public Integer savePlan(Plan plan, String planId) throws ParseException {
PlanDao findResponse = planDataRepository.findByPlanId(planId);
if (findResponse != null) {
//This line generate 500 error if create with already existing ID.
throw new IllegalArgumentException(PlanSearchEnum.RECORD_ALREADY_EXIST.getValue());
}
PlanDao planDao1 = requestToResponseMapper.panToPlanDao(plan);
PlanDao saveResponse = planDataRepository.save(planDao1);
return saveResponse.getInternalId();
}
Postman Output :
{
"message": {
"code": "500",
"description": "Unable to process request",
"type": "Internal Server Error"
}
}
As in the above postman response, I want the description to be like : "description": "Id already exist, Cannot create record" instead of the general message as show above. So how to do this ?
You will need to have a handler for the exception:
#ControllerAdvice
#Slf4j
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {
#ExceptionHandler(value = {IllegalArgumentException.class})
public ResponseEntity<Object> handleIllegalArgumentExceptions(Exception exception, WebRequest webRequest) {
HttpStatus errorCode = HttpStatus.INTERNAL_SERVER_ERROR;
return this.handleExceptionInternal(exception, new ErrorInfo(errorCode.value(), "Id already exist, Cannot create record"), new HttpHeaders(), errorCode, webRequest);
}
}
And the model ErrorInfo:
public class ErrorInfo {
private final int code;
private final String description;
}
Finally, you should definitely consider creating your own exception instead of using the generic IllegalArgumentException. You can create something more meaningful for your business case, such as RecordAlreadyExistsException.
This is my controller:
#PostMapping(value = "/endpoint", produces = { APPLICATION_JSON_VALUE, APPLICATION_XML_VALUE})
#ResponseBody
public Result generateResult(#Valid #RequestBody Request request) throws JsonProcessingException {
Result result = new Result();
// some code here
return result;
}
and this is my Request class:
#Data
#NoArgsConstructor
public class Request {
#NotNull
private String name;
private String type = "application/json";
}
the controller produces the correct output based on the Accept header in the request sent by the client. However, I want to send no Accept header and only send the following request:
{
"name": "my name",
"type": "application/xml"
}
Then based on the type the correct format should be output. I tried to add HttpServletResponse response to the parameter list of the controller method and then set the content type like this:
response.setHeader(CONTENT_TYPE, request.geType());
but it always returns json. any idea what else I should do?
I think a standard Spring's ResponseEntity builder give you all needed variety:
return ResponseEntity
.ok(//any object for json structure)
.headers(//any header)
.build();
Instead .ok() you can you any other method (that's http status code)
or
HttpHeaders responseHeaders = new HttpHeaders();
responseHeaders.set("MyHeader", "MyValue");
return new ResponseEntity<String>("Hello World", responseHeaders, HttpStatus.OK);
based on the comments I post this answer which worked for me. I changed my controller method like this:
#PostMapping(value = "/endpoint", produces = { APPLICATION_JSON_VALUE,
APPLICATION_XML_VALUE})
#ResponseBody
public ResponseEntity<Result> generateResult(#Valid #RequestBody Request request)
throws JsonProcessingException {
Result result = new Result();
// some code here
return ResponseEntity.accepted()
.headers(headers)
.body(result);
}
I try some tests on my UserController for updating the email field like this :
#Test
public void testUpdateUserNominal() throws Exception {
savedUser.setEmail(EMAIL_2);
UserDTORequest updatedUserDTORequest = modelMapper.map(savedUser, UserDTORequest.class);
String jsonMessage = gson.toJson(updatedUserDTORequest);
mvc.perform(put(USER_ROUTE)
.contentType(APPLICATION_JSON)
.accept(APPLICATION_JSON)
.content(jsonMessage))
.andExpect(status().isOk())
.andReturn();
assertThat(userService.findAll().size()).isEqualTo(1);
assertThat(userService.findAll().get(0).getEmail()).isEqualTo(EMAIL_2);
}
Here is the code on my controller :
#Override
public ResponseEntity<UserDTOResponse> update(UserDTORequest userDTORequest) throws ParseException {
Optional<User> optUser = userService.findByUri(userDTORequest.getUri());
if(optUser.isEmpty()){
return ResponseEntity
.notFound()
.build();
}
User user = convertToEntity(userDTORequest);
User userUpdated = userService.save(user);
UserDTOResponse userDTOResponse = convertToDto(userUpdated);
return ResponseEntity
.ok(userDTOResponse);
}
The response from the mockMvc is correct : the new email set is the good one.
But on the second assertThat :
assertThat(userService.findAll().get(0).getEmail()).isEqualTo(EMAIL_2);
The email is not the good one, the email is not updated.
What I do wrong ?
Thanks :)
I´m having some issues when returning some errors from a rest WebService.
Making a request with the header {"Accept":"application/octet-stream"}
(the service returns a document ResponseEntity<InputStreamResource> if all the process goes well).
When all the process goes well the document is downloaded fine, but when an error is occurred and the code jumps to the #ControllerAdvice and tries to return a JSON error. Here comes the problem, when trying to return the JSON springs crashes:
org.springframework.web.HttpMediaTypeNotAcceptableException: Could not find acceptable representation
Here is a example of some code:
Controller
#RequestMapping(value = "/test", method = RequestMethod.GET, produces = {MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_OCTET_STREAM_VALUE })
public ResponseEntity<CustomError> test() throws Exception {
throw new Exception();
}
ControllerAdvice
#ControllerAdvice
public class ExceptionHandlerAdvice {
private static final Logger logger = LogManager.getLogger(ExceptionHandlerAdvice.class);
#ExceptionHandler({Exception.class,Throwable.class})
#ResponseBody
public ResponseEntity<CustomError> handleUnhandledException(Exception exception) {
CustomError error = new CustomError(exception.getMessage());
return new ResponseEntity<CustomError>(error, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
CustomError:
public class CustomError {
private String errorDescription;
public CustomError(String errorDescription) {
super();
this.errorDescription = errorDescription;
}
public String getErrorDescription() {
return errorDescription;
}
public void setErrorDescription(String errorDescription) {
this.errorDescription = errorDescription;
}
}
I´ve also tried returning new headers on #controllerAdvice
#ExceptionHandler({Exception.class,Throwable.class})
#ResponseBody
public ResponseEntity<CustomError> handleUnhandledException(Exception exception) {
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
CustomError error = new CustomError(exception.getMessage());
return new ResponseEntity<CustomError>(error,headers, HttpStatus.INTERNAL_SERVER_ERROR);
}
Any idea how can I make this work or ignore Accept header on response?
It´s possible?
Thanks in advance
This exception means your response type not match with your request header. If you are expecting JSON/Stream to be returned, your request header should be {"Accept":"application/octet-stream,application/json"}.
I'm trying to get a string response from my controller but I get the below error:
SyntaxError: Unexpected end of JSON input(…) "Error 200"
When I change the response to a boolean or a different type, it's working ok. The problem is when I try to return a string.
js code:
$.ajax({
method: "POST",
url: "./signup",
data: _data,
dataType: "json",
contentType : "application/json;charset=UTF-8",
success : function(data) {
console.log(data)
},
error : function(qXHR, textStatus, errorThrown){
console.log(errorThrown, "Error " + qXHR.status);
}
});
controller code:
#RequestMapping(value = "/signup", method = RequestMethod.POST, produces = {"text/plain", "application/*"})
public #ResponseBody String signup(#RequestBody UserSignup details) {
//...
return message;
}
any idea how can I solve this problem? I have tried a few things but nothing work. I think the response format is wrong as what the code expects.
Edit
I have changed my code(removed produces) but I still getting the same error:
SyntaxError: Unexpected end of JSON input(…) "Error 200"
#RequestMapping(value = "/signup", method = RequestMethod.POST)
public #ResponseBody String signup(#RequestBody UserSignup details) {
message = "ok";
}
return message;
}
Your method is wrong. You are saying to produce produces = {"text/plain", "application/*"} But you are also adding the #ResponseBody which will generate JSON format response.
I would suggest you remove the attribute produces. And verify the string you are returning is well formed
Try to wrap your response in ResponseEntity class
#RequestMapping(value = "/signup", method = RequestMethod.POST)
public #ResponseBody ResponseEntity<String> signup(#RequestBody UserSignup details) {
message = "ok";
return new ResponseEntity<>(message, HttpStatus.OK);
}
Also double check data that you are sending to server, maybe this is the problem, can you show us _data value?
As I don't have problem when the response is different stuff as a String I have solved the problem creating my own object. So below is the code:
public class Response<T> {
private T message;
private Exception ex;
public Exception getEx() {
return ex;
}
public void setEx(Exception ex) {
this.ex = ex;
}
public T getMessage() {
return message;
}
public void setMessage(T message) {
this.message = message;
}
}
#Controller
public class MyControllerController {
private Response<String> _response;
private String message;
public MyController() { _response = new Response<>(); }
#RequestMapping(value = "/signup", method = RequestMethod.POST)
public #ResponseBody Response<String> signup(#RequestBody UserSignup details) {
try{
message = "";
// code...
_response.setMessage(message);
return _response;
}catch (Exception ex){
_response.setEx(ex);
return _response;
}
}
}
response example in the browser:
Object {message: "", ex: null}