Getting 400 bad request from same application on W

2019-06-13 15:31发布

问题:

I've been developing an application that provides a REST service. I have some tested code that I run against it to see if it works okay.

When running it against the application deployed on my local Weblogic development server, it works fine.

However, when I deployed it on another Weblogic server on a Red Hat machine, I get 400 Bad Request errors.

Here is the client code I'm using to test the service:

    Client client = Client.create();
    //WebResource webResource = client.resource("http://10.1.1.2:7001/NotificationFramework/rest/notifications/createNotification");
    WebResource webResource = client.resource("http://rhvm:7003/NotificationFramework/rest/notifications/createNotification");

    ClientResponse clientResponse = webResource.type("application/json").post(ClientResponse.class, testJsonObject.toString());
    JSONObject response2 = new JSONObject(clientResponse.getEntity(String.class)); 
    System.out.println(response2);

The commented line is the one on my local machine.

Here is the response I'm getting:

An error occurred: Server returned HTTP response code: 400 for URL: http://rhvm:7003/NotificationFramework/rest/notifications/createNotification

And here is an excerpt of the code providing the REST service:

@Path("/notifications")
public class RestServices {     

    @POST
    @Path("/createNotification")
    @Consumes( {MediaType.APPLICATION_JSON} )
    @Produces( {MediaType.APPLICATION_JSON} )
    public static NotificationResponse createNotification(JAXBElement<Notification> n) {
// do some stuff

return notificationResponse;
}

I've already tried putting an extra / on the end. And I've tested it with the RESTClient add-on for Firefox and I get the exact same behaviour.

Any help would be greatly appreciated.

Thanks in advance.

// Edit

I discovered that it's something to do with the JAXBElement.

The following services works:

@POST
@Path("testRest3")
@Consumes( {MediaType.APPLICATION_JSON} )
@Produces({MediaType.APPLICATION_JSON})
public static NotificationResponse testRest3() {
    logger.info("yo3");

    return new NotificationResponse(101, "yo");
}

but the following doesn't:

@POST
@Path("testRest4")
@Consumes( {MediaType.APPLICATION_JSON} )
@Produces({MediaType.APPLICATION_JSON})
public static NotificationResponse testRest4(JAXBElement<Notification> n) {
    logger.info("yo4");

    return new NotificationResponse(101, "yo");
}

I checked the Notification class as recommended by pestrella and found that @XmlRootElement was missing. I added this but this still hasn't fixed the problem. I'm not sure if it should be @Xml.. but I'm new to this. Following the tutorial from vogella.

Here is my Notification class:

@XmlRootElement
public class Notification {
    private int applicationId;
    private int notificationId;
    private int priority;
    private String message;
    private String detail;
    private String appUrl;

// methods and stuff

}

And here is the body as submitted with the RESTClient add-on for Firefox:

{"appUrl":"","message":"my message","notificationId":1110001,"detail":"my detail","priority":3,"applicationId":111}

回答1:

A 400 response in this instance can indicate some sort of error while unmarhsalling the POST body.

Using the @XMLRootElement is fine. However, you might have problems getting JAXB to unmarshal to primitive types (depends on what version you've got).

The safest way to get JAXB to unmarshal your Notification object is to use the Integer type instead of a primitive int type.

@XmlRootElement
public class Notification {
    private Integer applicationId;

    /* and the rest... */
}

Also, you should not need to wrap the Notification object with JAXBElement if you are using the @XmlRootElement annotation. Try removing the JAXBElement wrapper:

@POST
@Path("testRest")
@Consumes({MediaType.APPLICATION_JSON})
@Produces({MediaType.APPLICATION_JSON})
public static NotificationResponse testRest(Notification n) {
    logger.info("yo!");
    return new NotificationResponse(101, "yo");
}

If the problem persists, then you can always use a MessageBodyReader to unmarshal request bodies manually.

The typical way to do this for JSON request bodies is to implement MessageBodyReader and use a JSON parser of your choice, like Gson or Jackson.

@Provider
@Consumes("application/json")
public class CustomJsonReader<T> implements MessageBodyReader<T> {

    @Override
    public boolean isReadable(Class<?> type, Type genericType,
            Annotation[] annotations,MediaType mediaType) {
        return true;
    }

    @Override
    public T readFrom(Class<T> type, Type genericType, Annotation[] annotations,
            MediaType mediaType, MultivaluedMap<String, String> httpHeaders,
            InputStream entityStream) throws IOException, WebApplicationException {

        /* Convert the request body (passed in as InputStream) to a String.
         * Here I'm using apache commons IOUtils to convert the input stream
         */
        StringWriter writer = new StringWriter();
        IOUtils.copy(entityStream, writer, "UTF-8");
        String json = writer.toString();

        /* Use JSON parser to unmarshal the JSON to a complex object */
        return new Gson().fromJson(json, genericType);
    }
}