GettingStarted
GettingStarted

Reputation: 7605

Spring Boot REST API keeps saying 405 - Method Not Allowed

POSTMAN keeps telling me 405 Method Not Allowed

My Udemy class instructor did this and it worked.

@PostMapping(consumes = "application/json")
@ResponseStatus(HttpStatus.CREATED)
public Employee create(@RequestBody Employee employee) {
   return employeeRepository.save(employee);
}

The entire class is below

package com.example.pma.projectmanagement.api.controllers;


import com.example.pma.projectmanagement.dao.EmployeeRepository;
import com.example.pma.projectmanagement.entities.Employee;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/app-api/employees")
public class EmployeeApiController {
    @Autowired
    EmployeeRepository employeeRepository;

    @GetMapping
    public Iterable<Employee> getEmployees() {
        return employeeRepository.findAll();
    }

    @GetMapping("/{id}")
    public Employee getEmployeeById(@PathVariable("id") Long id) {
        return employeeRepository.findById(id).get();
    }

    @PostMapping(consumes = "application/json")
    @ResponseStatus(HttpStatus.CREATED)
    public Employee create(@RequestBody Employee employee) {
       return employeeRepository.save(employee);
    }

    @PutMapping(path = "/{id}", consumes = "application/json")
    @ResponseStatus(HttpStatus.OK)
    public Employee update(@RequestBody Employee employee) {
        return employeeRepository.save(employee);
    }

    @PatchMapping(path="/{id}", consumes = "application/json")
    @ResponseStatus(HttpStatus.OK)
    public Employee partialUpdate(@PathVariable("id") long id, @RequestBody Employee patchEmployee) {
        Employee e = employeeRepository.findById(id).get();

        if(patchEmployee.getEmail() != null) {
            e.setEmail(patchEmployee.getEmail());
        }
        if(patchEmployee.getFirstName() != null) {
            e.setFirstName(patchEmployee.getFirstName());
        }
        if(patchEmployee.getLastName() != null) {
            e.setLastName(patchEmployee.getLastName());
        }
        return employeeRepository.save(e);
    }
}

postman

I can GET but I can't POST, PUT or PATCH

UPDATE: I am using Spring Security and in Thymeleaf, I had to add

<input type="hidden" th:value="${_csrf.token}" name="_csrf" />

Do I need anything else in my JSON body to perform csrf?

I enabled DEBUG and got the log file

