0

I'm working on a Java Servlet Web Application where I want to create a login for the management. For that, I used mapping /management/login to render the login page and the same route to post the login request. Both use the same LoginController but I have defined doGet and doPost methods there. doGet works fine but when I hit login it also comes to doPost method but renders,

HTTP Status 405 – Method Not Allowed
Type: Status Report

Message: HTTP method POST is not supported by this URL

Description: The method received in the request-line is known by the origin server but not supported by the target resource.

Apache Tomcat/10.1.8

I just want to render the error message below the login form.

I referred to a couple of Stack Overflow answers, but none of them are working for me.

Below is my code,

LoginController.java


public class LoginController extends ControllerBase {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
        if (isAuthenticated(req)) {
            res.sendRedirect("/management/dashboard");
            return;
        }

        RequestDispatcher dispatcher = req.getRequestDispatcher("WEB-INF/views/management/login.jsp");
        dispatcher.forward(req, res);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
        String email = req.getParameter("email");
        String password = req.getParameter("password");

        if (Utils.isNullOrEmpty(email) || Utils.isNullOrEmpty(password)) {
            req.setAttribute("error", "Invalid credentials");
            RequestDispatcher dispatcher = req.getRequestDispatcher("WEB-INF/views/management/login.jsp");
            dispatcher.forward(req, res);
            return;
        }

        login(req);
        Console.log("User logged in");
        res.sendRedirect("/management/dashboard");
    }
}

Login.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" isELIgnored="false" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@taglib prefix="t" tagdir="/WEB-INF/tags" %>

<t:public_layout>
    <jsp:attribute name="title" trim="true">Management Login</jsp:attribute>
    <jsp:body>

        <div class="container p-5">
            <div class="row">
                <div class="col-md-8">
                    <h4>
                        Management Login
                    </h4>
                    <p>
                        Welcome back! Sign in to your account to access all the features.
                    </p>
                </div>

                <div class="col-md-4">
                    <form action="/management/login" method="post">
                        <div class="mb-3">
                            <label for="email" class="form-label">Email address</label>
                            <input type="email" class="form-control" name="email" id="email" placeholder="name@example.com"
                                         autocomplete="username">
                        </div>
                        <div class="mb-3">
                            <label for="password" class="form-label">Password</label>
                            <input type="password" class="form-control" name="password" id="password" placeholder="Password"
                                         autocomplete="current-password">
                        </div>
                        <div class="mb-3">
                            <button type="submit" class="btn btn-primary w-100">Sign in</button>
                        </div>

                        <c:if test="${not empty error}">
                            <div class="alert alert-danger" role="alert">
                                    ${error}
                            </div>
                        </c:if>
                    </form>
                </div>
            </div>
        </div>

    </jsp:body>
</t:public_layout>

web.xml

<!DOCTYPE web-app PUBLIC
    "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
    "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
    <display-name>Archetype Created Web Application</display-name>

    <servlet>
        <servlet-name>HomeController</servlet-name>
        <display-name>HomeController</display-name>
        <description>HomeController</description>
        <servlet-class>com.dinindu.oas.controller.HomeController</servlet-class>
    </servlet>
    <servlet>
        <servlet-name>ManagementController</servlet-name>
        <display-name>ManagementController</display-name>
        <description>ManagementController</description>
        <servlet-class>com.dinindu.oas.controller.management.ManagementController</servlet-class>
    </servlet>
    <servlet>
        <servlet-name>ManagementLoginController</servlet-name>
        <display-name>ManagementLoginController</display-name>
        <description>ManagementLoginController</description>
        <servlet-class>com.dinindu.oas.controller.management.LoginController</servlet-class>
    </servlet>
    <servlet>
        <servlet-name>ManagementDashboardController</servlet-name>
        <display-name>ManagementDashboardController</display-name>
        <description>ManagementDashboardController</description>
        <servlet-class>com.dinindu.oas.controller.management.DashboardController</servlet-class>
    </servlet>
    <servlet>
        <servlet-name>LogoutController</servlet-name>
        <display-name>LogoutController</display-name>
        <description>LogoutController</description>
        <servlet-class>com.dinindu.oas.controller.LogoutController</servlet-class>
    </servlet>

    <servlet-mapping>
        <servlet-name>HomeController</servlet-name>
        <url-pattern/>
    </servlet-mapping>

    <servlet-mapping>
        <servlet-name>ManagementController</servlet-name>
        <url-pattern>/management/*</url-pattern>
    </servlet-mapping>

    <servlet-mapping>
        <servlet-name>ManagementLoginController</servlet-name>
        <url-pattern>/management/login</url-pattern>
    </servlet-mapping>

    <servlet-mapping>
        <servlet-name>ManagementDashboardController</servlet-name>
        <url-pattern>/management/dashboard</url-pattern>
    </servlet-mapping>

    <servlet-mapping>
        <servlet-name>LogoutController</servlet-name>
        <url-pattern>/logout</url-pattern>
    </servlet-mapping>

    <servlet-mapping>
        <servlet-name>default</servlet-name>
        <url-pattern>/assets/*</url-pattern>
    </servlet-mapping>
</web-app>

enter image description here

starwarswii
  • 2,187
  • 1
  • 16
  • 19
0xdw
  • 3,755
  • 2
  • 25
  • 40
  • @tgdavies still the same :( – 0xdw Aug 30 '23 at 04:57
  • In fact, exact matches take precedence: https://stackoverflow.com/questions/11872973/what-happens-if-i-have-two-servlet-mappings-in-web-xml-that-match-a-request – tgdavies Aug 30 '23 at 05:06

1 Answers1

2

The problem is in this part

<servlet-mapping>
    <servlet-name>ManagementController</servlet-name>
    <url-pattern>/management/*</url-pattern>
</servlet-mapping>

<servlet-mapping>
    <servlet-name>ManagementLoginController</servlet-name>
    <url-pattern>/management/login</url-pattern>
</servlet-mapping>

Url pattern /management/* matches the request url /management/login and occurs prior the servlet mapping of ManagementLoginController servlet in web.xml. Hence ManagementController is the servlet responsible for processing of the request.

Here's an extract from Servlet 2.5 specification chapter SRV.11:

The path used for mapping to a servlet is the request URL from the request object minus the context path and the path parameters. The URL path mapping rules below are used in order. The first successful match is used with no further matches attempted:

  1. The container will try to find an exact match of the path of the request to the path of the servlet. A successful match selects the servlet.
  2. The container will recursively try to match the longest path-prefix. This is done by stepping down the path tree a directory at a time, using the ’/’ character as a path separator. The longest match determines the servlet selected.
  3. If the last segment in the URL path contains an extension (e.g. .jsp), the servlet container will try to match a servlet that handles requests for the extension. An extension is defined as the part of the last segment after the last ’.’ character.
  4. If neither of the previous three rules result in a servlet match, the container will attempt to serve content appropriate for the resource requested. If a "default" servlet is defined for the application, it will be used.

Hope it helps

Igor
  • 91
  • 3
  • Yeah, I saw the rules from @tgdaies comment. As I can understand, Java Servlet cannot handle child routes properly. Correct? :( – 0xdw Aug 30 '23 at 13:43
  • @0xdw I believe it can, you just need to put the servlet-mappings in order of their specificity(more specific first). In your case you should put ManagementLoginController mapping before ManagementController mapping – Igor Aug 31 '23 at 07:07