Spring boot throws HttpMediaTypeNotAcceptableException: Could not find acceptable representation - spring-boot

Here are the 2 mappings I have in my class:
#Override
#RequestMapping(value="/getUserDetails", consumes={"application/xml", "text/xml"}, produces={"application/xml", "text/xml"}, method = RequestMethod.POST)
#ResponseBody
#ApiOperation(httpMethod = "GET", value = "Response for user details", notes = "Gets response for user details", response = GetUserDetailsResponse.class )
#ApiResponse(code = 200, message = "returns response for user details")
#Timed(name = "http.userservice.getUserDetails", absolute = true)
public GetUserDetailsResponse getUserDetails(
#RequestBody GetUserDetailsRequest request) throws ServiceException {
GetUserDetailsResponse response = new GetUserDetailsResponse();
String username = request.getUsername();
User user = createUser(username);
response.setUser(user);
response.setUsername(username);
return response;
}
#Override
#RequestMapping(value = "/", method = RequestMethod.GET)
#ResponseBody
#ApiOperation(value = "hello world" , response = String.class )
#ApiResponse(code = 200, message = "hello world")
public String helloWorld() {
return "hello world";
}
When I request localhost:8080/,
I am getting proper response i.e. "hello world". "/" is mapped to second method below.
But when I request for localhost:8080/getUserDetails with POST request, spring throws HttpMediaTypeNotAcceptableException.
Any idea?
Below is the xml data i am sending as part of POST request
<userDetailsRequest>
    <username>abc#cdk.com</username>
</userDetailsRequest>

Related

How can I hide #ApiResponse form #ControllerAdvice for an endpoint?

