Project Trouble Shooting : 405 Method Not Allowed By Ambiguous URL mapping -> "/login", "/logout"
#Foreword
In the previous team project, I didn't have the reference point for specific Url while designing API. Team leader designed API, and I followed it. What's more, at that time, just following them wasn't easy for me, I couldn’t afford to keep them up.
Anyway, through this trouble shooting, I've realized that there must be a reason if a specific formula is popular.
#Problem : 405 Error with the this code
AuthController Code For "/login", "/logout"
@RestController
@RequestMapping
@RequiredArgsConstructor
public class AuthController {
public static final String ACCESS_TOKEN_COOKIE_NAME = "accessToken";
private final CookieUtils cookieUtils;
private final AuthService authService;
@PostMapping("/login")
public ResponseEntity<ResponseDTO<Void>> login(
@RequestBody @Valid LoginRequest request,
HttpServletResponse httpServletResponse
) {
Member member = authService.findByEmail(request.email());
TokenDTO tokenDTO = authService.login(member, request);
Cookie accessToken = cookieUtils.makeCookie(
ACCESS_TOKEN_COOKIE_NAME, tokenDTO.accessToken()
);
httpServletResponse.addCookie(accessToken);
return ResponseEntity.ok(ResponseDTO.ok());
}
@PostMapping("/logout")
public ResponseEntity<ResponseDTO<Void>> logout(HttpServletResponse httpServletResponse) {
Cookie expiredCookie = cookieUtils.expireCookie(ACCESS_TOKEN_COOKIE_NAME);
httpServletResponse.addCookie(expiredCookie);
return ResponseEntity.ok(ResponseDTO.ok());
}
}
#405 Not Support - Reason
I thought it's from this Incorrect URL mapping :
Current URL
Login Url -> POST localhost:8080/login
Logout Url -> POST localhost:8080/logout
@RestController
@RequestMapping
@RequiredArgsConstructor
public class AuthController {
@PostMapping("/login")
public ResponseEntity<ResponseDTO<Void>> login(~~~~
)
@PostMapping("/logout")
public ResponseEntity<ResponseDTO<Void>> logout(~~~~
)
: What we're getting in the previous senario is an HTTP response with the 405 Status Code, which is a client error indicating that the server doesn't support the method/verb sent in request.
As the name here suggests, the reason for this error is sending the request with a non-supported method. (I thought, in my case, it's caued by Ambiguous URL Mapping with similar name.)
#Solution : Defining Consise URL
To make Url clear, add "/auth" Url on @RequestMapping
As we can expect, we can solve this by defining concise URL mapping for POST "/login", "/logout" explicitly, adding @RequestMapping("/auth").
Modified URL
Login Url -> POST localhost:8080/auth/login
Logout Url -> POST localhost:8080/auth/logout
@RestController
@RequestMapping("/auth)
@RequiredArgsConstructor
public class AuthController {
@PostMapping("/login")
public ResponseEntity<ResponseDTO<Void>> login(~~~~
)
@PostMapping("/logout")
public ResponseEntity<ResponseDTO<Void>> logout(~~~~
)
I guess the previous URLs were similar to each other, so it didn't work well.
Below is the modified whole source code.
import com.FC.SharedOfficePlatform.domain.auth.controller.request.LoginRequest;
import com.FC.SharedOfficePlatform.domain.auth.dto.TokenDTO;
import com.FC.SharedOfficePlatform.domain.auth.service.AuthService;
import com.FC.SharedOfficePlatform.domain.member.entity.Member;
import com.FC.SharedOfficePlatform.global.common.CookieUtils;
import com.FC.SharedOfficePlatform.global.util.ResponseDTO;
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/auth")
@RequiredArgsConstructor
public class AuthController {
public static final String ACCESS_TOKEN_COOKIE_NAME = "accessToken";
private final CookieUtils cookieUtils;
private final AuthService authService;
@PostMapping("/login")
public ResponseEntity<ResponseDTO<Void>> login(
@RequestBody @Valid LoginRequest request,
HttpServletResponse httpServletResponse
) {
Member member = authService.findByEmail(request.email());
TokenDTO tokenDTO = authService.login(member, request);
Cookie accessToken = cookieUtils.makeCookie(
ACCESS_TOKEN_COOKIE_NAME, tokenDTO.accessToken()
);
httpServletResponse.addCookie(accessToken);
return ResponseEntity.ok(ResponseDTO.ok());
}
@PostMapping("/logout")
public ResponseEntity<ResponseDTO<Void>> logout(HttpServletResponse httpServletResponse) {
Cookie expiredCookie = cookieUtils.expireCookie(ACCESS_TOKEN_COOKIE_NAME);
httpServletResponse.addCookie(expiredCookie);
return ResponseEntity.ok(ResponseDTO.ok());
}
}
+Reference
(Ambiguous Mapping)
@RequestMapping(
value = "/employees",
produces = "application/json",
method = {RequestMethod.GET, RequestMethod.PUT}) ...Copy
Alternatively, we can define the new method/mapping separately:
@RequestMapping(value = "/employees",
produces = "application/json",
method = RequestMethod.GET)
public List<Employee> getEmployees() {
// Code to return the list of employees
}
@RequestMapping(value = "/employees",
produces = "application/json",
method = RequestMethod.PUT)
public Employee updateEmployee(@RequestBody Employee employee) {
// Code to update an employee
}