我正在使用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();
}