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

时间:2015-02-04 18:25:13

标签: java rest jersey jax-rs

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

this article之后,我将预期的异常与 ExceptionMapper 映射到三个类: AppException ,用于项目范围内的异常, NotFoundException < / strong>用于未找到记录的异常, GenericException 用于超出预期错误的任何其他异常。

我可以将 application / json 响应作为异常返回,但是当我尝试将响应作为 application / xml 返回时,我会从中获取一个HTML页面我的服务器使用 HTTP 500 状态代码。

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

{
    "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());
        }
    }

}

1 个答案:

答案 0 :(得分:4)

在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绑定的重复属性(如重复的代码元素)。

<强> WHY吗

  

默认情况下,如果某个类上的@XmlAccessorType不存在,则不存在   超类用@XmlAccessorType注释,然后是以下内容   假定该类的默认值为:

     

@XmlAccessorType(XmlAccessType.PUBLIC_MEMBER)

接下来,如果我们查看有关XmlAccessType的文档以了解PUBLIC_MEMBER,那么它就是这样说的:

  

每个公共getter / setter对和每个公共领域   将自动绑定到XML,除非通过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();
    }