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

标签 java rest jersey jax-rs

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

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

我可以将异常作为 application/json 响应返回,但当我尝试将响应作为 application/xml 返回时,我从我的服务器有 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());
        }
    }

}

最佳答案

在 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 绑定(bind)到您的 XML。否则将生成错误,因为这会导致您的 XML 响应具有用于 XML 绑定(bind)的重复属性(如重复的代码元素)。

为什么?

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。因此,例如属性 code 不仅受 XmlElement 的约束,还受其 getter 方法的约束。

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

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

选项 2:简单地从类中删除 XmlElement 注释,在这种情况下,getter 将仅决定绑定(bind)。

此外,不要忘记更新映射器类的 toResponse 消息以反射(reflect)输出是 XML:

@Override
    public Response toResponse(AppException ex) {
        return Response.status(ex.getStatus())
                .entity(new ErrorMessage(ex))
                .type(MediaType.APPLICATION_XML).
                build();
    }

关于java - 如何在 JAX-RS Web 服务中返回 XML 格式的响应?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28328646/

相关文章:

java - FindBugs 不检测 SQL 注入(inject)

java - 数组中的数据去哪儿了?

java - 使用 postman 访问共享点网站的欢迎页面

python - 如何为我的 REST 客户端生成 WSDL 文件?

java - 如何使用 Jersey 和 Dropwizard 重定向用户并将消息传递到目标页面?

java - 我在哪里可以获得 Jersey 指南包?

java - 在 Jar 中读取和写入文件

java - 阻塞的java线程的方法可以被另一个线程执行吗?

java - 在 REST Web 服务中接受逗号分隔值

java - Swagger Dropwizard 0.7 - 不显示 JSON 参数的 TextArea