2

This request below is causing a 400 bad request response:

$scope.addGiveaway = function(campaignId){
        var photo = $scope.photo;
        var giveaway = $scope.giveaway;
        var data = new FormData();
        data.append('photo', photo);
        data.append('giveaway', giveaway);
        var uploadUrl = "/dashboard/campaigns/"+campaignId+"/giveaways";
        $http.put(uploadUrl, data, {
            transformRequest: angular.identity,
            headers: {
                'Content-Type': 'multipart/form-data',
            }
        });

Here's the Spring controller:

    @RequestMapping(method = RequestMethod.PUT)
    public ResponseEntity<String> addGiveaway(@RequestParam("giveaway") Giveaway giveaway, 
                                              @RequestParam("photo") MultipartFile photo,
                                              @PathVariable("campaignId") long campaignId,
                                              Principal principal) throws Exception {

I have a @RequestMapping at the top of the entire controller class:

@RequestMapping("/dashboard/campaigns/{campaignId}/giveaways")

I've tried:

  • Changing the Giveaway object to a String and using a mapper to map the values manually
  • I've tried using angular.toJson
  • I've ensured all of the values are being populated (no null values being sent from Angular).
  • I've tried changing the Content-Type to undefined and to multipart/form-data

Why is this causing a 400 bad request response?

EDIT Posting entire controller class as requested. It shouldn't matter though as the logger at the top of the controller is not being called. It's not getting into the code of the controller.

@RestController
@RequestMapping("/dashboard/campaigns/{campaignId}/giveaways")
public class GiveawayController {

    @Autowired
    private GiveawayService giveawayService;

    @Autowired
    private CampaignService campaignService;

    @Autowired
    private BusinessUserService businessUserService;

    private static final Logger logger = Logger.getLogger(GiveawayController.class);

    /**
     * Uploads a giveaway to the server given data and a photo.
     * 
     * @param giveaway giveaway data to save
     * @param photo photo of the giveaway item
     * @param principal authenticated user
     * @return status code and errors
     * @throws Exception
     */
    @RequestMapping(method = RequestMethod.PUT)
    public ResponseEntity<String> addGiveaway(@RequestParam("giveaway") Giveaway giveaway, 
                                              @RequestParam("photo") MultipartFile photo,
                                              @PathVariable("campaignId") long campaignId,
                                              Principal principal) throws Exception {
        logger.info("Made it into the controller");

        Campaign campaign = campaignService.findOne(campaignId);
        campaignService.verifyCampaignOwnership(
                            businessUserService
                                .findByEmail(principal.getName()), campaign);

        //Validate form input
        HttpHeaders headers = giveawayService.validateGiveawayData(giveaway);

        //Validate image size & file type
        String extension = FileUtils.resolveExtension(photo.getContentType());
        if(FileUtils.isImage(extension)) {
            if(FileUtils.checkDimensions(photo, GiveawayService.MIN_ALLOWED_SIZE, 
                                         GiveawayService.MAX_ALLOWED_SIZE, GiveawayService.MIN_ALLOWED_SIZE, 
                                         GiveawayService.MAX_ALLOWED_SIZE, true)) {
                headers.add("Error-Giveaway-Image-Size", "Giveaway photo must be square and between 64-612 pixels.");
            }
        } else {
            headers.add("Error-Giveaway-Image-Type", "Only .jpg and .png file types are allowed.");
        }

        //If headers are not empty, then there's either validation or input errors 
        if(!headers.isEmpty()) {
            return new ResponseEntity<>(headers, HttpStatus.BAD_REQUEST);
        } else {
            giveaway.setPhotoExtension(extension);
            giveaway.setStatus(GiveawayStatus.INACTIVE);
            giveaway = giveawayService.save(giveaway);

            FileUtils.uploadResource(photo, giveaway.getGiveawayId() + extension, GiveawayService.GIVEAWAY_STORING_LOCATION);

            return new ResponseEntity<>(HttpStatus.CREATED);
        }

    }
}

Here's the giveaway POJO:

@Entity
@Table(name="GIVEAWAYS")
public class Giveaway implements Serializable {

    private static final long serialVersionUID = -8835490774774467020L;

    @Id
    @Column(name="giveaway_id")
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private long giveawayId;

    @ManyToOne
    @JoinColumn(name="campaign_id", foreignKey=@ForeignKey(name="give_campaign_id"), nullable=false)
    private Campaign campaign;

    @Column(name="giveaway_item")
    private String giveawayItem;

    @Column(name="description")
    private String description;

    @Column(name="photo_extension")
    private String photoExtension;

    @Column(name="amount_of_items")
    private int amountOfItems;

    @Column(name="status")
    private String status;

    @Column(name="eligibility")
    private String eligibility;

   //Getters and setters below...

And here's the JSON created from the form sent to the back end:

{giveawayItem: "testing", description: "test123", amountOfItems: 4, eligibility: "Followers"}

EDIT 3 Whenever I use angular.toJson or JSON.stringify on the data above, it turns it into this:

{"description":"dfgdg","amountOfItems":5,"eligibility":"Followers"}

For some reason, giveawayItem disappears. I don't know if that's causing any issues, but it's certainly troubling.

EDIT 4

Debug logging

2016-10-31 13:40:35.097 DEBUG 2716 --- [nio-8090-exec-3] o.s.web.servlet.DispatcherServlet        : DispatcherServlet with name 'dispatcherServlet' processing PUT request for [/dashboard/campaigns/1/giveaways]
2016-10-31 13:40:35.098 DEBUG 2716 --- [nio-8090-exec-3] s.w.s.m.m.a.RequestMappingHandlerMapping : Looking up handler method for path /dashboard/campaigns/1/giveaways
2016-10-31 13:40:35.102 DEBUG 2716 --- [nio-8090-exec-3] s.w.s.m.m.a.RequestMappingHandlerMapping : Returning handler method [public org.springframework.http.ResponseEntity<java.lang.String> com.glimpsmedia.app.controllers.GiveawayController.addGiveaway(com.glimpsmedia.app.model.Giveaway,org.springframework.web.multipart.MultipartFile,long,java.security.Principal) throws java.lang.Exception]
2016-10-31 13:40:35.121 DEBUG 2716 --- [nio-8090-exec-3] o.s.web.cors.DefaultCorsProcessor        : Skip CORS processing: request is from same origin
2016-10-31 13:40:35.139 DEBUG 2716 --- [nio-8090-exec-3] .w.s.m.m.a.ServletInvocableHandlerMethod : Error resolving argument [0] [type=com.glimpsmedia.app.model.Giveaway]
HandlerMethod details: 
Controller [com.glimpsmedia.app.controllers.GiveawayController]
Method [public org.springframework.http.ResponseEntity<java.lang.String> com.glimpsmedia.app.controllers.GiveawayController.addGiveaway(com.glimpsmedia.app.model.Giveaway,org.springframework.web.multipart.MultipartFile,long,java.security.Principal) throws java.lang.Exception]


org.springframework.web.bind.MissingServletRequestParameterException: Required Giveaway parameter 'giveaway' is not present
    at org.springframework.web.method.annotation.RequestParamMethodArgumentResolver.handleMissingValue(RequestParamMethodArgumentResolver.java:195) ~[spring-web-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    at org.springframework.web.method.annotation.AbstractNamedValueMethodArgumentResolver.resolveArgument(AbstractNamedValueMethodArgumentResolver.java:104) ~[spring-web-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    at org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:121) ~[spring-web-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    at org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:161) [spring-web-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:128) [spring-web-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:114) [spring-webmvc-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:827) [spring-webmvc-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:738) [spring-webmvc-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85) [spring-webmvc-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:963) [spring-webmvc-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:897) [spring-webmvc-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970) [spring-webmvc-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    at org.springframework.web.servlet.FrameworkServlet.doPut(FrameworkServlet.java:883) [spring-webmvc-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:651) [tomcat-embed-core-8.5.4.jar:8.5.4]
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846) [spring-webmvc-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:729) [tomcat-embed-core-8.5.4.jar:8.5.4]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:230) [tomcat-embed-core-8.5.4.jar:8.5.4]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165) [tomcat-embed-core-8.5.4.jar:8.5.4]
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52) [tomcat-embed-websocket-8.5.4.jar:8.5.4]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192) [tomcat-embed-core-8.5.4.jar:8.5.4]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165) [tomcat-embed-core-8.5.4.jar:8.5.4]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:317) [spring-security-web-4.1.1.RELEASE.jar:4.1.1.RELEASE]
    at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:127) [spring-security-web-4.1.1.RELEASE.jar:4.1.1.RELEASE]
    at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:91) [spring-security-web-4.1.1.RELEASE.jar:4.1.1.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) [spring-security-web-4.1.1.RELEASE.jar:4.1.1.RELEASE]
    at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:115) [spring-security-web-4.1.1.RELEASE.jar:4.1.1.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) [spring-security-web-4.1.1.RELEASE.jar:4.1.1.RELEASE]
    at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:137) [spring-security-web-4.1.1.RELEASE.jar:4.1.1.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) [spring-security-web-4.1.1.RELEASE.jar:4.1.1.RELEASE]
    at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:111) [spring-security-web-4.1.1.RELEASE.jar:4.1.1.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) [spring-security-web-4.1.1.RELEASE.jar:4.1.1.RELEASE]
    at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:169) [spring-security-web-4.1.1.RELEASE.jar:4.1.1.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) [spring-security-web-4.1.1.RELEASE.jar:4.1.1.RELEASE]
    at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63) [spring-security-web-4.1.1.RELEASE.jar:4.1.1.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) [spring-security-web-4.1.1.RELEASE.jar:4.1.1.RELEASE]
    at org.springframework.security.web.authentication.www.BasicAuthenticationFilter.doFilterInternal(BasicAuthenticationFilter.java:158) [spring-security-web-4.1.1.RELEASE.jar:4.1.1.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) [spring-security-web-4.1.1.RELEASE.jar:4.1.1.RELEASE]
    at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:200) [spring-security-web-4.1.1.RELEASE.jar:4.1.1.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) [spring-security-web-4.1.1.RELEASE.jar:4.1.1.RELEASE]
    at com.glimpsmedia.app.security.StatelessAuthenticationFilter.doFilter(StatelessAuthenticationFilter.java:52) [classes/:na]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) [spring-security-web-4.1.1.RELEASE.jar:4.1.1.RELEASE]
    at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:200) [spring-security-web-4.1.1.RELEASE.jar:4.1.1.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) [spring-security-web-4.1.1.RELEASE.jar:4.1.1.RELEASE]
    at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:121) [spring-security-web-4.1.1.RELEASE.jar:4.1.1.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) [spring-security-web-4.1.1.RELEASE.jar:4.1.1.RELEASE]
    at com.glimpsmedia.app.security.CsrfTokenFilter.doFilterInternal(CsrfTokenFilter.java:46) [classes/:na]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) [spring-security-web-4.1.1.RELEASE.jar:4.1.1.RELEASE]
    at org.springframework.security.web.csrf.CsrfFilter.doFilterInternal(CsrfFilter.java:124) [spring-security-web-4.1.1.RELEASE.jar:4.1.1.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) [spring-security-web-4.1.1.RELEASE.jar:4.1.1.RELEASE]
    at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:66) [spring-security-web-4.1.1.RELEASE.jar:4.1.1.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) [spring-security-web-4.1.1.RELEASE.jar:4.1.1.RELEASE]
    at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:105) [spring-security-web-4.1.1.RELEASE.jar:4.1.1.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) [spring-security-web-4.1.1.RELEASE.jar:4.1.1.RELEASE]
    at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:56) [spring-security-web-4.1.1.RELEASE.jar:4.1.1.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) [spring-security-web-4.1.1.RELEASE.jar:4.1.1.RELEASE]
    at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:214) [spring-security-web-4.1.1.RELEASE.jar:4.1.1.RELEASE]
    at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:177) [spring-security-web-4.1.1.RELEASE.jar:4.1.1.RELEASE]
    at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:346) [spring-web-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:262) [spring-web-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192) [tomcat-embed-core-8.5.4.jar:8.5.4]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165) [tomcat-embed-core-8.5.4.jar:8.5.4]
    at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99) [spring-web-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192) [tomcat-embed-core-8.5.4.jar:8.5.4]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165) [tomcat-embed-core-8.5.4.jar:8.5.4]
    at org.springframework.web.filter.HttpPutFormContentFilter.doFilterInternal(HttpPutFormContentFilter.java:87) [spring-web-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192) [tomcat-embed-core-8.5.4.jar:8.5.4]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165) [tomcat-embed-core-8.5.4.jar:8.5.4]
    at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:77) [spring-web-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192) [tomcat-embed-core-8.5.4.jar:8.5.4]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165) [tomcat-embed-core-8.5.4.jar:8.5.4]
    at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:197) [spring-web-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192) [tomcat-embed-core-8.5.4.jar:8.5.4]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165) [tomcat-embed-core-8.5.4.jar:8.5.4]
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:198) [tomcat-embed-core-8.5.4.jar:8.5.4]
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:108) [tomcat-embed-core-8.5.4.jar:8.5.4]
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:522) [tomcat-embed-core-8.5.4.jar:8.5.4]
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140) [tomcat-embed-core-8.5.4.jar:8.5.4]
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79) [tomcat-embed-core-8.5.4.jar:8.5.4]
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87) [tomcat-embed-core-8.5.4.jar:8.5.4]
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:349) [tomcat-embed-core-8.5.4.jar:8.5.4]
    at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:1110) [tomcat-embed-core-8.5.4.jar:8.5.4]
    at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66) [tomcat-embed-core-8.5.4.jar:8.5.4]
    at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:785) [tomcat-embed-core-8.5.4.jar:8.5.4]
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1425) [tomcat-embed-core-8.5.4.jar:8.5.4]
    at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) [tomcat-embed-core-8.5.4.jar:8.5.4]
    at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source) [na:1.8.0_91]
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source) [na:1.8.0_91]
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-8.5.4.jar:8.5.4]
    at java.lang.Thread.run(Unknown Source) [na:1.8.0_91]