I'm trying to migrate our manually writen OpenAPI (swagger) to a generated OpenAPI using springdoc-openapi for our Spring-Boot application. We got some issues, because the controller responses (mostly ErrorCodes) didn't match to the documentatation.
We already used a #ControllerAdvice annotated handler configuration. Here a snippet:
#ControllerAdvice
public class ExceptionHandler {
#ResponseStatus(code = HttpStatus.NOT_FOUND)
#ApiResponse(responseCode = "404", description = "(NOT FOUND) Resource does not exist!", content = #Content)
#ExceptionHandler(NotFoundException.class)
public void handleException(NotFoundException e) {
log.warn("Returning {} due to a NotFoundException: {}", HttpStatus.NOT_FOUND, e.toString());
}
#ResponseStatus(value = HttpStatus.BAD_REQUEST)
#ApiResponse(responseCode = "400", description = "(BAD REQUEST) Given resource is invalid!", content = #Content)
#ExceptionHandler(InvalidResourceException.class)
public void handleException(InvalidResourceExceptione) {
log.error("Invalid resource: {}", e.toString());
}
The generated API now showed all defined ApiResponses as responses for all controllers and endpoints. So I splittet the handler config using #ControllerAdvice(basePackageClasses = MyController.class) to group the possible exceptions. But there are still responses that are not fitting to all endpoints of a controller. Like:
#RestController
public class MyController {
#ResponseStatus(HttpStatus.CREATED)
#Operation(summary = "Create", description = "Create myResource!")
#PostMapping(value = "/myResources/", produces = {"application/json"})
#ResponseBody
public Integer create(#RequestBody MyResource newResource) throws InvalidResourceException {
return creationService.createResource(newResource).getId();
}
#ResponseStatus(HttpStatus.OK)
#Operation(summary = "Update", description = "Update myResource!")
#PutMapping(value = "/myResources/{id}", produces = {"application/json"})
public void update(#PathVariable("id") Integer id, #RequestBody MyResource newResource)
throws ResourceNotFoundException, InvalidResourceException {
return updateService.updateResource(id, newResource);
}
#ResponseStatus(HttpStatus.OK)
#Operation(summary = "Get", description = "Get myResource!")
#GetMapping(value = "/myResources/{id}", produces = {"application/json"})
#ResponseBody
public MyResource get(#PathVariable("id") Integer id) throws ResourceNotFoundException {
return loadingService.getResource(id);
}
}
POST will never respond with my 'business' 404 and GET will never respond with my 'business' 400. Is it possible to annotate an endpoint, so that not possible response codes are hidden in the API?
I tried to override the responses, but didn't work as intended:
#ResponseStatus(HttpStatus.OK)
#Operation(summary = "Get", description = "Get myResource!")
#ApiResponses({#ApiResponse(responseCode = "200", description = "(OK) Returning myResource"),
#ApiResponse(responseCode = "404", description = "(NOT FOUND) Resource does not exist!")})
#GetMapping(value = "/myResources/{id}", produces = {"application/json"})
#ResponseBody
public MyResource get(#PathVariable("id") Integer id) throws ResourceNotFoundException {
return loadingService.getResource(id);
}
400 still shows up...
You need to remove the #ApiResponse from your #ControllerAdvice class and need to add the respective response in your controller class, as mentioned by you.
#ResponseStatus(HttpStatus.OK)
#Operation(summary = "Get", description = "Get myResource!")
#ApiResponses({#ApiResponse(responseCode = "200", description = "(OK) Returning myResource"),
#ApiResponse(responseCode = "404", description = "(NOT FOUND) Resource does not exist!")})
#GetMapping(value = "/myResources/{id}", produces = {"application/json"})
#ResponseBody
public MyResource get(#PathVariable("id") Integer id) throws ResourceNotFoundException {
return loadingService.getResource(id);
}

How to wrap Path Not Found Exception in Spring Boot Rest using ExceptionHandler?

I am using Spring Boot and Spring Rest Example. In this example, I am passing custom header, if that value is valid, endpoint gets called successfully, if custom header value is not correct then I get below response, which I want to wrap into show it to the enduser using #ControllerAdvice ExceptionHandler.
Note: I went through Spring mvc - How to map all wrong request mapping to a single method, but here in my case I am taking decision based on CustomHeader.
{
"timestamp": "2020-01-28T13:47:16.201+0000",
"status": 404,
"error": "Not Found",
"message": "No message available",
"path": "/employee-data/employee-codes"
}
Controller
#Operation(summary = "Find Employee")
#ApiResponses(value = { #ApiResponse(code = 200, message = "SUCCESS"),
#ApiResponse(code = 500, message = "Internal Server Error") })
#Parameter(in = ParameterIn.HEADER, description = "X-Accept-Version", name = "X-Accept-Version",
content = #Content(schema = #Schema(type = "string", defaultValue = "v1",
allowableValues = {HeaderConst.V1}, implementation = Country.class)))
#GetMapping(value = "/employees/employee-codes", headers = "X-Accept-Version=v1")
public ResponseEntity<Employees> findEmployees(
#RequestParam(required = false) String employeeCd,
#RequestParam(required = false) String firstName,
#RequestParam(required = false) Integer lastName) {
Employees response = employeeService.getEmployees(employeeCd, firstName, lastName);
return new ResponseEntity<>(response, HttpStatus.OK);
}
I've implemented HttpMessageNotReadableException and HttpMediaTypeNotSupportedException and NoHandlerFoundException, but still not able to wrap this error.
Any suggestions?
I was able to find the solution for it.
# Whether a "NoHandlerFoundException" should be thrown if no Handler was found to process a request.
spring.mvc.throw-exception-if-no-handler-found=true
spring.resources.add-mappings=false
Error Handling Code:
#Override
protected ResponseEntity<Object> handleNoHandlerFoundException(NoHandlerFoundException ex, HttpHeaders headers,
HttpStatus status, WebRequest request) {
// custom logic here
return handleExceptionInternal(ex, error, getHeaders(), HttpStatus.BAD_REQUEST, request);
}
If you're using #ControllerAdvice,
do this:
#ControllerAdvice
public class RestResponseEntityExceptionHandler
extends ResponseEntityExceptionHandler {
#ExceptionHandler(value
= { IllegalArgumentException.class, IllegalStateException.class })
protected ResponseEntity<Object> handleConflict(
RuntimeException ex, WebRequest request) {
String bodyOfResponse = "This should be application specific";
return handleExceptionInternal(ex, bodyOfResponse,
new HttpHeaders(), HttpStatus.CONFLICT, request);
}
}

Mock Mvc unit test throws Http 406

My controller has this code
#RequestMapping(value = "/getUserByEmailId/{emailId}", method = GET, produces = "application/json")
public ResponseEntity<UserRegistrationResponse> getUserByEmailId(#PathVariable String emailId) throws ServiceException {
return new ResponseEntity(generateUserOutput(userService.findUserByEmail(emailId)), OK);
}
My test has this:
#Test
public void testGetUserByEmailId() throws Exception {
when(userService.findUserByEmail(any())).thenReturn(userOutput);
MvcResult mvcResult = performGet("/user/getUserByEmailId/{emailId}", userRegistrationRequest.getEmail());
assertEquals(200, mvcResult.getResponse().getStatus());
}
private MvcResult performGet(String path, String pathVariable) throws Exception {
return mockMvc.perform(get(path,pathVariable)
.accept(APPLICATION_JSON))
.andDo(print())
.andReturn();
}
This is returning HTTP status code 406.
MockHttpServletRequest:
HTTP Method = GET
Request URI = /user/getUserByEmailId/testEmailId#test.com
Parameters = {}
Headers = {Accept=[application/json]}
Body = <no character encoding set>
Session Attrs = {}
Handler:
Type = com.nz.unicorn.web.controller.UserController
Method = public org.springframework.http.ResponseEntity<com.nz.unicorn.web.response.UserRegistrationResponse> com.nz.unicorn.web.controller.UserController.getUserByEmailId(java.lang.String) throws com.nz.unicorn.service.exception.ServiceException
Async:
Async started = false
Async result = null
Resolved Exception:
Type = org.springframework.web.HttpMediaTypeNotAcceptableException
ModelAndView:
View name = null
View = null
Model = null
FlashMap:
Attributes = null
MockHttpServletResponse:
Status = 406
Error message = null
Headers = {}
Content type = null
Body =
Forwarded URL = null
Redirected URL = null
Cookies = []
Is there anything I can do? I have seen a lot of questions without an answer that I can use. Do note, mine is not a spring test, it's a unit test.
This is a workaround I did, changed the controller as follows and it works:
#RequestMapping(value = "/getByEmailId/{emailId}", method = RequestMethod.GET, produces = "application/json")
#ResponseStatus(OK)
public UserRegistrationResponse getUserByEmailId(#PathVariable String emailId) throws ServiceException {
return generateUserOutput(userService.findUserByEmail(emailId));
}
This solved, don't know why ResponseEntity still didn't work though.

