samedi 28 mars 2015

Spring RestController - jQuery PUT works only when changing certain values

Hello and thank you in advanced:


I'm using Spring 4 and jquery trying to PUT data to a RestController. I have three values that are passed to the controller: id, name, genre. For some reason, the PUT request works every time that I change the name field, but if I try to update just the genre field, the request never even hits the controller and fails with 400: Bad Request.


I'm totally stumped. My GET, POST, and DELETE methods are working just fine too. I'm going to include my config stuff since there's a good possibility that may be the issue...


manage-artist.js (knockout and jquery):



...

//update artist
self.updateArtist = function(artist){
self.clearMessages();

//put to database
$.ajax({
url: "../artist/" + artist.id(),
method: "PUT",
data: "id="+artist.id()+"&name="+artist.name()+"&genre="+artist.genre(),
datatype: "json",
success: function (data) {
console.log("Artist succesfully updated.");

//update message
self.successMessage("Artist successfully updated.");
},
error: function (err) {
console.log("An error occured while trying to update artist.");

//extract json response
self.errorResponse(err.responseJSON);

//...more error handling
}

});
};

...


ArtistController.java:



@RestController
@RequestMapping(value="/artist")
public class ArtistController {

@Autowired
private ArtistService artistService;

@Autowired
private ArtistValidator artistValidator;

@InitBinder
private void initBinder(WebDataBinder binder) {
binder.setValidator(artistValidator);
}

@RequestMapping(value="/", method=RequestMethod.GET)
public List<Artist> newArtistPage() {
return artistService.findAll();
}

@RequestMapping(value="/{id}", method=RequestMethod.GET)
public Artist artistListPage(@PathVariable Integer id) {
return artistService.findById(id);
}

@RequestMapping(value="/", method=RequestMethod.POST)
public Artist createArtist(@ModelAttribute @Valid Artist artist,
BindingResult result) {

//If there's validation errors
if (result.hasErrors())
throw new InvalidRequestException("Error creating artist", result);

return artistService.create(artist);
}

@RequestMapping(value="/{id}", method=RequestMethod.PUT)
public Artist editArtist(@ModelAttribute @Valid Artist artist, @PathVariable Integer id,
BindingResult result) {

//If there's validation errors
if (result.hasErrors())
throw new InvalidRequestException("Error updating artist", result);

//call update service
Artist updatedArtist;
try {
updatedArtist = artistService.update(artist);
} catch (ArtistNotFoundException e) {
throw new ResourceNotFoundException("Unable to update artist with ID = "+ id
+". No such artist exists.");
}

return updatedArtist;
}

@RequestMapping(value="/{id}", method=RequestMethod.DELETE)
public Artist deleteArtist(@PathVariable Integer id)
{
//call delete service
Artist deletedArtist;
try {
deletedArtist = artistService.delete(id);
} catch (ArtistNotFoundException e) {
throw new ResourceNotFoundException("Unable to delete artist with ID = "+ id
+". No such artist exists.");
}

return deletedArtist;
}

}


Initializer.java:



public class Initializer implements WebApplicationInitializer {

private static final String DISPATCHER_SERVLET_NAME = "dispatcher";

public void onStartup(ServletContext servletContext)
throws ServletException {
AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
ctx.register(WebAppConfig.class);
servletContext.addListener(new ContextLoaderListener(ctx));

ctx.setServletContext(servletContext);

Dynamic servlet = servletContext.addServlet(DISPATCHER_SERVLET_NAME,
new DispatcherServlet(ctx));
servlet.addMapping("/");
servlet.setLoadOnStartup(1);
}

}


WebAppConfig.java:



