axios post succeed but no method triggered in spring-controller - spring

I have tried use axios's get-method, it works, but when I tried to use axios's post-method, post could be sent, but spring-controller doesn't be triggered and no error reported in front/backend. I will put front- and backend code below.
//frontend code, post can be sent to server, So my guess here could be correct
axios({
method: "post",
url: 'http://localhost:8082/test',
data: {
email:this.email,
password: this.password
}
});
//backend controller, can not be triggered by axios's post-method
#RequestMapping(value = "/test", method = RequestMethod.POST)
public void Test(#RequestBody HashMap<String,String> map) {
System.out.print("kds");
}
I also tried backend code like this, but none of them worked.
#RequestMapping(value = "/test", method = RequestMethod.POST)
public void Test(#RequestParam("email") String email, #RequestParam("password") String password) {
System.out.print("kds");
}
Followed are code which I used for axios's get-method, this works, I don't know whether this could be helpful, so I also place it here.
//backend code with get-method, this works.
#RequestMapping(value = "/test", method = RequestMethod.GET)
public String test() {
return "returned data";
}
//frontend code with get-method, this works.
axios .get('http://localhost:8082/test').then(response => this.msg = response.data);

Spring csrf protection is on in default, which will redirect post-methode and return with a html-file (but not redirect get-method), that means post-method is intercepted before arriving at Spring-Controller(in this case, Test()).
This is why spring-controller not triggered in my question.
To solve the problem, i should either close csrf-protection in spring like below:
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable()................
or use csrf-token when post at axios side (I did not try this yet).
Last but not least, to #J.F., postman is great, thank you so much.
#Raja, Your question inspired me,thx.

Related

How to access url of this endpoint #GetMapping("/catalog/**") inside its method?

I have following endpoint:
#GetMapping("/catalog/**")
public String getCatalog(final Model model){
// code
}
This endpoint handles such urls: catalog/clothes, catalog/clothes/men,catalog/clothes/women and so on.
When I enter such url:
http://localhost:8080/catalog/clothes/women
this endpoint gets fired.
I need to get this url inside the method. How can I do this?
Thanks in advance.
The 2nd snippet looks Ok and the problem it is not getting called seems to be in other code, or share the class/configuration code.
#Controller
public class CatalogController {
private static final Logger logger = LoggerFactory.getLogger(CatalogController.class);
#GetMapping("/catalog/**")
public String getCatalog(final Model model, HttpServletRequest request, HttpServletResponse response){
logger.debug(request.getRequestURL().toString());
logger.debug(request.getQueryString());
logger.debug("Inside catalog");
return null;
}
}
If URL your enter is like - http://localhost:8080/catalog/a/b/c/d?key=value
The above code will log:
http://localhost:8080/catalog/a/b/c/d (URL)
key=value (query string)
http://localhost:8080/catalog/a/b/c/d

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.

Error handling on controller SpringMVC