2022-02-04 00:09:43.972 DEBUG 29544 --- [http-nio-8080-exec-7] o.s.security.web.FilterChainProxy        : /app-api/employees/ at position 1 of 14 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter'
2022-02-04 00:09:43.972 DEBUG 29544 --- [http-nio-8080-exec-7] o.s.security.web.FilterChainProxy        : /app-api/employees/ at position 2 of 14 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
2022-02-04 00:09:43.972 DEBUG 29544 --- [http-nio-8080-exec-7] w.c.HttpSessionSecurityContextRepository : HttpSession returned null object for SPRING_SECURITY_CONTEXT
2022-02-04 00:09:43.972 DEBUG 29544 --- [http-nio-8080-exec-7] w.c.HttpSessionSecurityContextRepository : No SecurityContext was available from the HttpSession: org.apache.catalina.session.StandardSessionFacade@6638e8dc. A new one will be created.
2022-02-04 00:09:43.972 DEBUG 29544 --- [http-nio-8080-exec-7] o.s.security.web.FilterChainProxy        : /app-api/employees/ at position 3 of 14 in additional filter chain; firing Filter: 'HeaderWriterFilter'
2022-02-04 00:09:43.972 DEBUG 29544 --- [http-nio-8080-exec-7] o.s.security.web.FilterChainProxy        : /app-api/employees/ at position 4 of 14 in additional filter chain; firing Filter: 'CsrfFilter'
2022-02-04 00:09:43.972 DEBUG 29544 --- [http-nio-8080-exec-7] o.s.security.web.csrf.CsrfFilter         : Invalid CSRF token found for http://localhost:8080/app-api/employees/
2022-02-04 00:09:43.973 DEBUG 29544 --- [http-nio-8080-exec-7] o.s.s.w.header.writers.HstsHeaderWriter  : Not injecting HSTS header since it did not match the requestMatcher org.springframework.security.web.header.writers.HstsHeaderWriter$SecureRequestMatcher@4bb07a96
2022-02-04 00:09:43.973 DEBUG 29544 --- [http-nio-8080-exec-7] w.c.HttpSessionSecurityContextRepository : SecurityContext is empty or contents are anonymous - context will not be stored in HttpSession.
2022-02-04 00:09:43.973 DEBUG 29544 --- [http-nio-8080-exec-7] s.s.w.c.SecurityContextPersistenceFilter : SecurityContextHolder now cleared, as request processing completed
2022-02-04 00:09:43.973 DEBUG 29544 --- [http-nio-8080-exec-7] o.s.security.web.FilterChainProxy        : /error at position 1 of 14 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter'
2022-02-04 00:09:43.973 DEBUG 29544 --- [http-nio-8080-exec-7] o.s.security.web.FilterChainProxy        : /error at position 2 of 14 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
2022-02-04 00:09:43.973 DEBUG 29544 --- [http-nio-8080-exec-7] w.c.HttpSessionSecurityContextRepository : HttpSession returned null object for SPRING_SECURITY_CONTEXT
2022-02-04 00:09:43.973 DEBUG 29544 --- [http-nio-8080-exec-7] w.c.HttpSessionSecurityContextRepository : No SecurityContext was available from the HttpSession: org.apache.catalina.session.StandardSessionFacade@6638e8dc. A new one will be created.
2022-02-04 00:09:43.973 DEBUG 29544 --- [http-nio-8080-exec-7] o.s.security.web.FilterChainProxy        : /error at position 3 of 14 in additional filter chain; firing Filter: 'HeaderWriterFilter'
2022-02-04 00:09:43.973 DEBUG 29544 --- [http-nio-8080-exec-7] o.s.security.web.FilterChainProxy        : /error at position 4 of 14 in additional filter chain; firing Filter: 'CsrfFilter'
2022-02-04 00:09:43.973 DEBUG 29544 --- [http-nio-8080-exec-7] o.s.security.web.FilterChainProxy        : /error at position 5 of 14 in additional filter chain; firing Filter: 'LogoutFilter'
2022-02-04 00:09:43.973 DEBUG 29544 --- [http-nio-8080-exec-7] o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/error'; against '/logout'
2022-02-04 00:09:43.973 DEBUG 29544 --- [http-nio-8080-exec-7] o.s.security.web.FilterChainProxy        : /error at position 6 of 14 in additional filter chain; firing Filter: 'UsernamePasswordAuthenticationFilter'
2022-02-04 00:09:43.973 DEBUG 29544 --- [http-nio-8080-exec-7] o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/error'; against '/login'
2022-02-04 00:09:43.973 DEBUG 29544 --- [http-nio-8080-exec-7] o.s.security.web.FilterChainProxy        : /error at position 7 of 14 in additional filter chain; firing Filter: 'DefaultLoginPageGeneratingFilter'
2022-02-04 00:09:43.973 DEBUG 29544 --- [http-nio-8080-exec-7] o.s.security.web.FilterChainProxy        : /error at position 8 of 14 in additional filter chain; firing Filter: 'DefaultLogoutPageGeneratingFilter'
2022-02-04 00:09:43.973 DEBUG 29544 --- [http-nio-8080-exec-7] o.s.security.web.FilterChainProxy        : /error at position 9 of 14 in additional filter chain; firing Filter: 'RequestCacheAwareFilter'
2022-02-04 00:09:43.973 DEBUG 29544 --- [http-nio-8080-exec-7] o.s.s.w.s.HttpSessionRequestCache        : saved request doesn't match
2022-02-04 00:09:43.973 DEBUG 29544 --- [http-nio-8080-exec-7] o.s.security.web.FilterChainProxy        : /error at position 10 of 14 in additional filter chain; firing Filter: 'SecurityContextHolderAwareRequestFilter'
2022-02-04 00:09:43.973 DEBUG 29544 --- [http-nio-8080-exec-7] o.s.security.web.FilterChainProxy        : /error at position 11 of 14 in additional filter chain; firing Filter: 'AnonymousAuthenticationFilter'
2022-02-04 00:09:43.973 DEBUG 29544 --- [http-nio-8080-exec-7] o.s.s.w.a.AnonymousAuthenticationFilter  : Populated SecurityContextHolder with anonymous token: 'org.springframework.security.authentication.AnonymousAuthenticationToken@ae239419: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@fffe9938: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: CAC31DD8567E461C97442AC1C889DAD0; Granted Authorities: ROLE_ANONYMOUS'
2022-02-04 00:09:43.973 DEBUG 29544 --- [http-nio-8080-exec-7] o.s.security.web.FilterChainProxy        : /error at position 12 of 14 in additional filter chain; firing Filter: 'SessionManagementFilter'
2022-02-04 00:09:43.973 DEBUG 29544 --- [http-nio-8080-exec-7] o.s.security.web.FilterChainProxy        : /error at position 13 of 14 in additional filter chain; firing Filter: 'ExceptionTranslationFilter'
2022-02-04 00:09:43.973 DEBUG 29544 --- [http-nio-8080-exec-7] o.s.security.web.FilterChainProxy        : /error at position 14 of 14 in additional filter chain; firing Filter: 'FilterSecurityInterceptor'
2022-02-04 00:09:43.973 DEBUG 29544 --- [http-nio-8080-exec-7] o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/error'; against '/projects/new'
2022-02-04 00:09:43.973 DEBUG 29544 --- [http-nio-8080-exec-7] o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/error'; against '/projects/save'
2022-02-04 00:09:43.973 DEBUG 29544 --- [http-nio-8080-exec-7] o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/error'; against '/employees/new'
2022-02-04 00:09:43.973 DEBUG 29544 --- [http-nio-8080-exec-7] o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/error'; against '/employees/save'
2022-02-04 00:09:43.973 DEBUG 29544 --- [http-nio-8080-exec-7] o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/error'; against '/'
2022-02-04 00:09:43.973 DEBUG 29544 --- [http-nio-8080-exec-7] o.s.s.w.u.matcher.AntPathRequestMatcher  : Request '/error' matched by universal pattern '/**'
2022-02-04 00:09:43.973 DEBUG 29544 --- [http-nio-8080-exec-7] o.s.s.w.a.i.FilterSecurityInterceptor    : Secure object: FilterInvocation: URL: /error; Attributes: [permitAll]
2022-02-04 00:09:43.973 DEBUG 29544 --- [http-nio-8080-exec-7] o.s.s.w.a.i.FilterSecurityInterceptor    : Previously Authenticated: org.springframework.security.authentication.AnonymousAuthenticationToken@ae239419: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@fffe9938: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: CAC31DD8567E461C97442AC1C889DAD0; Granted Authorities: ROLE_ANONYMOUS
2022-02-04 00:09:43.973 DEBUG 29544 --- [http-nio-8080-exec-7] o.s.s.access.vote.AffirmativeBased       : Voter: org.springframework.security.web.access.expression.WebExpressionVoter@7031aa78, returned: 1
2022-02-04 00:09:43.973 DEBUG 29544 --- [http-nio-8080-exec-7] o.s.s.w.a.i.FilterSecurityInterceptor    : Authorization successful
2022-02-04 00:09:43.973 DEBUG 29544 --- [http-nio-8080-exec-7] o.s.s.w.a.i.FilterSecurityInterceptor    : RunAsManager did not change Authentication object
2022-02-04 00:09:43.973 DEBUG 29544 --- [http-nio-8080-exec-7] o.s.security.web.FilterChainProxy        : /error reached end of additional filter chain; proceeding with original chain
2022-02-04 00:09:43.973 DEBUG 29544 --- [http-nio-8080-exec-7] o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/error'; against '/projects/new'
2022-02-04 00:09:43.973 DEBUG 29544 --- [http-nio-8080-exec-7] o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/error'; against '/projects/save'
2022-02-04 00:09:43.973 DEBUG 29544 --- [http-nio-8080-exec-7] o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/error'; against '/employees/new'
2022-02-04 00:09:43.973 DEBUG 29544 --- [http-nio-8080-exec-7] o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/error'; against '/employees/save'
2022-02-04 00:09:43.973 DEBUG 29544 --- [http-nio-8080-exec-7] o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/error'; against '/'
2022-02-04 00:09:43.973 DEBUG 29544 --- [http-nio-8080-exec-7] o.s.s.w.u.matcher.AntPathRequestMatcher  : Request '/error' matched by universal pattern '/**'
2022-02-04 00:09:43.974 DEBUG 29544 --- [http-nio-8080-exec-7] o.s.s.access.vote.AffirmativeBased       : Voter: org.springframework.security.web.access.expression.WebExpressionVoter@7031aa78, returned: 1
2022-02-04 00:09:43.974 DEBUG 29544 --- [http-nio-8080-exec-7] o.s.web.servlet.DispatcherServlet        : "ERROR" dispatch for POST "/error", parameters={}
2022-02-04 00:09:43.974 DEBUG 29544 --- [http-nio-8080-exec-7] w.c.HttpSessionSecurityContextRepository : SecurityContext is empty or contents are anonymous - context will not be stored in HttpSession.
2022-02-04 00:09:43.974  WARN 29544 --- [http-nio-8080-exec-7] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.web.HttpRequestMethodNotSupportedException: Request method 'POST' not supported]
2022-02-04 00:09:43.974 DEBUG 29544 --- [http-nio-8080-exec-7] o.s.web.servlet.DispatcherServlet        : Exiting from "ERROR" dispatch, status 405
2022-02-04 00:09:43.974 DEBUG 29544 --- [http-nio-8080-exec-7] o.s.s.w.a.ExceptionTranslationFilter     : Chain processed normally
2022-02-04 00:09:43.974 DEBUG 29544 --- [http-nio-8080-exec-7] s.s.w.c.SecurityContextPersistenceFilter : SecurityContextHolder now cleared, as request processing completed