@Configuration
@EnableWebMvc
@EnableTransactionManagement
@ComponentScan("com.project.jukebox")
@PropertySource("classpath:jdbc.properties")
@EnableJpaRepositories("com.project.jukebox.repositories")
public class WebAppConfig extends WebMvcConfigurerAdapter {

private static final String PROPERTY_NAME_DATABASE_DRIVER = "jdbc.driver";
private static final String PROPERTY_NAME_DATABASE_PASSWORD = "jdbc.password";
private static final String PROPERTY_NAME_DATABASE_URL = "jdbc.url";
private static final String PROPERTY_NAME_DATABASE_USERNAME = "jdbc.username";

private static final String PROPERTY_NAME_HIBERNATE_DIALECT = "hibernate.dialect";
private static final String PROPERTY_NAME_HIBERNATE_SHOW_SQL = "hibernate.show_sql";
private static final String PROPERTY_NAME_ENTITYMANAGER_PACKAGES_TO_SCAN = "entitymanager.packages.to.scan";

@Resource
private Environment env;

@Bean
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();

dataSource.setDriverClassName(env.getRequiredProperty(PROPERTY_NAME_DATABASE_DRIVER));
dataSource.setUrl(env.getRequiredProperty(PROPERTY_NAME_DATABASE_URL));
dataSource.setUsername(env.getRequiredProperty(PROPERTY_NAME_DATABASE_USERNAME));
dataSource.setPassword(env.getRequiredProperty(PROPERTY_NAME_DATABASE_PASSWORD));

return dataSource;
}

@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
entityManagerFactoryBean.setDataSource(dataSource());
entityManagerFactoryBean.setPersistenceProviderClass(HibernatePersistenceProvider.class);
entityManagerFactoryBean.setPackagesToScan(env.getRequiredProperty(PROPERTY_NAME_ENTITYMANAGER_PACKAGES_TO_SCAN));

entityManagerFactoryBean.setJpaProperties(hibProperties());

return entityManagerFactoryBean;
}

private Properties hibProperties() {
Properties properties = new Properties();
properties.put(PROPERTY_NAME_HIBERNATE_DIALECT, env.getRequiredProperty(PROPERTY_NAME_HIBERNATE_DIALECT));
properties.put(PROPERTY_NAME_HIBERNATE_SHOW_SQL, env.getRequiredProperty(PROPERTY_NAME_HIBERNATE_SHOW_SQL));
properties.put("hibernate.hbm2ddl.auto", "create-drop");
return properties;
}

@Bean
public JpaTransactionManager transactionManager() {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactory().getObject());
return transactionManager;
}

@Bean
public ResourceBundleMessageSource messageSource() {
ResourceBundleMessageSource source = new ResourceBundleMessageSource();
source.setBasename(env.getRequiredProperty("message.source.basename"));
source.setUseCodeAsDefaultMessage(true);
return source;
}

@Bean
public InternalResourceViewResolver getInternalResourceViewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/views/");
resolver.setSuffix(".jsp");
return resolver;
}

@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}

@Bean
public WebContentInterceptor webContentInterceptor() {
WebContentInterceptor interceptor = new WebContentInterceptor();
interceptor.setCacheSeconds(0);
interceptor.setUseExpiresHeader(true);
interceptor.setUseCacheControlHeader(true);
interceptor.setUseCacheControlNoStore(true);

return interceptor;
}

@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**").addResourceLocations("/resources/");
registry.addResourceHandler("/lib/**").addResourceLocations("/WEB-INF/lib/");
registry.addResourceHandler("/scripts/**").addResourceLocations("/WEB-INF/scripts/");
registry.addResourceHandler("/styles/**").addResourceLocations("/WEB-INF/styles/");
registry.addResourceHandler("/fonts/**").addResourceLocations("/WEB-INF/fonts/");
}

@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(webContentInterceptor());
}

}


Web.xml:



<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://ift.tt/ra1lAU"
xmlns="http://ift.tt/nSRXKP" xmlns:web="http://ift.tt/LU8AHS"
xsi:schemaLocation="http://ift.tt/nSRXKP http://ift.tt/1eWqHMP"
version="3.0">

<display-name>jukebox</display-name>

<!-- filters needed for put requests, known bug with spring rest -->
<filter>
<filter-name>HttpPutFormContentFilter</filter-name>
<filter-class>org.springframework.web.filter.HttpPutFormContentFilter</filter-class>
</filter>

<filter-mapping>
<filter-name>HttpPutFormContentFilter</filter-name>
<url-pattern>/*</url-pattern>
<servlet-name>dispatcher</servlet-name>
</filter-mapping>

</web-app>

Aucun commentaire:

Enregistrer un commentaire