title: Spring - "Content type 'application/json;charset=UTF-8' not supported"
body: <p>I get this error:</p>
  
  <pre><code>{
    "timestamp": 1507132684098,
    "status": 415,
    "error": "Unsupported Media Type",
    "exception": "org.springframework.web.HttpMediaTypeNotSupportedException",
    "message": "Content type 'application/json;charset=UTF-8' not supported",
    "path": "/api/v1/user/0045b213-e764-4ca5-b2e4-71058ef8b2bc"
  }
  </code></pre>
  
  <p>For the following request:</p>
  
  <pre><code>curl -X PUT --header 'Content-Type: application/json' --header 'Accept: application/json' -d '{ \ 
     "agreedToTerms": true, \ 
     "alias": "string", \ 
     "authToken": { \ 
       "created": "2017-10-04T15:57:16.485Z", \ 
       "key": "string", \ 
       "user": {} \ 
     }, \ 
     "authenticationTemporaryTokens": {}, \ 
     "createdDate": "2017-10-04T15:57:16.485Z", \ 
     "customerId": "string", \ 
     "dateJoined": "2017-10-04T15:57:16.485Z", \ 
     "email": "string", \ 
     "firstName": "string", \ 
     "lastLogin": "2017-10-04T15:57:16.485Z", \ 
     "lastName": "string", \ 
     "lastSyncFromMdm": "2017-10-04T15:57:16.485Z", \ 
     "lastZconsoleLogin": "2017-10-04T15:57:16.485Z", \ 
     "middleName": "string", \ 
     "modifiedDate": "2017-10-04T15:57:16.485Z", \ 
     "notificationRules": {}, \ 
     "objectId": "string", \ 
     "password": "string", \ 
     "passwordExpirationDate": "2017-10-04T15:57:16.485Z", \ 
     "phoneNumber": "string", \ 
     "phoneNumberVerified": true, \ 
     "pwdRecoveryRequest": true, \ 
     "resetPasswordToken": "string", \ 
     "resetPasswordTokenLifetime": { \ 
       "nano": 0, \ 
       "negative": true, \ 
       "seconds": 0, \ 
       "units": [ \ 
         { \ 
           "dateBased": true, \ 
           "duration": {}, \ 
           "durationEstimated": true, \ 
           "timeBased": true \ 
         } \ 
       ], \ 
       "zero": true \ 
     }, \ 
     "role": 0, \ 
     "roles": {}, \ 
     "signupSteps": 0, \ 
     "staff": true, \ 
     "status": 0, \ 
     "superuser": true, \ 
     "syncedFromMdm": true, \ 
     "termsVersion": 0, \ 
     "ugroup_id": "string" \ 
   }' 'http://localhost:8080/api/v1/user/0045b213-e764-4ca5-b2e4-71058ef8b2bc'
  </code></pre>
  
  <p>This is the controller (the method itself is at the bottom):</p>
  
  <pre><code>package com.zimperium.server.user.controller;
  
  import com.querydsl.core.types.Predicate;
  import com.zimperium.server.user.dto.CreateUserRequest;
  import com.zimperium.server.user.entity.legacy.User;
  import io.swagger.annotations.Api;
  import io.swagger.annotations.ApiOperation;
  import io.swagger.annotations.Extension;
  import io.swagger.annotations.ExtensionProperty;
  import lombok.extern.slf4j.Slf4j;
  import org.springframework.beans.factory.annotation.Autowired;
  import org.springframework.data.domain.Page;
  import org.springframework.data.domain.Pageable;
  import org.springframework.data.querydsl.binding.QuerydslPredicate;
  import org.springframework.http.MediaType;
  import org.springframework.http.ResponseEntity;
  import org.springframework.web.bind.annotation.*;
  
  import java.io.IOException;
  
  @Slf4j
  @RestController
  @RequestMapping(value = "/api/v1/user", produces = {MediaType.APPLICATION_JSON_VALUE})
  @Api(value = "user",
          description = "Manage users")
  public class BackwardsCompatibleController {
  
      @Autowired UserController delegate;
  
      @ApiOperation(value = "Search for a user",
                    extensions =
                    @Extension(name = "api-info", properties =
                    @ExtensionProperty(name = "availability", value = "service")
                    )
      )
      @RequestMapping(method = RequestMethod.GET)
      public Page&lt;User&gt; find(@QuerydslPredicate(root = User.class)
                                     Predicate predicate,
                             Pageable pageable) {
          return delegate.find(predicate, pageable);
      }
  
      @RequestMapping(method = RequestMethod.GET, value = "all")
      public Iterable&lt;User&gt; find(@QuerydslPredicate(root = User.class) Predicate predicate) {
  
          //noinspection unchecked
          return delegate.find(predicate);
      }
  
      @ApiOperation(value = "Look up information about a user",
                    extensions =
                    @Extension(name = "api-info", properties =
                    @ExtensionProperty(name = "availability", value = "service")
                    )
      )
      @RequestMapping(value = "/{id}", method = RequestMethod.GET)
      public User get(@PathVariable(value = "id") String id) {
          return delegate.get(id);
      }
  
      @ApiOperation(value = "Create a user",
                    extensions =
                    @Extension(name = "api-info", properties =
                    @ExtensionProperty(name = "availability", value = "public")
                    )
      )
      @RequestMapping(method = RequestMethod.POST)
      public User save(@RequestBody CreateUserRequest req) throws IOException {
          return delegate.save(req);
      }
  
      @ApiOperation(value = "Update a user",
                    extensions =
                    @Extension(name = "api-info", properties =
                    @ExtensionProperty(name = "availability", value = "service")
                    )
      )
      @RequestMapping(value = "/{id}", method = RequestMethod.PUT)
      public void save(@PathVariable(value = "id") String id, @RequestBody User user) {
          delegate.save(id, user);
      }
  }
  </code></pre>
  
  <p>And this is the <code>User</code> class:</p>
  
  <pre><code>package com.zimperium.server.user.entity.legacy;
  
  import java.io.Serializable;
  import java.time.Duration;
  import java.util.Collection;
  import java.util.Date;
  import javax.persistence.Basic;
  import javax.persistence.Cacheable;
  import javax.persistence.CascadeType;
  import javax.persistence.Column;
  import javax.persistence.Entity;
  import javax.persistence.GeneratedValue;
  import javax.persistence.Id;
  import javax.persistence.OneToMany;
  import javax.persistence.OneToOne;
  import javax.persistence.Table;
  import javax.persistence.Temporal;
  import javax.persistence.TemporalType;
  import javax.persistence.Transient;
  import javax.validation.constraints.NotNull;
  import javax.validation.constraints.Size;
  
  import org.eclipse.persistence.annotations.UuidGenerator;
  
  import com.fasterxml.jackson.annotation.JsonIgnore;
  import com.fasterxml.jackson.annotation.JsonManagedReference;
  import lombok.AllArgsConstructor;
  import lombok.Builder;
  import lombok.Data;
  import lombok.EqualsAndHashCode;
  import lombok.NoArgsConstructor;
  import lombok.ToString;
  
  @Table(name = "users_user")
  @Entity
  @UuidGenerator(name = User.ID_GENERATOR)
  @Data
  @Builder
  @NoArgsConstructor
  @AllArgsConstructor
  @ToString(exclude = {"password"})
  @EqualsAndHashCode(of = {"email", "status"})
  @Cacheable(false)
  public class User implements Serializable
  {
      public static final Integer ACTIVE = 1;
      public static final Integer INACTIVE = 2;
      public static final Integer BLOCKED = 3;
      public static final Integer VERIFICATION_MAIL_SENT = 4;
      public static final Integer EXPIRED = 5;
      public static final Integer NEEDS_RE_LOGIN = 6;
      public static final Integer DELETED = 7;
  
      public static final String ID_GENERATOR = "user_id_gen";
      private static final long serialVersionUID = 1L;
  
  
      @Id
      @Size(min = 1, max = 64)
      @Column(name = "object_id", nullable = false, length = 64)
      @GeneratedValue(generator = ID_GENERATOR)
      private String objectId;
  
      @Basic(optional = false)
      @NotNull
      @Size(min = 1, max = 128)
      @Column(name = "password", nullable = false, length = 128)
      private String password;
  
      @Basic(optional = false)
      @NotNull
      @Column(name = "last_login", nullable = false)
      @Temporal(TemporalType.TIMESTAMP)
      private Date lastLogin;
  
      @Basic(optional = false)
      @NotNull
      @Column(name = "is_superuser", nullable = false)
      private boolean isSuperuser;
  
      // @Pattern(regexp="[a-z0-9!#$%&amp;'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&amp;'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?", message="Invalid email")//if the field contains email address consider using this annotation to enforce field validation
      @Basic(optional = false)
      @NotNull
      @Size(min = 1, max = 255)
      @Column(name = "email", nullable = false, length = 255)
      private String email;
  
      @Basic(optional = false)
      @NotNull
      @Size(min = 1, max = 120)
      @Column(name = "alias", nullable = false, length = 120)
      private String alias;
  
      @Size(max = 255)
      @Column(name = "first_name", length = 255)
      private String firstName;
  
      @Size(max = 255)
      @Column(name = "middle_name", length = 255)
      private String middleName;
  
      @Size(max = 255)
      @Column(name = "last_name", length = 255)
      private String lastName;
  
      @Size(max = 255)
      @Column(name = "ugroup_id", length = 255)
      private String ugroup_id;
  
      @Basic(optional = false)
      @NotNull
      @Column(name = "is_staff", nullable = false)
      private boolean isStaff;
  
      @Basic(optional = false)
      @NotNull
      @Column(name = "status", nullable = false)
      private int status;
  
      @Basic(optional = false)
      @NotNull
      @Column(name = "date_joined", nullable = false)
      @Temporal(TemporalType.TIMESTAMP)
      private Date dateJoined;
  
      @Basic(optional = false)
      @NotNull
      @Column(name = "agreed_to_terms", nullable = false)
      private boolean agreedToTerms;
  
      @Basic(optional = false)
      @NotNull
      @Column(name = "pwd_recovery_request", nullable = false)
      private boolean pwdRecoveryRequest;
  
      @Basic(optional = false)
      @NotNull
      @Column(name = "role", nullable = false)
      private int role;
  
      @Basic(optional = false)
      @NotNull
      @Column(name = "signup_steps", nullable = false)
      private int signupSteps;
  
      @Column(name = "terms_version")
      private Integer termsVersion;
  
      @Basic(optional = false)
      @NotNull
      @Column(name = "created_date", nullable = false)
      @Temporal(TemporalType.TIMESTAMP)
      private Date createdDate;
  
      @Basic(optional = false)
      @NotNull
      @Column(name = "modified_date", nullable = false)
      @Temporal(TemporalType.TIMESTAMP)
      private Date modifiedDate;
  
      @Size(max = 128)
      @Column(name = "phone_number", length = 128)
      private String phoneNumber;
  
      @Basic(optional = false)
      @NotNull
      @Column(name = "is_synced_from_mdm", nullable = false)
      private boolean isSyncedFromMdm;
  
      @Basic(optional = false)
      @NotNull
      @Column(name = "is_phone_number_verified", nullable = false)
      private boolean isPhoneNumberVerified;
  
      @Column(name = "last_sync_from_mdm")
      @Temporal(TemporalType.TIMESTAMP)
      private Date lastSyncFromMdm;
  
      @Column(name = "last_zconsole_login")
      @Temporal(TemporalType.TIMESTAMP)
      private Date lastZconsoleLogin;
  
      @Column(name = "password_expiration_date")
      @Temporal(TemporalType.TIMESTAMP)
      private Date passwordExpirationDate;
  
      @Column(name = "system_id")
      private String customerId;
  
      @JsonManagedReference
      @OneToMany(mappedBy="user", cascade = CascadeType.ALL)
      private Collection&lt;UserRole&gt; roles;
  
      @JsonIgnore
      @OneToMany(mappedBy="user", cascade = CascadeType.ALL)
      private Collection&lt;UsersUserUserPermissions&gt; usersUserUserPermissionses;
  
      @JsonIgnore
      @OneToMany(mappedBy="user", cascade = CascadeType.ALL)
      private Collection&lt;UsersUseruserobjectpermission&gt; usersUseruserobjectpermissions;
  
      @OneToOne(mappedBy="user", cascade = CascadeType.ALL)
      private AuthToken authToken;
  
      @JsonManagedReference
      @OneToMany(mappedBy = "user", cascade = CascadeType.ALL)
      private Collection&lt;AuthenticationTemporaryToken&gt; authenticationTemporaryTokens;
  
      @JsonManagedReference
      @OneToMany(mappedBy = "user", cascade = CascadeType.ALL)
      private Collection&lt;NotificationRule&gt; notificationRules;
  
  
  
      /**
       * Include the temporary token and expiration duration. These allow the user to reset his password without
       * knowing the previous password.
       */
      @Transient
      private String resetPasswordToken;
  
      @Transient
      private Duration resetPasswordTokenLifetime;
  }
  </code></pre>
  
  <p>I put a breakpoint on the first line of the function, and I got the error before reaching it. Also, it doesn't occur if I remove <code>@RequestBody User user</code> from the parameter list, which as much as I understand means that it happens on a fail attempt to convert the JSON to a <code>User</code> object.</p>