In my SecurityConfiguration file, if I add this, POSTMAN works.

 http.csrf().disable();

But I feel like i shouldn't have to disable security to test. After deployment, I would remove that line

Here is my entire file

package com.example.pma.projectmanagement.security;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

import javax.sql.DataSource;

@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Autowired
    DataSource dataSource;

    @Autowired
    BCryptPasswordEncoder bCryptPasswordEncoder;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.jdbcAuthentication().dataSource(dataSource)
                .usersByUsernameQuery("SELECT USERNAME, PASSWORD, ENABLED FROM USER_ACCOUNTS WHERE USERNAME = ?")
                .authoritiesByUsernameQuery("SELECT USERNAME, ROLE FROM USER_ACCOUNTS WHERE USERNAME = ?")
                .dataSource(dataSource)
                .passwordEncoder(bCryptPasswordEncoder);
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/projects/new").hasRole("ADMIN")
                .antMatchers("/projects/save").hasRole("ADMIN")
                .antMatchers("/employees/new").hasAuthority("ADMIN")
                .antMatchers("/employees/save").hasAuthority("ADMIN")
                .antMatchers("/", "/**").permitAll()
                .and()
                .formLogin();


        http.csrf().disable(); // added this line to get POSTMAN to work
        http.headers().frameOptions().disable();
    }
}

Upvotes: 4

Views: 9998

Answers (3)

Vlad Ulshin
Vlad Ulshin

Reputation: 480

With csrf enabled (assuming you can use postman to post when csrf disabled ) you have to include csrf value in your postman post. You can get it from the page/form that has <input type="hidden" th:value="${_csrf.token}" name="_csrf" />

you mentioned above.

Upvotes: 1

Josh Van de Walle
Josh Van de Walle

Reputation: 216

Remove the /12 from your URL. Since there is no value in the PostMapping annotation it will use the Controller's RequestMapping value. The reason you get 405 is because that URI only has a GetMapping.

Upvotes: 0

Xavi Font
Xavi Font

Reputation: 564

In your Postman... Did you make the method "Post"... on the left of the url field?

That must fix it.

enter image description here

Upvotes: 0

Related Questions