2016-10-31 13:40:35.140 DEBUG 2716 --- [nio-8090-exec-3] .m.m.a.ExceptionHandlerExceptionResolver : Resolving exception from handler [public org.springframework.http.ResponseEntity<java.lang.String> com.glimpsmedia.app.controllers.GiveawayController.addGiveaway(com.glimpsmedia.app.model.Giveaway,org.springframework.web.multipart.MultipartFile,long,java.security.Principal) throws java.lang.Exception]: org.springframework.web.bind.MissingServletRequestParameterException: Required Giveaway parameter 'giveaway' is not present
2016-10-31 13:40:35.140 DEBUG 2716 --- [nio-8090-exec-3] .w.s.m.a.ResponseStatusExceptionResolver : Resolving exception from handler [public org.springframework.http.ResponseEntity<java.lang.String> com.glimpsmedia.app.controllers.GiveawayController.addGiveaway(com.glimpsmedia.app.model.Giveaway,org.springframework.web.multipart.MultipartFile,long,java.security.Principal) throws java.lang.Exception]: org.springframework.web.bind.MissingServletRequestParameterException: Required Giveaway parameter 'giveaway' is not present
2016-10-31 13:40:35.140 DEBUG 2716 --- [nio-8090-exec-3] .w.s.m.s.DefaultHandlerExceptionResolver : Resolving exception from handler [public org.springframework.http.ResponseEntity<java.lang.String> com.glimpsmedia.app.controllers.GiveawayController.addGiveaway(com.glimpsmedia.app.model.Giveaway,org.springframework.web.multipart.MultipartFile,long,java.security.Principal) throws java.lang.Exception]: org.springframework.web.bind.MissingServletRequestParameterException: Required Giveaway parameter 'giveaway' is not present
2016-10-31 13:40:35.141 DEBUG 2716 --- [nio-8090-exec-3] o.s.web.servlet.DispatcherServlet        : Null ModelAndView returned to DispatcherServlet with name 'dispatcherServlet': assuming HandlerAdapter completed request handling
2016-10-31 13:40:35.141 DEBUG 2716 --- [nio-8090-exec-3] o.s.web.servlet.DispatcherServlet        : Successfully completed request
2016-10-31 13:40:35.142 DEBUG 2716 --- [nio-8090-exec-3] o.s.web.servlet.DispatcherServlet        : DispatcherServlet with name 'dispatcherServlet' processing PUT request for [/error]
2016-10-31 13:40:35.142 DEBUG 2716 --- [nio-8090-exec-3] s.w.s.m.m.a.RequestMappingHandlerMapping : Looking up handler method for path /error
2016-10-31 13:40:35.144 DEBUG 2716 --- [nio-8090-exec-3] s.w.s.m.m.a.RequestMappingHandlerMapping : Returning handler method [public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.BasicErrorController.error(javax.servlet.http.HttpServletRequest)]
2016-10-31 13:40:35.144 DEBUG 2716 --- [nio-8090-exec-3] o.s.web.cors.DefaultCorsProcessor        : Skip CORS processing: request is from same origin
2016-10-31 13:40:35.149 DEBUG 2716 --- [nio-8090-exec-3] o.s.w.s.m.m.a.HttpEntityMethodProcessor  : Written [{timestamp=Mon Oct 31 13:40:35 EDT 2016, status=400, error=Bad Request, exception=org.springframework.web.bind.MissingServletRequestParameterException, message=Required Giveaway parameter 'giveaway' is not present, path=/dashboard/campaigns/1/giveaways}] as "application/json" using [org.springframework.http.converter.json.MappingJackson2HttpMessageConverter@7cbe3a05]
2016-10-31 13:40:35.149 DEBUG 2716 --- [nio-8090-exec-3] o.s.web.servlet.DispatcherServlet        : Null ModelAndView returned to DispatcherServlet with name 'dispatcherServlet': assuming HandlerAdapter completed request handling
2016-10-31 13:40:35.149 DEBUG 2716 --- [nio-8090-exec-3] o.s.web.servlet.DispatcherServlet        : Successfully completed request
Jake Miller
  • 2,432
  • 2
  • 24
  • 39
  • Can you try enabling debug level logging on the core spring classes? Like ``? Also, not an expert on JSON, but that string fails on http://jsonlint.com – riddle_me_this Oct 31 '16 at 17:26
  • @bphilipnyc I'm using Spring Boot, so I don't have any XML configuration files. I'll look up how to enable it in my application.properties file. In the mean time, could you look at my edit #3? – Jake Miller Oct 31 '16 at 17:36
  • In your application.properties, set `logging.level.org.springframework.web=DEBUG` – riddle_me_this Oct 31 '16 at 17:38
  • 1
    Off the top of my head, it seems odd to be using form-multipart for PUT. I'd tend to use JSON instead. – chrylis -cautiouslyoptimistic- Oct 31 '16 at 17:44
  • @chrylis The reason I'm using multipart is because I'm sending a file alongside form data. The photo parameter is an image. – Jake Miller Oct 31 '16 at 17:45
  • Not sure whether this will help, but you can try setting it to undefined instead: http://stackoverflow.com/questions/37039852/send-formdata-with-other-field-in-angular – riddle_me_this Oct 31 '16 at 17:50

