14

I have the following controller:

@RestController
public class RestaurantController {
    @Autowired
    RestaurantService restaurantService;
    @RequestMapping(value = "/restaurant/", method = RequestMethod.GET)
    public ResponseEntity<List<Restaurant>> listAllRestaurants() {
        System.out.println("Fetching all restaurants");
        List<Restaurant> restaurants = restaurantService.findAllRestaurants();
        if(restaurants.isEmpty()){
            return new ResponseEntity<List<Restaurant>>(HttpStatus.NO_CONTENT);//You many decide to return HttpStatus.NOT_FOUND
        }
        return new ResponseEntity<List<Restaurant>>(restaurants, HttpStatus.OK);
    }
    @RequestMapping(value = "/restaurant/{id}", method = RequestMethod.PUT)
    public ResponseEntity<Restaurant> updateRestaurant(@PathVariable("id") int id, @RequestBody Restaurant restaurant) {
        System.out.println("Updating Restaurant " + id);

        Restaurant currentRestaurant = restaurantService.findById(id);

        if (currentRestaurant==null) {
            System.out.println("Restaurant with id " + id + " not found");
            return new ResponseEntity<Restaurant>(HttpStatus.NOT_FOUND);
        }

        currentRestaurant.setName(restaurant.getName());
        currentRestaurant.setDescription(restaurant.getDescription());
        currentRestaurant.setIcon(restaurant.getIcon());

        restaurantService.updateRestaurant(currentRestaurant);
        return new ResponseEntity<Restaurant>(currentRestaurant, HttpStatus.OK);
    }
}

Here is my calls from the PostMan. First I am making the GET call to get all the restaurants and it works fine enter image description here Second I am trying to update the object by I am getting the following error. enter image description here At the Tomcat 8.0.32, I am getting the following log:

13-Feb-2016 16:55:09.442 WARNING [http-apr-8080-exec-9] org.springframework.web.servlet.PageNotFound.handleHttpRequestMethodNotSupported Request method 'PUT' not supported

I don't understand this is how possible. And here is my dependencies:

