Ajax request to Spring REST api 415 error

2019-06-25 14:28发布

问题:

I have a problem with my Spring Rest controller. I am trying to post (PUT) data from my client (angularJS) to my server (Spring), but every time I try to send something I get a 415 Media not supported error.

With Maven I have added jackson-core (2.6.3) and jackson-databind (2.6.3) to my Spring API. I am also using @EnableWebMvc to automatically add the Jackson message converter to Spring. In my Spring controller I am using @RestController for access to the REST methods of Spring.


My REST API controller:

@RequestMapping(value = "/location/update/{id}", method = RequestMethod.PUT, consumes = MediaType.APPLICATION_JSON_VALUE)
public RippleUser updateUserLocation(@PathVariable("id") Integer id, @RequestBody RippleUser user) {
    return user;
}

I have tried differend types of consumes:

  • MediaType.APPLICATION_JSON_VALUE
  • "application/json"
  • Without consumes
  • And so on

My RippleUser model (partially)

@Entity
@Table(name = "user")
@JsonRootName(value = "user")
public class RippleUser implements Serializable {

    @NotNull
    @Column(name = "active", nullable = false)
    private boolean activeUser;

    @Column(name = "latitude", nullable = true)
    private Float lattitude;

    @Column(name = "longitude", nullable = true)
    private Float longitude;

    @Temporal(TemporalType.TIMESTAMP)
    @Column(name = "lastActive", nullable = true)
    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "dd-MM-yyyy HH:mm:ss")
    private Date timestamp;
}

In this model I have all the necessary getters and setters.


My AngularJS client:

 httpService.updateInBackend = function (url, data, callback, errorCallback) {
                http.put(url, data)
                        .then(function successCallback(response) {
                            callback(response.data);
                        }, function errorCallback(response) {
                            errorCallback(response);
                        });
            };

The URL: http://server:port/app/location/update/{id}

The data:

params: {
    {
        "user": {
            "latitude": 52.899370,
            "longitude": 5.804548,
            "timestamp": 1449052628407
        }
    }
};

For this method I also added @JsonRootName(value = "user") to my RippleUser model.

I have also tried without the user attribute (also removed it from my model):

params: {
    {
        "latitude": 52.899370,
        "longitude": 5.804548,
        "timestamp": 1449052628407
    }
};

The AngularJS HTTP method (PUT, POST, DELETE, GET, and so on) checks what type is in the parameters and automatically sets the right headers.


Chrome Postman

Just to make sure I have also tried this method in the Chrome Postman plugin

The URL: http://server:port/app/location/update/2

Method: PUT

Headers: Content-Type: application/json

Body:

{
    "latitude": 52.899370,
    "longitude": 5.804548,
    "timestamp": 1449052628407
}

The error this gives is:

HTTP Status 415 The server refused this request because the request entity is in a format not supported by the requested resource for the requested method.

Update

I can receive information in my RestController when I change my @ResponseBody from RippleUser to String:

@RequestMapping(value = "/location/update/{id}", method = RequestMethod.PUT, consumes = MediaType.APPLICATION_JSON_VALUE)
public RippleUser updateUserLocation(@PathVariable("id") Integer id, @RequestBody String user) {
    return user;
}

I also tried sending an empty user object, but that results in the same error message

Answer to my problem is below.

回答1:

The problem with this code is a bit different than I expected. The 415 media not supported error is thrown by Spring, because it could not fill in all the required fields with the post.

My solution was to add a class especially used by Spring to convert data to and from while sending and receiving. The benefit of this extra class is that I can manage the data the API sends and receives.


UserLocation

Adding the following class to my project did the trick (with getters and setters):

public class UserLocation {

    private Float latitude;

    private Float longitude;

    private Date lastActive;
}

This class contains all the necessary data for my PUT request. Spring automatically fills in all the fields.


Controller

To make this work I also had to modify my controller. The new code is as follows:

@RequestMapping(value = "/location/update/{id}", method = RequestMethod.PUT)
public UserLocation updateUserLocation(@PathVariable("id") Integer id, @RequestBody UserLocation location) {
    //Find user by ID
    //Add location to user
    //Update the user

    return //the new user
}

Hope this helps anyone