I am developing an application in jax-rs and spring mvc.
I want to notify my client each time when an default error is occured like
400, 403, 404, 405 and 415.
Controller
#Controller
#RequestMapping("/customer")
public class CustomerController {
#Autowired
CustomerService customerService;
// ........xxxxxx..............xxxxxxx................xxxxxxx.............//
#CrossOrigin
#RequestMapping(value = "/",
method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
public #ResponseBody String fetchCustomer() throws JsonProcessingException {
return new ObjectMapper().writeValueAsString(customerService.fetchAllCustomer());
}
// ........xxxxxx..............xxxxxxx................xxxxxxx.............//
}
Client
$http({
method: "GET",
contentType: "application/json",
url: baseUrl + '/customer'
}).success(function (response) {
console.log(response);
// you can also use
console.log(JSON.stringify(response);
}).error(function (response) {
console.log(response);
});
When i request a service from client i want to send response back with status code and custom message.
Example
When i defind method = post on controller and from client i send request as get service should return message like
error:{
Status Code: 405,
Message: Invalid Method
url: error/405
}
Check this out for reference.
Define a method for handling the specific error scenario and annotate it as #ExceptionHandler. The exception in your scenario (request method not supported) is HttpRequestMethodNotSupportedException.class. You can create more generic handler methods using Throwable, Exception etc.
In order to prevent duplication of error handling across controllers, one convenient way is to define all handlers in single class and use #ControllerAdvice on that. This way, all handlers will be applied to all controllers.
Do not return a String but return a org.springframework.http.ResponseEntity.
You can add status codes to this object
ResponseEntity<String> responseEntity = new ResponseEntity<String>("This is a response", HttpStatus.INTERNAL_SERVER_ERROR);
return responseEntity;
So your method signature will also change as below
#RequestMapping(value = "/", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public #ResponseBody ResponseEntity<String> fetchCustomer() throws JsonProcessingException {
try {
String str = new ObjectMapper().writeValueAsString(customerService.fetchAllCustomer());
return new ResponseEntity<String>(str, HttpStatus.OK);
}
catch (Exception e) {
return new ResponseEntity<String>(e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
}
}
If there is an error, you can either use controller advice or catch the exception and update the ResponseEntity appropriately

Spring MVC Controller method mapping using form body

I'm building a small application to serve as a client for some third party library here at work. The API states that a Webhookis needed to respond some asynchronous events, but all their methods have the very same signature, apart from a changing _method field between the calls. For example, I have a _method = ping, media, etc.
I'd like to have separate methods on my controller to respond for each one of these methods. If the app allowed me to specify different URLs for each method it would be easy to use Spring MVC's #RequestMapping for each one of them. But I have to specify a single endpoint to receive all calls.
Is there a way (for example using Spring's HttpMessageConverter or something like that) to map different controller methods based on what the Request Body is? I've already tried with #RequestBody, #RequestParam but didn't seem to find anything.
I really, really didn't want to use a bunch of case, switch methods on a front controller to dispatch actions based on my _method field that comes with my POST data, so I happen to believe someone had this problem before and solved it intelligently.
Thanks a lot!
Edit 1: Providing source code
#Controller
#RequestMapping("/webhooks")
public class WebhookController {
#RequestMapping(method = RequestMethod.POST, params = {"_method=ping"})
#ResponseBody
public String ping(){
return "pong";
}
#RequestMapping(method = RequestMethod.POST, params = {"_method=media"})
#ResponseBody
public String media(){
return "media";
}
}
This is the answer:
{
"timestamp": 1440875190389,
"status": 400,
"error": "Bad Request",
"exception": "org.springframework.web.bind.UnsatisfiedServletRequestParameterException",
"message": "Parameter conditions \"_method=ping\" not met for actual request parameters: ",
"path": "/webhooks"
}
Right, I got it working. The answer is a bit tricky so I wanted to register it here should anyone have such problem.
#Neil McGuigan pointed me on the right direction on his comment but I didn't pay attention at first. The main culprit here is a very, very, very bad API design on our remote application's side.
_method is a field used to specify non-standard HTTP verbs such as PUT, PATCH, DELETE, TRACE and so on. This field is filtered by HiddenHttpMethodFilter and the HttpServletRequest is wrapped with this 'new' method. You can see at the file's source how it works.
As I wanted this _method field to get thru the filter without modifying the whole request (and causing the errors because there's no such verb as pingor message on `RequestMethod) I firstly had to deactivate the filter. This could be done by two ways:
I could stop Spring Boot from automagically configuring Spring MVC, skipping WebMvcAutoConfiguration from being loaded when the ApplicationContext was loaded. As you can imagine this is a BIG, BIG, BIIIIG NO because, well, things could happen.
I could use a FilterRegistrationBean to disable the bad filter. Pretty simple and straightforward, this was the method I chose to use:
#Bean
public FilterRegistrationBean registration(HiddenHttpMethodFilter filter) {
FilterRegistrationBean registration = new FilterRegistrationBean(filter);
registration.setEnabled(false);
return registration;
}
Last but not least, I decided to give HiddenHttpMethodFilter a little extension to somehow improve how the requests were getting thru. The Java EE Spec is pretty clear on the Servlet Spec Commandments where it states:
Thou should not alter your request on your side. You must respect the sender (something like that)
Though I agree with this, for the sake of my mental stability I decided to alter it anyway. To achieve this, we can use a simple HttpServletRequestWrapper, override the chosen methods and filter the original request with the wrapped part. I ended up doing something like this:
public class WhatoolsHiddenHttpMethodFilter extends OrderedHiddenHttpMethodFilter {
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
String paramValue = request.getParameter(OrderedHiddenHttpMethodFilter.DEFAULT_METHOD_PARAM);
if("POST".equals(request.getMethod()) && StringUtils.hasLength(paramValue)) {
String method = paramValue.toUpperCase(Locale.ENGLISH);
List<String> whatoolsMethods = Arrays.asList("ping", "message", "carbon", "media", "media_carbon", "ack");
if(whatoolsMethods.contains(paramValue)){
WhatoolsHiddenHttpMethodFilter.HttpMethodRequestWrapper wrapper = new WhatoolsHiddenHttpMethodFilter
.HttpMethodRequestWrapper(request, "POST", paramValue);
filterChain.doFilter(wrapper, response);
} else {
WhatoolsHiddenHttpMethodFilter.HttpMethodRequestWrapper wrapper = new WhatoolsHiddenHttpMethodFilter
.HttpMethodRequestWrapper(request, method, null);
filterChain.doFilter(wrapper, response);
}
} else {
filterChain.doFilter(request, response);
}
}
private static class HttpMethodRequestWrapper extends HttpServletRequestWrapper {
private final String method;
private final String whatoolsMethod;
public HttpMethodRequestWrapper(HttpServletRequest request, String method, String whatoolsMethod) {
super(request);
this.method = method;
this.whatoolsMethod = whatoolsMethod;
}
#Override
public String getMethod() {
return this.method;
}
#Override
public String getHeader(String name) {
if("x-whatools-method".equals(name)){
return this.whatoolsMethod;
}
return super.getHeader(name);
}
#Override
public Enumeration<String> getHeaderNames() {
List<String> names = Collections.list(super.getHeaderNames());
if(this.whatoolsMethod != null){
names.add("x-whatools-method");
}
return Collections.enumeration(names);
}
}
}
So, what this does is to wrap the request with a new x-whatools-method header when the header is in my whatoolsMethods list. With this, I can easily use #RequestMapping's headers property and map the requests to the correct controller methdods.
Back to the initial question, I'm almost sure (well, 99,95% should be completely sure but let's not risk it) the params property on #RequestMapping works only for request parameters on GET URIs, e.g http://foo.bar/?baz=42. It won't work filtering parameters sent on the request's body.
Thanks Neil for your guidance, even if small! I hope this helps someone.
You can use params in a request mapping:
#RequestMapping(value="/foo", params={"_method=ping"})
Assuming these are post parameters that is
params DOES work for POST, I promise you
Here's my controller:
#Controller
#RequestMapping("/test1")
public class ParamTestController {
#RequestMapping(method = RequestMethod.POST)
#ResponseBody String getA(){
return "A";
}
#RequestMapping(method = RequestMethod.POST, params = {"b"})
#ResponseBody String getB(){
return "B";
}
}
Here's my test:

Spring MVC #RestController and redirect

I have a REST endpoint implemented with Spring MVC #RestController. Sometime, depends on input parameters in my controller I need to send http redirect on client.
Is it possible with Spring MVC #RestController and if so, could you please show an example ?
Add an HttpServletResponse parameter to your Handler Method then call response.sendRedirect("some-url");
Something like:
#RestController
public class FooController {
#RequestMapping("/foo")
void handleFoo(HttpServletResponse response) throws IOException {
response.sendRedirect("some-url");
}
}
To avoid any direct dependency on HttpServletRequest or HttpServletResponse I suggest a "pure Spring" implementation returning a ResponseEntity like this:
HttpHeaders headers = new HttpHeaders();
headers.setLocation(URI.create(newUrl));
return new ResponseEntity<>(headers, HttpStatus.MOVED_PERMANENTLY);
If your method always returns a redirect, use ResponseEntity<Void>, otherwise whatever is returned normally as generic type.
Came across this question and was surprised that no-one mentioned RedirectView. I have just tested it, and you can solve this in a clean 100% spring way with:
#RestController
public class FooController {
#RequestMapping("/foo")
public RedirectView handleFoo() {
return new RedirectView("some-url");
}
}
redirect means http code 302, which means Found in springMVC.
Here is an util method, which could be placed in some kind of BaseController:
protected ResponseEntity found(HttpServletResponse response, String url) throws IOException { // 302, found, redirect,
response.sendRedirect(url);
return null;
}
But sometimes might want to return http code 301 instead, which means moved permanently.
In that case, here is the util method:
protected ResponseEntity movedPermanently(HttpServletResponse response, String url) { // 301, moved permanently,
return ResponseEntity.status(HttpStatus.MOVED_PERMANENTLY).header(HttpHeaders.LOCATION, url).build();
}
As the redirections are usually needed in a not-straightforward path, I think throwing an exception and handling it later is my favourite solution.
Using a ControllerAdvice
#ControllerAdvice
public class RestResponseEntityExceptionHandler
extends ResponseEntityExceptionHandler {
#ExceptionHandler(value = {
NotLoggedInException.class
})
protected ResponseEntity<Object> handleNotLoggedIn(
final NotLoggedInException ex, final WebRequest request
) {
final String bodyOfResponse = ex.getMessage();
final HttpHeaders headers = new HttpHeaders();
headers.add("Location", ex.getRedirectUri());
return handleExceptionInternal(
ex, bodyOfResponse,
headers, HttpStatus.FOUND, request
);
}
}
The exception class in my case:
#Getter
public class NotLoggedInException extends RuntimeException {
private static final long serialVersionUID = -4900004519786666447L;
String redirectUri;
public NotLoggedInException(final String message, final String uri) {
super(message);
redirectUri = uri;
}
}
And I trigger it like this:
if (null == remoteUser)
throw new NotLoggedInException("please log in", LOGIN_URL);
if you #RestController returns an String you can use something like this
return "redirect:/other/controller/";
and this kind of redirect is only for GET request, if you want to use other type of request use HttpServletResponse

Resources