3 Answers3

1

Your request url /dashboard/campaigns/"+campaignId+"/giveaways is not actually mapped to the controller method.

As you need to pass campaignId as @PathVariable, you should map it, in your controller method.

Class Level Mapping (at top): /dashboard/campaigns/

Method Level Mapping : {campaignId}/giveaways

The code follows below:

    @RestController(value = "/dashboard/campaigns/") {
    public class GiveawayController {
           @RequestMapping(value="{campaignId}/giveaways", method =RequestMethod.PUT)
    public ResponseEntity<String> addGiveaway(    
             @RequestParam("giveaway")Giveaway giveaway, 
             @RequestParam("photo") MultipartFile photo,
             @PathVariable("campaignId") long campaignId,
              Principal principal) throws Exception {
             //code here
        }
    }

P.S.: I have assumed that /dashboard/campaigns/ mapping at class level.

Vasu
  • 21,832
  • 11
  • 51
  • 67
  • Still have the same issue unfortunately. I even tried removing the `{campaignId}` field entirely, manually entering the number "1" when I needed that variable. I'm still getting a 400 response. – Jake Miller Oct 31 '16 at 16:42
  • yeah, one second. – Jake Miller Oct 31 '16 at 16:50
  • It shouldn't matter because the logger at the top of the controller is not printing anything. That means it's an error at the request level right? – Jake Miller Oct 31 '16 at 16:57
  • Did you look at my answer closely how I configured class level and method level mappings ? – Vasu Oct 31 '16 at 16:58
  • yes, I did it exactly how you had it. I reverted it back when it didn't do anything. I even tried copying the whole mapping above the controller method itself. – Jake Miller Oct 31 '16 at 16:59
  • I added the Giveaway POJO and the JSON sent to the back end. – Jake Miller Oct 31 '16 at 17:03
  • In angularjs change it to 'Content-Type': 'application/json' – Vasu Oct 31 '16 at 17:03
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/127058/discussion-between-javaguy-and-jake-miller). – Vasu Oct 31 '16 at 17:05
  • This isn't true. I fixed it with my current mapping set up and it's working 100%. – Jake Miller Nov 01 '16 at 16:42
