Root path works for all HTTP methods - java

I have a spring boot application.yml where context path is mentioned as below.
server:
servlet:
contextPath: /home
I have the following RootController.java
#RestController
#Slf4j
#RequestMapping("/")
public class RootController {
#RequestMapping(method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public String getContent() throws IOException {
return "This is root content";
}
}
But the root endpoint works for all HTTP methods like 'POST', 'PUT', etc., even though method is mentioned as 'GET' in #RequestMapping. It is supposed to work only for GET method. I'm not sure about the problem. Can someone help fix this ?

Related

Log url to Spring controller from inside it

Suppose you have a Spring MVC controller, something like this
#Controller
public class RestController {
#GetMapping(value = "/test")
public #ResponseBody Test getTestData(...) {
// console log path to controller: http://localhost:80/app/test
return testData;
}
}
Is it possible to log/print from inside the controller the url to it? In the example above the output would be something like https://localhost:80/app/test
Using .getRequestUrl from the servlet is not behaving correctly.
You can inject UriComponentsBuilder as parameter then use the method toUriString(). From the documentation, it is used to build a relative URI from the current request’s, this should work as your are expected Doc.
#Controller
public class RestController {
...
#GetMapping(value = "/test")
public #ResponseBody Test getTestData(UriComponentsBuilder ucb, ...) {
LOGGER.debug(ucb.toUriString());
// console log path to controller: http://localhost:80/app/test
return testData;
}
...
}

PageNotFound - Request method 'GET' not supported

I am getting this error while calling an API from postman, after I hosted my spring app in VM. Locally it works. But Get methods in my VMs are working.
[http-nio-8081-exec-4] PageNotFound - Request method 'GET' not supported
My controller method looks like this:
#RestController
#RequestMapping("/orders/")
public class OrdersController {}
#PostMapping(value = "create", produces = "text/plain")
private String createOrder(#RequestBody POCreationRequest request) throws ParseException {
The API request running forever and dont get any response. I found the exception in my log. Any idea on this issue?
You created two urls there:
url/orders/ -> accepts get/post/etc... (though its not implemented)
url/orders/create -> accepts post
#RestController
#RequestMapping("/orders")
public class OrdersController {
#PostMapping(value = "create", produces = "text/plain")
private String createOrder(#RequestBody POCreationRequest request) throws ParseException {
System.out.println(request)}
}
You can try the above code.
You are trying to make a GET request on an only POST endpoint, thus then not loading the page. Your endpoint should be of type GET. You can also have the same endpoint for GET and POST requests as follows:
#RestController
#RequestMapping("/orders/")
public class OrdersController {}
#PostMapping(value = "create", produces = "text/plain")
private String createOrder(#RequestBody POCreationRequest request) throws ParseException {
//Parse post requests
}
#GetMapping(value= "create")
private String servePage() {
return create; //create is the name of the html view.
}
Now when going to localhost:8080/orders/create it should serve the view.
You can also make the GET mapping return a JSON object by:
#GetMapping(value= "create")
private String serveJSON() {
return "{\"hello\": \"world\"}"; //The string is treated as JSON and not as a view.
}

Reactive Spring does not support HttpServletRequest as parameter in REST endpoint?

I created a RestController which look like this :
#RestController
public class GreetingController {
#RequestMapping(value = "/greetings", method = RequestMethod.GET)
public Mono<Greeting> greeting(HttpServletRequest request) {
return Mono.just(new Greeting("Hello..." + request.toString()));
}
}
Unfortunately when I try to hit the "greetings" endpoint I get an exception :
java.lang.IllegalStateException: No resolver for argument [0] of type
[org.apache.catalina.servlet4preview.http.HttpServletRequest]
I am using
compile('org.springframework.boot.experimental:spring-boot-starter-web-reactive')
How to fix this ?
Link to full stack-trace. Link to build.gradle
----------EDIT----------
Using the interface. Now getting :
java.lang.IllegalStateException: No resolver for argument [0] of type
[javax.servlet.http.HttpServletRequest] on method (rest is same)
You should never use the Servlet API in a Spring Reactive Web application. This is not supported and this is making your app container-dependent, whereas Spring Web Reactive can work with non-Servlet runtimes such as Netty.
Instead you should use the HTTP API provided by Spring; here's your code sample with a few changes:
import org.springframework.http.server.reactive.ServletServerHttpRequest;
#RestController
public class GreetingController {
#GetMapping("/greetings")
public Mono<Greeting> greeting(ServerHttpRequest request) {
return Mono.just(new Greeting("Hello..." + request.getURI().toString()));
}
}
You can inject either ServerWebExchange or directly ServerHttpRequest / ServerHttpResponse.
I went deep into the call hierarchy and found that there is this class
InvocableHandlerMethod, in package org.springframework.web.reactive.result.method
, which has :
private List<HandlerMethodArgumentResolver> resolvers = new ArrayList<>();
There is a resolveArguments() method in this class, which is called to "resolve the arguments". Unfortunately there is no resolver for
javax.servlet.http.HttpServletRequest in this list.
There is however a ServerWebExchangeArgumentResolver which is able to resolve ServletServerHttpRequest, from this I can extract the HttpServletRequest. Yeaaa....
So the endpoint looks like :
#RequestMapping(value = "/greetings", method = RequestMethod.GET)
public Mono<Greeting> greeting(ServletServerHttpRequest servletServerHttpRequest) {
HttpServletRequest httpServletRequest = servletServerHttpRequest.getServletRequest();
.
.
.
}
It is important that the ServletServerHttpRequest be from the package org.springframework.http.server.reactive

SpringBoot and #CrossOrigin annotation with POST only

I have the following class
#RestController
#RequestMapping("/bets")
#CrossOrigin
public class BetRestController {
#Autowired
private BetController betController;
#ResponseBody
#RequestMapping(method=RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE)
public BetResource betOnGame(#RequestBody #Valid BetResource betResource) {
return BetTranslator.from(betController.betOnGame(BetTranslator.from(betResource)));
}
#ResponseBody
#RequestMapping(method = RequestMethod.GET)
public List<BetResource> getAllBets() {
return betController.getAllBets().stream().map(BetTranslator::from).collect(Collectors.toList());
}
}
The problem is that when I try to access the POST method I am getting:
XMLHttpRequest cannot load http://localhost:8080/bets. Origin http://localhost:3000 is not allowed by Access-Control-Allow-Origin.
but GET method works.
What I have wrong in my configuration or maybe there is a bug in #CrossOrigin annotation processing?!
If I understand your problem correctly, it is possible to specify CORS origin on one specific method.
Sprint Boot documentation : https://spring.io/guides/gs/rest-service-cors/
Enabling CORS
Controller method CORS configuration
So that the RESTful web service will include CORS access control
headers in its response, you just have to add a #CrossOrigin
annotation to the handler method:
Here's the example from Spring boot website :
#CrossOrigin(origins = "http://localhost:9000")
#RequestMapping("/greeting")
public #ResponseBody Greeting greeting(#RequestParam(required=false, defaultValue="World") String name) {
System.out.println("==== in greeting ====");
return new Greeting(counter.incrementAndGet(), String.format(template, name));
}