400 (Bad Request) while sending json in Spring

I'm trying to send json string to Spring controller, i'm getting 400 - bad request as response
i'm using Spring 4.0.3
This is my controller
#Controller
public class Customer{
#RequestMapping(value = "/apis/test", method = RequestMethod.GET, produces = "application/json")
public #ResponseBody String test(HttpServletRequest params) throws JsonIOException {
String json = params.getParameter("json");
JsonParser jObj = new JsonParser();
JsonArray jsonObj = (JsonArray ) jObj.parse(json);
for(int i = 0; i < jsonObj.size(); i++) {
JsonObject jsonObject = jsonObj.get(i).getAsJsonObject();
System.out.println(jsonObject.get("name").getAsString());
}
return json;
}
}
Please help me to solve this
#RequestMapping(value = "/apis/test", method = RequestMethod.GET, produces = "application/json")
The above means this is a HTTP GET method which does not normally accept data. You should be using a HTTP POST method eg:
#RequestMapping(value = "/apis/test", method = RequestMethod.POST, consumes = "application/json")
public #ResponseBody String test(#RequestParam final String param1, #RequestParam final String param2, #RequestBody final String body) throws JsonIOException {
then you can execute POST /apis/test?param1=one&param2=two and adding strings in the RequestBody of the request
I hope this helps!

Change url path in Spring MVC by #RequestMapping

Currently path is showing
http://localhost:8081/UserLogin/login
But i want this as
http://localhost:8081/UserLogin/index
or
http://localhost:8081/UserLogin/
My controller class is
#RequestMapping(value = "/login" ,method = RequestMethod.POST)
public ModelAndView test(HttpServletRequest request, HttpServletResponse response) {
//return "hi this is a test";
String userName = request.getParameter("data[Admin][user_name]");
String userPass=request.getParameter("data[Admin][password]");
int userId=userDAO.getUser(userName, userPass);
if(userId!=0){
String message = "welcome!!!";
return new ModelAndView("result", "message", message);
}
else{
String message = "fail";
return new ModelAndView("index", "message",message);
}
}
Want to change in else condition when not match.
Thanks in advance. :)
I would return a redirect to render the view under the new URL:
request.addAttribute("message",message) // better use a Model
return "redirect:/[SERVLET_MAPPING]/index";
It take some time to understand what you want: - I guess you want to alter the URL that is returned from the Server after login.
But this does not work this way, because the URL is requested from the browser and the server can not change them. Instead the server can respond an "HTTP 303 Redirect" (instead of the view). This cause the Browser to load the URL given with the Redirect.
#RequestMapping(value = "/login" ,method = RequestMethod.POST)
public ModelAndView test(HttpServletRequest request, HttpServletResponse response) {
//return "hi this is a test";
String userName = request.getParameter("data[Admin][user_name]");
String userPass=request.getParameter("data[Admin][password]");
int userId=userDAO.getUser(userName, userPass);
if(userId!=0){
return new ModelAndView(new RedirectView("/result", true)); // "/result" this is/become an URL!
}
else {
return new ModelAndView(new RedirectView("/index", true)); // "/index" this is/become an URL!
}
}
#RequestMapping(value = "/index" ,method = RequestMethod.GET)
public ModelAndView test(HttpServletRequest request, HttpServletResponse response) {
String message = "fail";
return new ModelAndView("index", "message",message); //"index" the the name of an jsp (or other template)!!
}
#RequestMapping(value = "/result" ,method = RequestMethod.GET)
public ModelAndView test(HttpServletRequest request, HttpServletResponse response) {
String message = "welcome!!!";
return new ModelAndView("result", "message", message); //"result" the the name of an jsp (or other template)!!
}
#See http://en.wikipedia.org/wiki/URL_redirection

Resources