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
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 ....
}
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);
}
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.
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.
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
...
}