<properties>
        <springframework.version>4.1.6.RELEASE</springframework.version>
        <springsecurity.version>4.0.1.RELEASE</springsecurity.version>
        <hibernate.version>4.3.6.Final</hibernate.version>
        <mysql.connector.version>5.1.31</mysql.connector.version>
        <jackson.version>2.6.3</jackson.version>
        <joda-time.version>2.3</joda-time.version>
        <testng.version>6.9.4</testng.version>
        <mockito.version>1.10.19</mockito.version>
        <h2.version>1.4.187</h2.version>
        <dbunit.version>2.2</dbunit.version>
    </properties>

    <dependencies>
        <!-- Spring -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>${springframework.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>${springframework.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>${springframework.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>${springframework.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-orm</artifactId>
            <version>${springframework.version}</version>
        </dependency>
        <!-- Hibernate -->
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-core</artifactId>
            <version>${hibernate.version}</version>
        </dependency>

        <!-- jsr303 validation -->
        <dependency>
            <groupId>javax.validation</groupId>
            <artifactId>validation-api</artifactId>
            <version>1.1.0.Final</version>
        </dependency>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-validator</artifactId>
            <version>5.1.3.Final</version>
        </dependency>

        <!-- MySQL -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>${mysql.connector.version}</version>
        </dependency>

        <!-- Joda-Time -->
        <dependency>
            <groupId>joda-time</groupId>
            <artifactId>joda-time</artifactId>
            <version>${joda-time.version}</version>
        </dependency>

        <!-- To map JodaTime with database type -->
        <dependency>
            <groupId>org.jadira.usertype</groupId>
            <artifactId>usertype.core</artifactId>
            <version>3.0.0.CR1</version>
        </dependency>
        <!-- Spring Security -->
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-web</artifactId>
            <version>${springsecurity.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-config</artifactId>
            <version>${springsecurity.version}</version>
        </dependency>
        <!-- Servlet+JSP+JSTL -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet.jsp</groupId>
            <artifactId>javax.servlet.jsp-api</artifactId>
            <version>2.3.1</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
            <version>1.2</version>
        </dependency>
        <!-- Need this for json to/from object -->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-core</artifactId>
            <version>${jackson.version}</version>
        </dependency>

        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>${jackson.version}</version>
        </dependency>
        <!-- Testing dependencies -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>${springframework.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.testng</groupId>
            <artifactId>testng</artifactId>
            <version>${testng.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.mockito</groupId>
            <artifactId>mockito-all</artifactId>
            <version>${mockito.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <version>${h2.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>dbunit</groupId>
            <artifactId>dbunit</artifactId>
            <version>${dbunit.version}</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

If more information is needed please tell me! Thanks.

Edit 1:

@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {


    @Autowired
    public void configureGlobalSecurity(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication().withUser("bill").password("user").roles("USER");
        auth.inMemoryAuthentication().withUser("admin").password("admin").roles("ADMIN");
        auth.inMemoryAuthentication().withUser("dba").password("dba").roles("ADMIN","DBA");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {

      http.authorizeRequests()
        .antMatchers("/", "/home","/restaurant/**").permitAll()
        .antMatchers("/list").access("hasRole('USER')")
        .antMatchers("/list").access("hasRole('ADMIN')")
        .antMatchers("/admin/**").access("hasRole('ADMIN')")
        .antMatchers("/db/**").access("hasRole('ADMIN') and hasRole('DBA')")
        .and().formLogin().loginPage("/login")
        .usernameParameter("ssoId").passwordParameter("password")
        .and().csrf()
        .and().exceptionHandling().accessDeniedPage("/Access_Denied");
    }
}

Edit 2:

2016-02-14 12:30:56 DEBUG FilterChainProxy:324 - /restaurant/1 at position 1 of 12 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter'

2016-02-14 12:30:56 DEBUG FilterChainProxy:324 - /restaurant/1 at position 2 of 12 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'

2016-02-14 12:30:56 DEBUG HttpSessionSecurityContextRepository:159 - No HttpSession currently exists

2016-02-14 12:30:56 DEBUG HttpSessionSecurityContextRepository:101 - No SecurityContext was available from the HttpSession: null. A new one will be created.

2016-02-14 12:30:56 DEBUG FilterChainProxy:324 - /restaurant/1 at position 3 of 12 in additional filter chain; firing Filter: 'HeaderWriterFilter'

2016-02-14 12:30:56 DEBUG HstsHeaderWriter:128 - Not injecting HSTS header since it did not match the requestMatcher org.springframework.security.web.header.writers.HstsHeaderWriter$SecureRequestMatcher@3ded3d8a

2016-02-14 12:30:56 DEBUG FilterChainProxy:324 - /restaurant/1 at position 4 of 12 in additional filter chain; firing Filter: 'CsrfFilter'

2016-02-14 12:30:56 DEBUG CsrfFilter:106 - Invalid CSRF token found for http://localhost:8080/SpringSecurityCusotmLoginFormAnnotationExample/restaurant/1

2016-02-14 12:30:56 DEBUG DispatcherServlet:861 - DispatcherServlet with name 'dispatcher' processing PUT request for [/SpringSecurityCusotmLoginFormAnnotationExample/Access_Denied]

2016-02-14 12:30:56 DEBUG RequestMappingHandlerMapping:294 - Looking up handler method for path /Access_Denied

2016-02-14 12:30:56 DEBUG ExceptionHandlerExceptionResolver:134 - Resolving exception from handler [null]: org.springframework.web.HttpRequestMethodNotSupportedException: Request method 'PUT' not supported

2016-02-14 12:30:56 DEBUG ResponseStatusExceptionResolver:134 - Resolving exception from handler [null]: org.springframework.web.HttpRequestMethodNotSupportedException: Request method 'PUT' not supported

2016-02-14 12:30:56 DEBUG DefaultHandlerExceptionResolver:134 - Resolving exception from handler [null]: org.springframework.web.HttpRequestMethodNotSupportedException: Request method 'PUT' not supported

2016-02-14 12:30:56 WARN PageNotFound:198 - Request method 'PUT' not supported

2016-02-14 12:30:56 DEBUG HttpSessionSecurityContextRepository:337 - SecurityContext is empty or contents are anonymous - context will not be stored in HttpSession.

2016-02-14 12:30:56 DEBUG DispatcherServlet:1034 - Null ModelAndView returned to DispatcherServlet with name 'dispatcher': assuming HandlerAdapter completed request handling

2016-02-14 12:30:56 DEBUG DispatcherServlet:996 - Successfully completed request

2016-02-14 12:30:56 DEBUG DefaultListableBeanFactory:248 - Returning cached instance of singleton bean 'delegatingApplicationListener'

2016-02-14 12:30:56 DEBUG HttpSessionSecurityContextRepository:337 - SecurityContext is empty or contents are anonymous - context will not be stored in HttpSession.

2016-02-14 12:30:56 DEBUG SecurityContextPersistenceFilter:105 - SecurityContextHolder now cleared, as request processing completed

whistling_marmot
  • 3,561
  • 3
  • 25
  • 39
KostasC
  • 1,076
  • 6
  • 20
  • 40
  • 1
    It sounds like the PUT verb has been disabled by Apache Tomcat. Do a Google search on disabling PUT in Apache Tomcat and you will see many posts that talk about disabling. You can then see if any of these setting exist (a) in you app, or more likely (b) in your Apache Tomcat settings. – EJK Feb 13 '16 at 16:10
  • @EJK it is also have to do something with my Spring Security Config? – KostasC Feb 13 '16 at 17:31
  • @EJK I have try an another wepapp with PUT, DELETE, and it is working! – KostasC Feb 13 '16 at 18:05

12 Answers12

19

I had the same error but for me it was caused by the fact that I had ommited the ID as URL parameter. I ommited it because the ID was present in the JSON body.

When I changed .../restaurant to ...restaurant/1, the error went away.

progonkpa
  • 3,590
  • 9
  • 31
  • 50
  • 1
    LOL - thanks for the reminder progonkpa to start with the small stuff. I had a null ID. – Forrest Jan 21 '19 at 05:07
  • I had a similar issue. My Put request url was `/task/update` with a complete json object in the `RequestBody`, changed it to `/task/{id}` and it worked as expected. – mal Apr 21 '19 at 12:24
14

Try turning up the logging level for org.springframework.web to DEBUG. This will give you some insight into how Spring is trying to deal with the request. Hopefully, it will give you (or us) some more clues on how to fix it.

If you're using Spring Boot, just add this line to your application.properties file:

logging.level.org.springframework.web=DEBUG

Edit after seeing additional logging:

The 'PUT' not supported message is a bit misleading. The real problem comes before that. You don't have a valid CSRF token. How are you submitting the request? It looks like you are using the PostMan tool (but I am not familiar with this tool) rather than submitting the form directly from a web-page. There may be some way that you can add the token to your request using the tool. Does it work without the tool - submitting the form directly from the web-page?

whistling_marmot
  • 3,561
  • 3
  • 25
  • 39
  • Yes, you are right! I did it from the web-page and follow this link http://stackoverflow.com/questions/27834867/spring-security-csrf-token-not-working-with-ajax. And now I am getting 400 bad request! – KostasC Feb 15 '16 at 16:49
  • Thanks, problem solved. In my case, I forgot to attach "@ResponseBody" on the controller. Silly mistake :^( – kimchoky Oct 30 '19 at 04:00
  • After a lot of searching I finally realized what caused the error on a similar issue I experienced. Apparently the whole CORS problem was caused by my own mistake and the advice for allowing this logging made it possible for me realize this. Great answer. – Barrosy Nov 26 '22 at 17:54
3

I worked with a basic CRUD application in Spring Boot. In PUT mapping, I passed the path variable in the wrong way.

@PutMapping(path ={"studentId"}) replaced to @PutMapping(path ="{studentId}")

1

By default, in WebMvcAutoConfiguration, HttpPutFormContentFilter is not configured and hence the issue.

Its fixed in spring boot [1.3.0) version and working fine. So, you may try updating your spring-boot version or configure this filter manually to make things work.

Issue reference.
Another Stackoverlow reference

Hope it helps.

Community
  • 1
  • 1
Rites
  • 2,242
  • 5
  • 28
  • 44
1

So this Basically happens because of the missing parameters in the URL or the wrong URL.

In My case, I was forgetting the " id "i.e "1" param in my URL.

Solution: http://localhost:8080/products/1

when I added this param 1 after the /products, the error went away.

( You Can try to use this accordingly to your project. )

0

instead of sending request to /restaurant/1/, send it to /restaurant/1

hahn
  • 3,588
  • 20
  • 31
0

If you don't need csrf you can do this.

@Configuration
@EnableWebSecurity
public class SpringSecurityConfiguration extends WebSecurityConfigurerAdapter {

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.csrf().disable();
}

}

FishingIsLife
  • 1,972
  • 3
  • 28
  • 51
0

I think your Model should implement Serializable interface and should have a default constructor.

For me issue was solved by adding above to my Model class and @RequestBody to the Controller method argument.

Jag
  • 77
  • 8
0

HTTP 405 Error Code is also returned IF the object cannot be deserialized, this happen as example with object without the default constructor.

take as example, this service :

import javax.ws.rs.*;
import com.sun.jersey.spi.resource.Singleton;

@Singleton
@Path("root")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class SampleRestService { ..
  @PUT
  @Path("sample")
  public void update(Sample whitelist) throws SampleException {
    // do some
}

and the Object sample, overriding the default constructor :

class Sample{
 int x,y;
 public sample(int x, int y){
   // boring stuff
 }
}

in this case de unmarshaling cannot deserialize the request content, and the 405 is triggered. Or at last that is how is working with glassfish 3.5 and javax.ws.rs-api-2.0.1.jar and jersey-xxx-2.25.1.jar.

The fix is actually add the default constructor (ie. the one without parameters) to the objects passed through the APIs.

loreii
  • 380
  • 4
  • 13
0

In my case, i had to use a model class with @RequestBody for the patch controller.

Bukunmi
  • 2,504
  • 1
  • 18
  • 15
0

In my case, I got a right response with post instead of put.

0

I have been facing the same issue but later on, I got it corrected by rectifying the put mapping path in the backend server from <@PutMapping("/user/{id} ")> to <@PutMapping("/user/{id}")>. Make sure there isn't a space after id;

Josh
  • 718
  • 10
  • 25
Juyel
  • 31
  • 3