POST not supported in custom Servlet as #Bean in Spring Boot - java

I'm trying to integrate a 3rd party servlet into my Spring Boot application and when I try to submit a POST to the servlet, I see the following in the logs:
PageNotFound: Request method 'POST' not supported
I've made a simple test that show this. I started using an auto generated Spring Boot project. Then I created the following Servlet:
public class TestServlet extends HttpServlet {
private static final Logger log = LoggerFactory.getLogger(TestServlet.class);
#Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPost(req, resp); //To change body of generated methods, choose Tools | Templates.
log.info("doPost was called!!");
}
}
Then I my created Configuration like so:
#Configuration
public class ServletConfig {
#Bean //exposes the TestServlet at /test
public Servlet test() {
return new TestServlet();
}
}
Then I run the application within Tomcat7. I see in the logs:
ServletRegistrationBean: Mapping servlet: 'test' to [/test/]
Then I try to hit the endpoint with cUrl like so:
curl -v http://localhost:8080/test -data-binary '{"test":true}'
or
curl -XPOST -H'Content-type: application/json' http://localhost:8080/test -d '{"test":true}'
I've tried adding a #RequestMapping, but that didn't work either. Can anyone help me figure out how to support another Servlet inside my Spring Boot application?
You can find the sample application here: https://github.com/andrewserff/servlet-demo
Thanks!

From my previous experiences you have to call the servlet with a slash at the end (like http://localhost:8080/test/). If you don't put the slash at the end, the request is routed to the servlet mapped to /, which is by default the DispatcherServlet from Spring (your error message comes from that servlet).

The TestServlet#doPost() implementation calls the super.doPost() - which always sends a 40x error (either 405 or 400 depending on the HTTP Protocol used).
Here's the implementation:
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
String protocol = req.getProtocol();
String msg = lStrings.getString("http.method_post_not_supported");
if (protocol.endsWith("1.1")) {
resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);
} else {
resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
}
}
A Servlet can be registered using two ways:
registering the Servlet as a Bean (your approach - which should be fine) or
using a ServletRegistrationBean:
#Configuration
public class ServletConfig {
#Bean
public ServletRegistrationBean servletRegistrationBean(){
return new ServletRegistrationBean(new TestServlet(), "/test/*");
}
}
The slightly changed Servlet:
public class TestServlet extends HttpServlet {
private static final Logger log = LoggerFactory.getLogger(TestServlet.class);
#Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// super.doPost(req, resp);
log.info("doPost was called!!");
}
}

Related

Adding separate custom Servlet in Vaadin 23 - Spring 2.7.1

This question was already asked, however since then all answers (that I could found) are no longer valid.
Essentially I want to implement a website with Vaadin (V23), that communicates with a WebApp via POST requests that is running on another server (physically). To do it, I want to create separate Servlet that would handle the communication (receiving side) with another Server. Let's say, this is not implemneted version:
#WebServlet(urlPatterns = "/communication", name = "QuizServlet", asyncSupported = true)
public class QuizServlet extends HttpServlet {
#Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.sendError(400, "Not implemented");
}
#Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.sendError(400, "Not implemented");
}
}
The problem is however, that I always get redirected to default dispatcher Servlet, and it seems, regardless of what I do:
SpringVaadinServlet was deprecated and no longer exists, extending VaadinServlet does not work.
Changing mappings in properties (vaadin.url-mapping=) also does not work, I just get redirected to this new mapping in all cases.
Trying to do servlets on separate ports yields same redirection on all ports, even if explicitly registering my custom Servlet on the Connector, with separate Sevice (WebMvcConfigurer Tomcat configuration). Answer from this post, also too old.
Registering servlet directly also does not do anything (by implementing WebApplicationInitializer).
There for the question, how to make use of two different servlets with new Vaadin 23 and Spring Boot 2.7.1?
I have found some kind of a solution to my problem. Namely on startup of my BootAplication, I am also starting the second separate Tomcat server that uses my custom Servlet :
#Service
public class QuizServer {
private final Log log = LogFactory.getLog(QuizServer.class);
#PostConstruct
public void startServer() throws IOException, LifecycleException {
start();
}
private void start() throws IOException, LifecycleException {
Tomcat tomcat = new Tomcat();
String contextPath = "/";
String appBase = new File(".").getAbsolutePath();
Context ctx = tomcat.addContext(contextPath, appBase);
Tomcat.addServlet(ctx, "quizServlet", new QuizServlet());
ctx.addServletMappingDecoded("/*", "quizServlet");
tomcat.setPort(8085);
tomcat.start();
tomcat.getConnector();
log.info("Quiz server started");
}
}
#WebServlet(urlPatterns = "/*", name = "quizServlet", asyncSupported = true)
public class QuizServlet extends HttpServlet {
#Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.getWriter().println("Test");
}
#Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.sendError(400, "Not implemented");
}
}
It is a bit crude though, since ideally, it shouldn't require a separate server.

How to handle 401 with spring boot with angular 8

