In my Spring boot app replacing a legacy, i have defined a webservice EndPoint.
soem of the user today comes in with payload that does nothave the namespace URI.
since namespace is not there, Spring throws No Endpoint mapping found error.
Is there a way i can add a default Endpoint so that it will get invoked if no mapping is found.
Thanks
You can try the following to create a fallback method for all request
#RequestMapping(value = "*", method = RequestMethod.GET)
#ResponseBody
public String getFallback() {
return "Fallback for GET Requests";
}
You can get more information here https://www.baeldung.com/spring-requestmapping
Related
I have multiple links in my HTML, which are referring to URI in Controller Class,
How can I get this URI in some variable which can be used further, at last, I want to store these URI in DB.
HTML Code :
<td>Win Report</td>
<td>Win Report</td>
Spring Controller Class :
#RequestMapping(value = "/ui/report/win", method = RequestMethod.GET)
public String winReport() {
return "win_report";
}
#RequestMapping(value = "/ui/report/niw", method = RequestMethod.GET)
public String niwReport() {
return "niw_report";
}
You can use the below solution to retrieve the page Url as well as avoid using repetitive method calls.
You can use a Spring Boot HandlerInterceptor, here's a brief description of the same :
Handler interceptors are used when you want to apply specific
functionality to certain or all requests.
Handler Interceptors should implement the interface HandlerInterceptor. HandlerInterceptor can be used to avoid repetitive handler code.
We can use HandlerInterceptor for different purposes like authorization checks, locale checks, logging, creating common application parameters etc.
HandlerInterceptor works similar to the servlet filter. But in some cases filters are more powerful than HandlerInterceptor.
In Spring-MVC the HandlerInterceptor is configured in spring application context xml file or by Java configuration.
HandlerInterceptor has three methods.
preHandle( ) : It is executed before actual handler is executed.
postHandle( ) : It is executed after handler is executed.
afterCompletion( ) : It is executed after the complete request is finished.
For more details, you can use an example from the below link
https://www.tuturself.com/posts/view?menuId=3&postId=1071
You can get the entire sample project which can help you with the setup at
https://github.com/ninja-panda
To get the request url you can do the following:
#RequestMapping(value = "/ui/report/win", method = RequestMethod.GET)
public String winReport(HttpServletRequest request){
String request = request.getRequestURI();
// do somehting here
return "win_report"
}
Spring will automatically inject the HttpServletRequest.
Update:
If your want get the urls for all of your methods in your controller, you can go with RequestMappingHandlerMapping:
private final RequestMappingHandlerMapping handlerMapping;
#Autowired
public YourController(RequestMappingHandlerMapping handlerMapping) {
this.handlerMapping = handlerMapping;
}
With handlerMapping.getHandlerMethods(), you can access all mappings decleared in your controller. With reflection and getMappingAnnotation, you can then read the value of each RequestMapping annotation.
You can try the getServletPath() like following:
#RequestMapping(value = "/ui/report/win", method = RequestMethod.GET)
public String winReport(HttpServletRequest request){
String mapping = request.getServletPath();
// do somehting here
System.out.println(mapping); // Will print /ui/report/win
return "win_report"
}
I have following REST controller with GET method that have BODY, that works fine with tests and postman
#RestController
#RequestMapping(value = "/xxx")
public class Controller {
#GetMapping({"/find"})
public LocalDateTime findMax(#RequestBody List<ObjectId> ids) {
//return sth
}
}
but when FeignClient is used to call service, instead GET request a POST request is generated (#GetMapping annotation is ignored)
#FeignClient
public interface CoveragesServiceResource extends CoveragesService {
#GetMapping({"/find"})
LocalDateTime findMax(#RequestBody List<ObjectId> ids);
}
that gives an error:
Request method 'POST' not supported
GET request technically can have body but the body should have no meaning as explained in this answer. You might be able to declare a GET endpoint with a body but some network libraries and tools will simply not support it e.g. Jersey can be configured to allow it but RESTEasy can't as per this answer.
It would be advisable to either declare /find as POST or don't use #RequestBody.
I have an web application and I'm trying to creat a simple POSt method that will have a value inside the body request:
#RequestMapping(value = "/cachettl", method = RequestMethod.POST)
#CrossOrigin(origins = "http://localhost:3000")
public #ResponseBody String updateTtl(#RequestBody long ttl) {
/////Code
}
My request which I call from some rest client is:
POST
http://localhost:8080/cachettl
Body:
{
"ttl": 5
}
In the response I get 403 error "THE TYPE OF THE RESPONSE BODY IS UNKNOWN
The server did not provide the mandatory "Content-type" header."
Why is that happening? I mention that other GET requests are working perfectly.
Thanks!
Edit:
When I tried it with postman the error message I got is "Invalid CORS request".
Spring application just doesn't know how to parse your message's body.
You should provide "header" for your POST request to tell Spring how to parse it.
"Content-type: application/json" in your case.
You can read more about http methods here: https://developer.mozilla.org/en-US/docs/Learn/HTML/Forms/Sending_and_retrieving_form_data
Updated:
Just in case of debug, remove useless annotations to test only POST mechanism. Also, change types of arg and return type. And try to use case-sensitive header.
#RequestMapping(value = "/cachettl", method = RequestMethod.POST)
public void updateTtl(#RequestBody String ttl) {
System.out.println("i'm working");
}
Since the error is about the response type, you should consider adding a produces attribute, i.e :
#RequestMapping(value = "/cachettl", method = RequestMethod.POST, produces=MediaType.APPLICATION_JSON_VALUE)
Since you are also consuming JSON, adding a consumes attribute won't hurt either :
#RequestMapping(value = "/cachettl", method = RequestMethod.POST, consumes=MediaType.APPLICATION_JSON_VALUE, produces=MediaType.APPLICATION_JSON_VALUE)
The error message is slightly misleading. Your server code is not being hit due an authentication error.
Since you say spring-security is not in play then I suspect you're being bounced by a CORS violation maybe due to a request method restriction. The response body generated by this failure (if any at all) is automatic and will not be of the application/json type hence the client failure. I suspect if you hit the endpoint with something that doesn't care for CORS such as curl then it will work.
Does your browser REST client allow you to introspect the CORS preflight requests to see what it's asking for?
I have a spring boot application.
I have a custom error controller, that is mapped to using ErrorPage mappings. The mappings are largely based on HTTP Status codes, and normally just render a HTML view appropriately.
For example, my mapping:
#Configuration
class ErrorConfiguration implements EmbeddedServletContainerCustomizer {
#Override public void customize( ConfigurableEmbeddedServletContainer container ) {
container.addErrorPages( new ErrorPage( HttpStatus.NOT_FOUND, "/error/404.html" ) )
}
And my error controller:
#Controller
#RequestMapping
public class ErrorController {
#RequestMapping( value = "/error/404.html" )
#ResponseStatus(value = HttpStatus.NOT_FOUND)
public String pageNotFound( HttpServletRequest request ) {
"errors/404"
}
This works fine - If I just enter a random non-existent URL then it renders the 404 page.
Now, I want a section of my site, lets say /api/.. that is dedicated to my JSON api to serve the errors as JSON, so if I enter a random non-existent URL under /api/.. then it returns 404 JSON response.
Is there any standard/best way to do this? One idea I tried out was to have a #ControllerAdvice that specifically caught a class of custom API exceptions I had defined and returned JSON, and in my standard ErrorController checking the URL and throwing an apprpriate API exception if under that API URL space (but that didn't work, as the ExceptionHandler method could not be invoked because it was a different return type from the original controller method).
Is this something that has been solved?
The problem was my own fault. I was trying to work out why my #ExceptionHandler was not able to catch my exception and return JSON - As I suggested at the end of my question, I thought I was having problems because of conflicting return types - this was incorrect.
The error I was getting trying to have my exception handler return JSON was along the lines of:
"exception": "org.springframework.web.HttpMediaTypeNotAcceptableException",
"message": "Could not find acceptable representation"
I did some more digging/experimenting to try to narrow down the problem (thinking that the issue was because I was in the Spring error handling flow and in an ErrorController that was causing the problem), however the problem was just because of the content negotiation stuff Spring does.
Because my errorPage mapping in the web.xml was mapping to /error/404.html, Spring was using the suffix to resolve the appropriate view - so it then failed when I tried to return json.
I have been able to resolve the issue by changing my web.xml to /error/404 or by turning off the content negotiation suffix option.
Now, I want a section of my site, lets say /api/.. that is dedicated
to my JSON api to serve the errors as JSON, so if I enter a random
non-existent URL under /api/.. then it returns 404 JSON response.
Is there any standard/best way to do this? One idea I tried out was to
have a #ControllerAdvice that specifically caught a class of custom
API exceptions I had defined and returned JSON, and in my standard
ErrorController checking the URL and throwing an apprpriate API
exception if under that API URL space (but that didn't work, as the
ExceptionHandler method could not be invoked because it was a
different return type from the original controller method).
I think you need to rethink what you are trying to do here. According to HTTP response codes here
The 404 or Not Found error message is an HTTP standard response code
indicating that the client was able to communicate with a given
server, but the server could not find what was requested.
So when typing a random URL you may not want to throw 404 all the time. If you are trying to handle a bad request you can do something like this
#ControllerAdvice
public class GlobalExceptionHandlerController {
#ExceptionHandler(NoHandlerFoundException.class)
#ResponseStatus(value = HttpStatus.BAD_REQUEST)
#ResponseBody
public ResponseEntity<ErrorResponse> noRequestHandlerFoundExceptionHandler(NoHandlerFoundException e) {
log.debug("noRequestHandlerFound: stacktrace={}", ExceptionUtils.getStackTrace(e));
String errorCode = "400 - Bad Request";
String errorMsg = "Requested URL doesn't exist";
return new ResponseEntity<>(new ErrorResponse(errorCode, errorMsg), HttpStatus.BAD_REQUEST);
}
}
Construct ResponseEntity that suites your need.
In Spring 3 MVC, I have a controller that I call SettingsController, and it has methods such as displayUsers() for displaying a list of users, saveUser(), and deleteUser(). SettingsContoller also controls roles and other things.
I'd love to be able to use URL routing such that /settings/users would call displayUsers(), /settings/users/save would call saveUser(), and /settings/users/delete would call deleteUser().
My code is below, and I'm getting the error message that follows the code. What am I doing wrong? Thanks!
#Controller
#RequestMapping("/settings")
public class SettingsController {
#Transactional
#RequestMapping(value = {"/users/save"}, method = {RequestMethod.POST})
public ModelAndView saveUser(details removed){
//details removed
}
#RequestMapping(value = {"/users/delete"}, method = {RequestMethod.POST})
public ModelAndView deleteUser(details removed){
//details removed
}
#RequestMapping(value = {"/users"}, method = RequestMethod.GET)
public ModelAndView settingsUsers(details removed){
//details removed
}
}
Error:
HTTP ERROR: 500
Could not resolve view with name 'settings/users/delete' in servlet with name 'spring'
RequestURI=/das-portal/srv/settings/users/delete
Caused by:
javax.servlet.ServletException: Could not resolve view with name 'settings/users/delete' in servlet with name 'spring'
at org.springframework.web.servlet.DispatcherServlet.render(DispatcherServlet.java:1029)
...
It looks to me like you've set up your controller correctly. As you pointed out, the problem might be in how Spring parses annotations upon start up.
How did you configure Sprint to parse annotations such as #Controller? Do you explicitly set up any sort of HandlerMapping? If you use <context:component-scan>, then it registers a DefaultAnnotationHandlerMapping for you.
The good news is that you can chain multiple handler mapping classes together. The DispatcherServlet will check each one in the order that you specify via the order property of the handler mapping beans (in other words, use the order property to indicate the precedence of your handlers).
So, throw <bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping"/> into your configuration and set its order property as appropriate.
What about using just one method checking mode?
#RequestMapping(value = "/users/{action}", method = RequestMethod.POST)
public String userAction(#PathVariable String action, ...) {
if (mode.equals("save")) {
//your save code here
}
}