Spring test-mvc PUT not supported - spring

This is my Controller method:
#RequestMapping(method = RequestMethod.PUT,produces="application/json", headers = "content-type=application/json")
public #ResponseBody User updateUser(#RequestBody User user) throws PropertyErrorException, ItemNotFoundException {
return service.UpdateUser(user);
}
Using Spring test-mvc I would like to write a unit test case:
#Test
public void UpdateUser() throws Exception {
mockMvc.perform(put("/r01/users").body(userJsonString.getBytes())
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(content().type(MediaType.APPLICATION_JSON))
.andExpect(content().string(userString));
}
Running this test case generates:
WARN org.springframework.web.servlet.PageNotFound - Request method 'PUT' not supported
Also, updateUser method is never called and the response code is 405.
I have written a lot of tests, all GET requests, and they are working correctly. This means that I am confident in ContextConfiguration. What have I missed?

You are explicitly expecting a header of content-type in your #RequestMapping method but not sending the content type in your request, I think that is the reason why the request is failing..try this.
mockMvc.perform(put("/r01/users").contentType(MediaType.APPLICATION_JSON).body(userJsonString.getBytes())
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(content().type(MediaType.APPLICATION_JSON))
.andExpect(content().string(userString));

Related

Zuul proxy server throwing Internal Server Error when request takes more time to process

Zuul Proxy Error
I am getting this error when the request takes more time to process in the service.But Zuul returns response of Internal Server Error
Using zuul 2.0.0.RC2 release
As far as I understand, in case of a service not responding, a missing route, etc. you can setup the /error endpoint to deliver a custom response to the user.
For example:
#Controller
public class CustomErrorController implements ErrorController {
#RequestMapping(value = "/error", produces = "application/json")
public #ResponseBody
ResponseEntity error(HttpServletRequest request) {
// consider putting these in a try catch
Integer statusCode = (Integer)request.getAttribute("javax.servlet.error.status_code");
Throwable exception = (Throwable)request.getAttribute("javax.servlet.error.exception");
// maybe add some error logging here, e.g. original status code, exception, traceid, etc.
// consider a better error to the user here
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("{'message':'some error happened', 'trace_id':'some-trace-id-here'}");
}
#Override
public String getErrorPath() {
return "/error";
}
}

Error 404 on PUT request while having a GET

Got a small problem on my rest server. It's based on spring web framework.
Here's the code that poses me problems :
#RestController
#RequestMapping("users")
public class usersWS {
//some other functions
//works
#RequestMapping(
value="/{iduser}/functions/",
method=RequestMethod.GET,
produces={"application/json"})
public ResponseEntity<String> getUserFunctions(#PathVariable("iduser") String iduser){
//do stuff
return stuff;
}
//Don't works
#RequestMapping(
value="/{iduser}/functions/"
method=RequestMethod.PUT,
consumes={"application/json"})
public ResponseEntity<String> addUserFunctions(#RequestBody String json, #PathVariable("iduser") String iduser){
//do stuff
return stuff;
}
}
Server is launched by :
#SpringBootApplication()
#ImportResource("classpath*:**/jdbc-context.xml")
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
To call this server, I use the HTML handler found here : Spring HTTP Client
When I call the get verb, everything is working fine. I get the iduser, get the data I want, no problem.
When I call the put verb... I have an error 404. I checked, the url (http://localhost:8080/users/xxx/functions/) are exactly the same, I do send the body.
I would understand to get a 405 error, but I really don't understand how I can have a 404. If the mapping was wrong, the server should at least see that there is a function on the get verb and throw me a 405.
I have other functions using the PUT/POST that are working but they don't have a #PathVariable. Is it possible to mix #RequestBody and #PathVariable ?
Any help is gladly welcome.

Spring mvc - Configuring Error handling for XML and JSON Response

i have one REST API method :which will return Xml as response . Just for simplicity assume it throws simple Exception.
#RequestMapping(value = "machine/xmlData", method = RequestMethod.GET, produces = "application/xml")
public ResponseEntity<String> getXml(HttpServletRequest request)
throws Exception {
return getDataFromService();
}
Now i am handling the Exception in REST Controller like this.
This is generic Exception Handle method, for other API methods as well.(Xml or JSON Response)
#ExceptionHandler(Exception.class)
#ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
public ResponseEntity HandleException(Exception ex, HttpServletRequest request) {
ex.printStackTrace();
// here logic to generate Custom error Object
return new ResponseEntity<Object>(customErrorObject, HttpStatus.INTERNAL_SERVER_ERROR);
}
Case 1: Accept :"application/xml" and valid Response from Service
Everything works fine.
Case 2: Accept :"application/xml" and Exception from Service
then i get 406 Not Representable
As per my understanding it is
because ResponseEntity from HandleException is JSON and accept header
is "application/xml" thats why i am getting 406.
Is there anyway that i can send the error Response from HandleException method as xml and json ?
I know on REST API methods we can define something like this produces={"application/json","application/xml"} i am struggling to put this on HandleException Method.
Any tip would be of great help.
Thanks.
You could take advantage of the spring-mvc HttpMessageConverters by using the #ResponseBody annotation( https://spring.io/blog/2013/05/11/content-negotiation-using-spring-mvc). This annotation is responsible for choosing the correct messageConverter for a given response type.
For your response to be xml or json compatible you need to do the following:
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class WrappedExceptionResponse {
public String respone;
public String getRespone() {
return respone;
}
public void setRespone(String respone) {
this.respone = respone;
}
}
And change your exception handler method to
#ExceptionHandler(Exception.class)
#ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
public #ResponseBody WrappedExceptionResponse HandleException(Exception ex, HttpServletRequest request) {
// ex.printStackTrace();
// here logic to generate Custom error Object
WrappedExceptionResponse resp=new WrappedExceptionResponse();
resp.setRespone(ex.getMessage());
return resp;
And then your exception response would be dependent on the content-type you give.

get error HTTP Status 405 for rest web service POST method

I load at browser as
localhost:8080/picking/addPick get error HTTP Status 405 - Request method 'GET' not supported.
What wrong?Hope advice thanks
#Controller
#RequestMapping("/picking")
public class PickerController {
#RequestMapping(method = RequestMethod.GET)
public #ResponseBody ArrayList getAllPickingItems()throws SQLException, ClassNotFoundException{
//....
}
#RequestMapping(value="/addPick",method=RequestMethod.POST)
#ResponseStatus(HttpStatus.CREATED)
public Boolean add(#RequestBody PickingInfo pickingInfo,HttpServletResponse response){
try{
Boolean success = pickerMethod.addPickingInfo(pickingInfo);
response.setHeader("addPickingInfo", success+"");
return true;
}catch(Exception ex){
System.out.println(ex.getMessage());
}
return false;
}
}
You limited URI/picking/addPick to POST requests :
#RequestMapping(value="/addPick",method=RequestMethod.POST)
When you try to open that URI from your browser you're sending a GET request, not a POST. If you want to be able to access /picking/addPick from your browser you must either :
remove the restriction method=RequestMethod.POST
allow explicitely GET requests : method = { RequestMethod.POST, RequestMethod.GET }
If you just want to test a POST method, use SoapUI. Simply create a "New REST Project", paste your service URI, and send any type of HTTP Request you want.
You have mapped /addPick to the add method only for POST requests. Therefor GET is not mapped to anything (and in this case there is no point in mapping get to the method since you are also using #RequestBody)

Why doesn't Spring MVC throw an error when you POST to a controller action that accepts HTTP GET?

I just noticed a weird problem as I've been testing my application. I was accidentally POSTing to a method that accepts HTTP GET (It was a typo - I'm a little tired), but the weird thing is that Spring was executing a GET action anyway - it wasn't throwing an error.
Here is the mapping for my GET action that I was POSTing to instead:
#RequestMapping(value = "/partialMapping/{partialMappingId}/edit", method = RequestMethod.GET)
public ModelAndView edit(#PathVariable long partialMappingId) {
return new ModelAndView(view("edit"), "partialMapping",
partialMappingService.findPartialMapping(partialMappingId));
}
What I would have expected was for Spring to say, "There is no action called /partialMapping/{partialMappingId}/edit for HTTP POST".
Instead... if you use the HandlerAdapter and pass it "POST" and "/partialMapping/1/edit", it runs my index action instead ("/partialMapping"). It doesn't throw an error. Why?
Is this a bug in spring, or is this desired behaviour? It's not a big deal when it comes to production code, but it surely makes debugging problems harder.
Here is the code I am using to execute a controller action in my tests:
protected ModelAndView handle(HttpServletRequest request, HttpServletResponse response) {
try {
final HandlerMapping handlerMapping = applicationContext.getBean(HandlerMapping.class);
final HandlerExecutionChain handler = handlerMapping.getHandler(request);
assertNotNull("No handler found for request, check you request mapping", handler);
final Object controller = handler.getHandler();
// if you want to override any injected attributes do it here
final HandlerInterceptor[] interceptors =
handlerMapping.getHandler(request).getInterceptors();
for (HandlerInterceptor interceptor : interceptors) {
final boolean carryOn = interceptor.preHandle(request, response, controller);
if (!carryOn) {
return null;
}
}
return handlerAdapter.handle(request, response, controller);
} catch(Exception e) {
throw new RuntimeException(e);
}
}
I found this code per another answer to a question on this site.
I believe your test code mimics the dispatch step that tries to find a matching Controller method signature after the URL and HTTP method have resolved. In other words, you are not testing your controller at the right level if you want to test the HTTP message bindings. For that kind of testing you would probably want to deploy to a server (perhaps embedded Jetty inside your test) and use RestTemplate to call it. That's what I do anyway.
If you annotate with Spring MVC annotations as below
#RequestMapping(method = RequestMethod.GET it should work.

Resources