我使用 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响应?
@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());
}
}
}
答案 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();
}