Inconsistent error: Request method 'POST' not supported - java

Using Spring REST API [Spring MVC]
Scenario:
when request comes to the EmployeeController , it is forced to forward the request/response to another URI,if it falls under a specific logic.
The controller method has RequestMapping set with 'RequestMethod.POST' and the destination controller- SpecialController has the method named invalidRequest() that has RequestMapping set with 'RequestMethod.GET'
EmployeeController:
#RestController
#RequestMapping(value = "/employee")
public class EmployeeController {
String res = null;
#RequestMapping(value = "/update", method = RequestMethod.POST)
public String updateEmployeeDetails(HttpServletRequest httpRequest, HttpServletResponse httpResponse,
#Valid #RequestBody Employee emp) throws ServletException, IOException {
// based on logic, forward the request to a different controller that handles invalid request
if( ...) { // condition checking
RequestDispatcher requestDispatcher = httpRequest.getServletContext().getRequestDispatcher("/invalidRequest");
requestDispatcher.forward(httpRequest, httpResponse);
}
if(..someother condition..) {
String res = "something";
}
return res;
Destination Controller:
#RestController
#RequestMapping(value = "/invalidRequest")
public class SpecialController {
#RequestMapping(value = "", method = RequestMethod.GET)
public String invalidRequest(HttpServletRequest httpRequest, HttpServletResponse httpResponse) {
httpResponse.setStatus(401);
return "invalidRequest";
}
}
Question: Inconsistency Problem [Actual Problem]:
In 90% of the times, this is working, but a few times very rarely, I get the below error.
If I am getting this error always, then it would have made some meaning and I would have the below mentioned 'possible fix'
But since it is working most of the times, and not working only sometimes, I need your help in finding out why?
> org.springframework.web.HttpRequestMethodNotSupportedException:
> Request method 'POST' not supported
> at org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping.handleNoMatch(RequestMappingInfoHandlerMapping.java:198)
> at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.lookupHandlerMethod(AbstractHandlerMethodMapping.java:286)
> at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.getHandlerInternal(AbstractHandlerMethodMapping.java:233)
> at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.getHandlerInternal(AbstractHandlerMethodMapping.java:56)
> at org.springframework.web.servlet.handler.AbstractHandlerMapping.getHandler(AbstractHandlerMapping.java:300)
> at org.springframework.web.servlet.DispatcherServlet.getHandler(DispatcherServlet.java:1101)
> at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:916)
> at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:876)
> at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:961)
> at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:863)
possible fix if the error was consistent:
#RestController
#RequestMapping(value = "/invalidRequest")
public class SpecialController {
#RequestMapping(value = "", method = RequestMethod.GET)
public String invalidRequest(HttpServletRequest httpRequest, HttpServletResponse httpResponse) {
httpResponse.setStatus(401);
return "invalidRequest";
}
#RequestMapping(value = "", method = RequestMethod.POST)
public String invalidRequest2(HttpServletRequest httpRequest, HttpServletResponse httpResponse) {
return invalidRequest(httpRequest, httpResponse);
}
}

Related

How to add Location header to the http response?

I have a Java project and I'm using Servlet in order to handle http requests.
I also using Spring
When I receive a request to create a new object (for example an account), I would like also to return the “location” header with the GET URL of the newly created object.
for example: location: /accounts/1000
I understand the header are added to the Servlet filter (correct me if Im wrong)
public class ApiLogFilter implements Filter {
private static final Logger LOGGER = LoggerFactory.getLogger("apilogger");
#Override
public void init(FilterConfig filterConfig) throws ServletException {
}
#Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
HttpServletResponse httpServletResponse = ((HttpServletResponse) servletResponse);
httpServletResponse.addHeader( "Location","the location value");
try {
filterChain.doFilter(servletRequest, servletResponse);
} finally {
String queryString = httpServletRequest.getQueryString() != null ? httpServletRequest.getQueryString() : "N/A";
String logMessage = "URL: " + httpServletRequest.getRequestURL() + ", Query String: " + queryString + ", Response Status: " + httpServletResponse.getStatus() ;
LOGGER.info(logMessage);
}
}
#Override
public void destroy() {
}
}
But I don't understand how to get the location value from the API
#RequestMapping("/accounts")
public class IgnoreRuleController {
private AccountService accountService;
public void setIgnoreRuleService(IgnoreRuleService ignoreRuleService) {
this.accountService = ignoreRuleService;
}
#RequestMapping(method = RequestMethod.POST)
#ResponseBody
public String createAccount(#RequestBody Account account) {
return new Gson().toJson(accountService.createAccount(account));
}
}
I found solution here
http://learningviacode.blogspot.com/2013/07/post-with-location.html
you didn't need to do anything with the filter.
in the api itself:
#RequestMapping(method = RequestMethod.POST)
#ResponseBody
public ResponseEntity<String> createIgnoreRule(#RequestBody IgnoreRule ignoreRule) {
String response = new Gson().toJson(ignoreRuleService.createIgnoreRule(ignoreRule));
final URI location = ServletUriComponentsBuilder
.fromCurrentServletMapping().path("/ignore_rules/{id}").build()
.expand(ignoreRule.getId()).toUri();
final HttpHeaders headers = new HttpHeaders();
headers.setLocation(location);
final ResponseEntity<String> entity = new ResponseEntity<>(response, headers, HttpStatus.CREATED);
return entity;
}
It's very simple, you can pass the header directly throw your method signature:
#RequestMapping(value="/create-account", method = RequestMethod.POST)
#ResponseBody
public String createAccount(#RequestHeader HttpHeaders httpHeader, #RequestBody Account account) {
var s = httpHeader.get("Location");
System.out.println(s.get(0));
return ...
}
In fact you can pass the whole request also which contains everything (Headers, Body, ...):
#RequestMapping(value="/create-account", method = RequestMethod.POST)
#ResponseBody
public String createAccount(HttpServletRequest httpRequest, #RequestBody Account account) {
var s = httpRequest.getHeader("Location");
System.out.println(s);
return ....
}