How configure a RESTful controller in Spring 3 with annotations?

I am trying to create a RESTful controller using Spring 3.0. The controller is for a management API for a portal application. The operations I want to perform are:
GET /api/portals to list all the portals
POST /api/portals to create a new portal
GET /api/portals/{id} to retrieve an existing portal
PUT /api/portals/{id} to update an existing portal
DELETE /api/portal/{id} to delete an existing portal
After annotating the controller as illustrated below I find the the operations to list all the portals or create a new portal do not get mapped.
So my questions are:
Have I annotated the class correctly?
Am I following the correct conventions for implementing a RESTful web service?
Might there be something broken in Spring?
The code extract below shows how I have annotated my class:
#Controller
#RequestMapping("/api/portals")
public final class PortalAPIController
{
private final static Logger LOGGER = LoggerFactory.getLogger(PortalAPIController.class);
#RequestMapping(value = "/", method = RequestMethod.GET)
public String listPortals(final Model model)
{
PortalAPIController.LOGGER.debug("Portal API: listPortals()");
.
.
return "portals";
}
#RequestMapping(value = "/", method = RequestMethod.POST)
public String createPortal(#RequestBody final MultiValueMap<String, String> portalData, final Model model)
{
PortalAPIController.LOGGER.debug("Portal API: createPortal()");
.
.
return "portal";
}
#RequestMapping(value = "/{id}", method = RequestMethod.GET)
public String getPortal(#PathVariable("id") final String portalId, final Model model, final HttpServletResponse response)
throws IOException
{
PortalAPIController.LOGGER.debug("Portal API: getPortal()");
.
.
return "portal";
}
#RequestMapping(value = "/{id}", method = RequestMethod.PUT)
public String updatePortal(#PathVariable("id") final String portalId,
#RequestBody final MultiValueMap<String, String> portalData, final Model model, final HttpServletResponse response)
throws IOException
{
PortalAPIController.LOGGER.debug("Portal API: updatePortal()");
.
.
return "portal";
}
#RequestMapping(value = "/{id}", method = RequestMethod.DELETE)
public String deletePortal(#PathVariable("id") final String portalId, final Model model, final HttpServletResponse response)
throws IOException
{
PortalAPIController.LOGGER.debug("Portal API: deletePortal()");
.
.
return "portal";
}
.
.
}
During start-up I am seeing that Spring things it has registered the end-points:
2010-02-19 01:18:41,733 INFO [org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping] - Mapped URL path [/api/portals/] onto handler [com.btmatthews.mars.portal.web.controller.PortalAPIController#141717f]
2010-02-19 01:18:41,734 INFO [org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping] - Mapped URL path [/api/portals/{id}] onto handler [com.btmatthews.mars.portal.web.controller.PortalAPIController#141717f]
2010-02-19 01:18:41,734 INFO [org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping] - Mapped URL path [/api/portals/{id}.*] onto handler [com.btmatthews.mars.portal.web.controller.PortalAPIController#141717f]
2010-02-19 01:18:41,735 INFO [org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping] - Mapped URL path [/api/portals/{id}/] onto handler [com.btmatthews.mars.portal.web.controller.PortalAPIController#141717f]
2010-02-19 01:18:41,735 INFO [org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping] - Mapped URL path [/api/portals] onto handler [com.btmatthews.mars.portal.web.controller.PortalAPIController#141717f]
2010-02-19 01:18:41,735 INFO [org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping] - Mapped URL path [/api/portals.*] onto handler [com.btmatthews.mars.portal.web.controller.PortalAPIController#141717f]
But when I try to invoke my API using cURL
curl http://localhost:8080/com.btmatthews.minerva.portal/api/portals/
or
curl http://localhost:8080/com.btmatthews.minerva.portal/api/portals
I get the following errors:
2010-02-19 01:19:20,199 WARN [org.springframework.web.servlet.PageNotFound] - No mapping found for HTTP request with URI [/com.btmatthews.minerva.portal/api/portals] in DispatcherServlet with name 'portal'
2010-02-19 01:19:32,360 WARN [org.springframework.web.servlet.PageNotFound] - No mapping found for HTTP request with URI [/com.btmatthews.minerva.portal/api/portals/] in DispatcherServlet with name 'portal'
I get the same problem when I try to do a create:
curl -F ...... --request POST http://localhost:8080/com.btmatthtews.minerva/api/portals
But if try to operate on an existing resource (retrieve, update or delete) it works okay.
Update: The solution was provided in a comment by #axtavt. I was using <url-pattern>/api/*</url-pattern> in my web.xml servlet mapping. It needed to be changed to <url-pattern>/</url-pattern>
Double check the url-pattern in your web.xml and compare it to your curl argument.
Here is an example I wrote which walks you through the whole Spring MVC process.
The URL you post in your curl excerpt
http://localhost:8080/portal/api/portals
does not match the URL in the Spring warning
/com.btmatthews.minerva.portal/api/portals
Without knowing how your webapp is set up, what context path it's at, what the Spring context looks like, etc., it's pretty hard to diagnose but this sounds like a big clue to me.
You Have done few Mistakes.
#RequestMapping(value = "/", method = RequestMethod.GET)
public String listPortals(final Model model)
{
}
Over here the method must accept some parameter Model, but that's not passed.
you must write that inside method. or if u want to pass that, u must sent it as path variable or as a request Param.
#RequestMapping(value = "/{id}", method = RequestMethod.GET)
public String getPortal(#PathVariable("id")
here the correct syntax is #PathVariable(value="id")
And if this doesn't work
then try this:
#RequestMapping(value = "/SOMETHING", method = RequestMethod.GET)
In Spring 3, the Rest Controller is no more than a regular controller (#Component) and the only difference is that a Rest controller returns JSON/XML instead of "Views". So I think you are doing ok with respect to annotations. However, two missing things I can't see:
1- Every method needs to have a "produce" value (for content negotiation):
#RequestMapping(value = "/{id}", method = RequestMethod.GET, produces = "application/json")
2- You need an ObjectMapper of some type (ex: Marshaller and Unmarshaller) to be able to map objects to JSON/XML and vice-versa. Those two should be in your servlet config, instead of configuring a view resolver.
And that should be it.

Categories