I want to be able to handle 401 and show a specific page in angular 8 but currently only showing index.html file
Things to mind
Angular is the view for the spring boot so its not a separate application
I am not using spring security. Im just using filters in spring to determine if to be authorize
This is my filter.
#Component
public class CustomSessionFilter extends OncePerRequestFilter {
protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
HttpServletRequest req = (HttpServletRequest) httpServletRequest;
if (!req.getRequestURI().startsWith("/sample/path")){
HttpServletResponse httpResponse = (HttpServletResponse) httpServletResponse;
httpResponse.setContentType("application/json");
httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
return;
}
}
Maybe its relevant that i have a Controller that extend ErrorController
#CrossOrigin
#RestController
public class IndexController implements ErrorController {
private static final String PATH = "/error";
#RequestMapping(value = PATH)
public ModelAndView saveLeadQuery() {
return new ModelAndView("forward:/");
}
#Override
public String getErrorPath() {
return PATH;
}
}
EDIT: I didnt use spring security because i dont need to login i just have to go through specific path and do some authentication.. and there is no user for the application
I just wanted to place my 0.02$ here.
In order to secure a route in your application, you can create a Security Config that extends WebSecurityConfigurerAdapter, where you can protect certain routes through either antMatchers or mvcMathchers (second is recommended). Furthermore, there you can declare a set of roles or conditions (via Spring Expression Language) that will automatically throw a 401 Error in case a user that does not have an access is trying to access the route.
More about that you can find here https://www.baeldung.com/security-spring
As of ErrorController, I believe that controller advice would be more suitable for this use case. It pretty much intercepts all errors that you declare and can return more informative and generic response :)
More about that https://spring.io/blog/2013/11/01/exception-handling-in-spring-mvc
As

Cannot register a servlet which doesn't have mapping in web.xml by ServletRunner?

I am using com.meterware.servletunit.ServletRunner to initialize one of my servlets.
I try to use the next method:
ServletRunner.registerServlet(String resourceName, String servletClassName)
The resourceName, as I understand, is servlet mapping, say "/myservlet/*" or so.
But the problem is this servlet has no mapping in web.xml file and is supposed to be initialized on startup. I need to initialize this servlet in my JUnit. How can I do that?
you can simply do the following and it should work as expected
where HelloAppEngine is my servlet class
#Test
public void doGetWritesResponse() throws Exception {
ServletRunner sr = new ServletRunner();
sr.registerServlet("/hello", HelloAppEngine.class.getName());
WebClient wc = sr.newClient();
WebResponse response = wc.getResponse(http://localhost/hello");
assertEquals("content type", "text/plain", response.getContentType());
assertEquals("response text is ", "Hello app Engine", response.getText());
}
the source code for HelloAppEngine is as the following
#WebServlet(name = "HelloAppEngine", value = "/hello")
public class HelloAppEngine extends HttpServlet {
#Override
public void doGet(HttpServletRequest request, HttpServletResponse
response) throws IOException {
response.setContentType("text/plain");
response.getWriter().print("Hello app Engine");
}
}

Tomcat sends 403 at DELETE request although readonly is false

I've got a Servlet that implements the doDelete method.
When I run the application on my tomcat (8.0.0 RC1) I get a 403 response. I checked the documentation regarding this (http://tomcat.apache.org/tomcat-8.0-doc/default-servlet.html) but no change.
My Servlet:
#WebServlet(urlPatterns = "/api/binding/*",
initParams = {
#WebInitParam(name="readonly", value="false")
})
public class BindingServlet extends BasicServlet {
private static final Logger LOGGER = Logger.getLogger(BindingServlet.class);
#Override
protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws ServletException {
String request = this.getRawRequest(req);
// some code
}
}
I followd the servlet implemenation through annotations from this page: http://blog.caucho.com/2009/10/06/servlet-30-tutorial-weblistener-webservlet-webfilter-and-webinitparam/

can we change servlet filter with in-it parameters as interceptor in spring

I have a filter in one of the jar files added as reference.
My project is in spring nature. I have developed a web services. All the requests to this web services should be intercepted by my filter "HelloWorld".
This filter is there in one of the reference files.
Here I thought I would implement it as Interceptor.
The filter in reference files looks like
public class HelloWorld implements Filter {
private static final Logger _logger = Logger.getLogger(HelloWorld.class.getName());
protected String name = null;
public HelloWorld()
{
}
public HelloWorld(String name) {
this.name = name;
}
public void doFilter(ServletRequest req, ServletResponse res,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
PrintWriter out = response.getWriter();
out.println("Hello "+name);
chain.doFilter(req, res);
}
public void init(FilterConfig config) throws ServletException {
//Get init parameter
String testParam = config.getInitParameter("test");
//Print the init parameter
System.out.println("test param: " + testParam);
}
public void destroy() {
//add code to release any resource
}
//some other methods as well
}
What would be the best way to implement this. I cannot configure filter in my web.xml due to limitation in my application.
Can we directly give this HelloWorld filter as reference to interceptor, so that it behaves like interceptor.
Can we change this filter as interceptor in spring and configure in spring.xml without changing the functionality.
Apologies if my question is simple.
Thanks.

Categories

Resources