Return web page body from Spring end point

Is it possible to return html web page as very long string from Spring endpoint? For example:
#PostMapping(value = "/redirect/{token}", consumes = { MediaType.APPLICATION_XML_VALUE,
MediaType.APPLICATION_JSON_VALUE },
MediaType.APPLICATION_JSON_VALUE })
public ModelAndView handleRedirectMessage(#PathVariable("token") String token,
#RequestBody PaymentTransaction transaction, HttpServletRequest request) throws Exception {
String body = "<html>.....</html>";
return new ModelAndView("redirect:" + body);
}
I would like to return html page body when user opens the Spring end point into the web browser.
As suggested by #user2478398
#PostMapping(value = "/redirect/{token}",produces = MediaType.TEXT_HTML_VALUE)
public String handleRedirectMessage(#PathVariable("token") String token,
#RequestBody PaymentTransaction transaction, HttpServletRequest request) throws Exception {
String body = "<http>.....</html>";
return body;
}
Try to use produce as - produces = MediaType.TEXT_HTML_VALUE
I have changed your code and you can changed it your way , this is an idea to resolved you problem
#PostMapping(value = "/redirect/{token}",produces = MediaType.TEXT_HTML_VALUE)
public ModelAndView handleRedirectMessage(#PathVariable("token") String token,
#RequestBody PaymentTransaction transaction, HttpServletRequest request) throws Exception {
String body = "<HTML><body>Now you can redirect just do it man !!.</body></HTML>";
return new ModelAndView("redirect:" + body);
}

Spring MVC default #RequestMapping to catch unmatched URL

I have this Controller
#Controller
#RequestMapping(value = "/v1.0/user")
public class UserController {
#RequestMapping(value = "/findOne/{id}", method = RequestMethod.GET)
public #ResponseBody String findOne(#PathVariable("id") Integer id) {
log.info("findOne");
return "found URL";
}
}
Which will match the URL: http://localhost:8080/v1.0/user/findOne/4
But if my path varibale is not correct: http://localhost:8080/v1.0/user/findOne/4A
The I get nothing. Not even an error. It's as if Spring swallowed the URL.
I added
#RequestMapping(value = "/.*", method = RequestMethod.GET)
public #ResponseBody String redirectEverythingOtherThanTest(){
log.info("no url matched");
return "badly formed URL for Users";
}
Again I get nothing. What I'm trying to accomplish is for each Controller to have a unique message when the URL does not match.
In the end in each of my Controller classes I added
#ExceptionHandler(Exception.class)
public #ResponseBody String handleException(Exception e, HttpServletRequest request, HttpServletResponse response) {
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
return e.getMessage();
}
And this will catch all exceptions and I can treat it as a catch all.

Spring Rest Service Call returning error on line 1 at column 1: Document is empty

