如何在JAX-RS Web服务中以XML格式返回响应?

我正在使用JAX-RS和Java 6以及Jersey 1.8开发REST服务,我的服务器是JBoss 5.1.0 GA,我开始使用一个接收参数并返回空值的简单端点.

this article之后,我将ExceptionMapper的预期异常映射到三个类:项目范围内异常的AppException,未找到记录异常的NotFoundException和预期错误之外的任何其他异常的GenericException.

我可以返回异常,因为application / json响应很好,但是当我尝试将响应作为application / xml返回时,我从我的服务器获得一个带有HTTP 500状态代码的HTML页面.

例如,如果我没有将所需参数发送到我的端点,我会正确获取其JSON响应的HTTP 400状态代码:

{
    "status": 400,
    "message": "org.xml.sax.SAXParseException: Premature end of file.",
    "developerMessage": "javax.ws.rs.WebApplicationException: org.xml.sax.SAXParseException: Premature end of file.... 40 more\r\n"
}

但是如果我尝试返回XML响应,我会得到这个HTML页面:

<html>
    <head>
        <title>JBoss Web/2.1.3.GA - Informe de Error</title>
    </head>
    <body>
        <h1>Estado HTTP 500 - </h1>
        <HR size="1" noshade="noshade">
            <p>
                <b>type</b> Informe de estado
            </p>
            <p>
                <b>mensaje</b>
                <u></u>
            </p>
            <p>
                <b>descripción</b>
                <u>El servidor encontró un error interno () que hizo que no pudiera rellenar este requerimiento.</u>
            </p>
            <HR size="1" noshade="noshade">
                <h3>JBoss Web/2.1.3.GA</h3>
     </body>
  </html>

我的ErrorMessage类看起来像我链接的文章中描述的:

import java.lang.reflect.InvocationTargetException;

import javax.ws.rs.core.Response;

import org.apache.commons.beanutils.*;

import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;

import com.sun.jersey.api.NotFoundException;

@XmlRootElement(name = "errorMessage")
public class ErrorMessage {

    @XmlElement(name = "status")
    int status;

    @XmlElement(name = "message")
    String message;

    @XmlElement(name = "developerMessage")
    String developerMessage;

    public int getStatus() {
        return status;
    }

    public void setStatus(int status) {
        this.status = status;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public String getDeveloperMessage() {
        return developerMessage;
    }

    public void setDeveloperMessage(String developerMessage) {
        this.developerMessage = developerMessage;
    }

    public ErrorMessage(AppException ex) {
        try {
            BeanUtils.copyProperties(this, ex);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }

    public ErrorMessage(NotFoundException ex) {
        this.status = Response.Status.NOT_FOUND.getStatusCode();
        this.message = ex.getMessage();
    }

    public ErrorMessage() {
    }

    @Override
    public String toString(){
        return "ErrorMessage{" + "status=" + status + ", message='" + message + '\'' + ", developerMessage='" + developerMessage + "'}";
    }
}

为什么我在指定时无法返回XML响应?

这是我正在使用的GenericExceptionMapper类

@Provider
public class GenericExceptionMapper implements ExceptionMapper<Throwable> {

    @Override
    public Response toResponse(Throwable exception) {

        ErrorMessage errorMessage = new ErrorMessage();     
        setHttpStatus(exception, errorMessage);
        errorMessage.setMessage(exception.getMessage());
        StringWriter errorStackTrace = new StringWriter();
        exception.printStackTrace(new PrintWriter(errorStackTrace));
        errorMessage.setDeveloperMessage(errorStackTrace.toString());
        // The only change is the MediaType.APPLICATION_XML, originally it was MediaType.APPLICATION_JSON
        return Response.status(errorMessage.getStatus()).entity(errorMessage).type(MediaType.APPLICATION_XML).build();
    }
    private void setHttpStatus(Throwable ex, ErrorMessage errorMessage) {
        if(ex instanceof WebApplicationException ) { 
            errorMessage.setStatus(((WebApplicationException)ex).getResponse().getStatus());
        } else {
            errorMessage.setStatus(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode());
        }
    }

}

解决方法:

在ErrorMessage类之上提供@XmlAccessorType(XmlAccessType.FIELD)应该可以解决问题:

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class ErrorMessage {

    /** contains the same HTTP Status code returned by the server */
    @XmlElement(name = "status")
    int status;

此JAXB批注将告诉服务器将属性而不是getter绑定到XML.否则将生成错误,因为这会导致XML响应具有XML绑定的重复属性(如重复的代码元素).

为什么?

By default, if @XmlAccessorType on a class is absent, and none of its
super classes is annotated with @XmlAccessorType, then the following
default on the class is assumed:

@XmlAccessorType(XmlAccessType.PUBLIC_MEMBER)

接下来,如果我们查看有关XmlAccessType的文档以了解PUBLIC_MEMBER,请注意以下内容:

Every public getter/setter pair and every public field
will be automatically bound to XML, unless annotated by XmlTransient.

因此,ErrorMessage的代码变得冲突,因为XmlElement也用于将JavaBean属性映射到XML.因此,例如,属性代码不仅受XmlElement的约束,而且受其getter方法的约束.

为了克服这个问题,我们有两个选择:

选项1:我们可以使用XmlAccessType.FIELD,以便只使用字段并省略getter.

选项2:只需从类中删除XmlElement注释,在这种情况下,getter将仅决定绑定.

另外,不要忘记更新mapper类的toResponse消息以反映输出是XML:

@Override
    public Response toResponse(AppException ex) {
        return Response.status(ex.getStatus())
                .entity(new ErrorMessage(ex))
                .type(MediaType.APPLICATION_XML).
                build();
    }
上一篇:Java Jersey PUT方法和工作客户端


下一篇:java – Moxy无法将L​​ist编组为JSON