0

Here's my interpretation of your problem.

Actually you're struggling to transmit the object from the Angular front side and trying to get it through @RequestParam which is naturally can't be straightforward as @RequestParam will normally works on string type, means it can't just map a complex object . It's all makes sense as the 400 http code is indicating a malformed request.

A possible solution (notice that @RequestBody instead of @RequestParam on Giveaway ):

// Angular side
$scope.addGiveaway = function(campaignId){
        var photo = $scope.photo;
        var giveaway = $scope.giveaway;
        var data = new FormData();
        data.append('photo', photo);
        data.append('giveaway', giveaway);
        var uploadUrl = "/dashboard/campaigns/"+campaignId+"/giveaways";
        $http.put(uploadUrl, data, {
            transformRequest: angular.identity,
            headers: {
                'Content-Type': 'multipart/form-data',
            }
        });


// Spring side
 @RequestMapping(method = RequestMethod.PUT)
    public ResponseEntity<String> addGiveaway(@RequestBody Giveaway giveaway, 
                                              @RequestParam("photo") MultipartFile photo,
                                              @PathVariable("campaignId") long campaignId,
                                              Principal principal) throws Exception {
Fevly Pallar
  • 3,059
  • 2
  • 15
  • 19
  • Causes a 415 response. Here's the error: `org.springframework.web.HttpMediaTypeNotSupportedException: Content type 'multipart/form-data;boundary=----WebKitFormBoundaryBQQTVmmDn1riaVzC;charset=UTF-8' not supported`. I think the boundary is the issue but I can't seem to find anything on it. – Jake Miller Nov 01 '16 at 15:30
  • My mistake, the JSON part is just fine (as it was being sent), and the content type of the HTTP header must be *multipart/form-data*, but I'm not sure whether or not this *@RequestBody* needs a json provider to be set up for. – Fevly Pallar Nov 02 '16 at 06:28
0

I fixed it. I changed the Giveaway object to a String and parsed that with an ObjectMapper. Here's my new controller:

@RequestMapping(method = RequestMethod.PUT)
    public ResponseEntity<String> addGiveaway(@RequestParam("giveaway") String giveawayData, 
                                              @RequestParam("photo") MultipartFile photo,
                                              @PathVariable("campaignId") long campaignId,
                                              Principal principal) throws Exception {

I then used var giveaway = angular.toJson($scope.giveaway) to format the json properly. I then appended the giveaway data to the end of the request instead of adding it to the form data, like this:

'.../giveaways?giveaway=' + giveaway;

Jake Miller
  • 2,432
  • 2
  • 24
  • 39