I wrote a REST Call which will return health status when called
#RestController
#RequestMapping(value = "/account")
public class HealthCheckController {
protected final Logger log = LoggerFactory.getLogger(this.getClass());
#RequestMapping(value = "/health", method = RequestMethod.GET, produces = { "application/json" })
#ResponseStatus(HttpStatus.OK)
#ApiOperation(value = "Returns the health status of the application", notes = "Load balancer user this to confirm the health of the node")
public #ResponseBody String getHealth(HttpServletRequest request, HttpServletResponse response) throws Exception {
log.info("***" + RequestCorrelation.getId() + "***" + "HealthCheckController - getHealth () Called");
return "{\"health\":{\"SERVICES\":\"OK\"}}";
}
}
When I open this in swagger or postman it returns proper response. But when i hit this URL in chrome browser i am seeing
This page contains the following errors:
error on line 1 at column 1: Document is empty
Below is a rendering of the page up to the first error.
Why so? and how to fix this?
Having the same issue. Have an object with the following class annotations and method:
#Consumes({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
#Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
#GET
#Path("version")
public String getVersion() { return "v1"; }
Added MediaType.TEXT_PLAIN to the end of the #Produces annotation. Didn't work.
Moved it the beginning of the #Produces annotation. Didn't work.
Moving/Adding it to the method resolved the issue for me. Your client will need to be able to accept that media type as well.
#GET
#Path("version")
#Produces({MediaType.TEXT_PLAIN})
public String getVersion() { return "v1"; )
HTH
In your getHealth() method, you're returning a String but in your #RequestMapping annotation, you specify that your method will produce JSON.
Try one of these:
#RequestMapping(value = "/health", method = RequestMethod.GET, produces = { "text/plain" })
//Now, pass Accept = "text/plain" in the request header:
or
#RequestMapping(method = RequestMethod.GET, produces = { MediaType.APPLICATION_JSON_VALUE })
public List<String> getHealth(..) {
/*
...
*/
ArrayList<String> list=new ArrayList();
list.add("Health OK");
return list;
}
This would give you
["Health OK"] in the response.
Try to return not a String but
return new ResponseEntity<>(yourString, HttpStatus.OK);
and also change this
public #ResponseBody String getHealth(HttpServletRequest request, HttpServletResponse response) throws Exception {
to this
public #ResponseBody ResponseEntity<String> getHealth(HttpServletRequest request, HttpServletResponse response) throws Exception {
And if it doesn't work, try to add .xml or .json to the end of your URL, when you accessing it in browser.

How to return 404 response status in Spring Boot #ResponseBody - method return type is Response?

I'm using Spring Boot with #ResponseBody based approach like the following:
#RequestMapping(value = VIDEO_DATA_PATH, method = RequestMethod.GET)
public #ResponseBody Response getData(#PathVariable(ID_PARAMETER) long id, HttpServletResponse res) {
Video video = null;
Response response = null;
video = videos.get(id - 1);
if (video == null) {
// TODO how to return 404 status
}
serveSomeVideo(video, res);
VideoSvcApi client = new RestAdapter.Builder()
.setEndpoint("http://localhost:8080").build().create(VideoSvcApi.class);
response = client.getData(video.getId());
return response;
}
public void serveSomeVideo(Video v, HttpServletResponse response) throws IOException {
if (videoDataMgr == null) {
videoDataMgr = VideoFileManager.get();
}
response.addHeader("Content-Type", v.getContentType());
videoDataMgr.copyVideoData(v, response.getOutputStream());
response.setStatus(200);
response.addHeader("Content-Type", v.getContentType());
}
I tried some typical approaches as:
res.setStatus(HttpStatus.NOT_FOUND.value());
new ResponseEntity(HttpStatus.BAD_REQUEST);
but I need to return Response.
How to return here 404 status code if video is null?
This is very simply done by throwing org.springframework.web.server.ResponseStatusException:
throw new ResponseStatusException(
HttpStatus.NOT_FOUND, "entity not found"
);
It's compatible with #ResponseBody and with any return value. Requires Spring 5+
Create a NotFoundException class with an #ResponseStatus(HttpStatus.NOT_FOUND) annotation and throw it from your controller.
#ResponseStatus(code = HttpStatus.NOT_FOUND, reason = "video not found")
public class VideoNotFoundException extends RuntimeException {
}
Your original method can return ResponseEntity (doesn't change your method behavior):
#RequestMapping(value = VIDEO_DATA_PATH, method = RequestMethod.GET)
public ResponseEntity getData(#PathVariable(ID_PARAMETER) long id, HttpServletResponse res{
...
}
and return the following:
return new ResponseEntity(HttpStatus.NOT_FOUND);
You can just set responseStatus on res like this:
#RequestMapping(value = VIDEO_DATA_PATH, method = RequestMethod.GET)
public ResponseEntity getData(#PathVariable(ID_PARAMETER) long id,
HttpServletResponse res) {
...
res.setStatus(HttpServletResponse.SC_NOT_FOUND);
// or res.setStatus(404)
return null; // or build some response entity
...
}

Categories

Resources