Java 如何处理setter在JAXB自动解组器上从请求体引发的异常?
我将以下JSON请求正文发送到我的控制器:Java 如何处理setter在JAXB自动解组器上从请求体引发的异常?,java,web-services,rest,jaxb,jax-rs,Java,Web Services,Rest,Jaxb,Jax Rs,我将以下JSON请求正文发送到我的控制器: {"Game": {"url": "asd"}} @PUT @Path("/{name}") @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) public Response createRow( @PathParam("name") String name, Game gameData) throws Exception{
{"Game": {"url": "asd"}}
@PUT
@Path("/{name}")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Response createRow(
@PathParam("name") String name,
Game gameData) throws Exception{
Game.createRow(gameData); // + exception handling etc.
}
其中,Game
是我的模型类,用@XmlRootElement
注释(以及一些在此上下文中不重要的JPA注释)
控制员:
{"Game": {"url": "asd"}}
@PUT
@Path("/{name}")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Response createRow(
@PathParam("name") String name,
Game gameData) throws Exception{
Game.createRow(gameData); // + exception handling etc.
}
现在,我明白了当创建controller方法的Game-gameData
参数时,会调用model类中的setter。需要注意的设定者是:
public void setUrl(String url) throws Exception{
String regex = "^(https?|ftp|file)://[-a-zA-Z0-9+&@#/%?=~_|!:,.;]*[-a-zA-Z0-9+&@#/%=~_|]";
Pattern pattern = Pattern.compile(regex);
System.out.println("URL: " + url);
if ( url == null || url.length() == 0) {
throw new Exception("The url of the game is mandatory!");
} else {
Matcher matcher = pattern.matcher(url);
if (!matcher.matches()) {
throw new Exception("The url is invalid! Please check its syntax!");
} else {
this.url = url;
}
}
}
发生的情况是,在将JSON字符串反序列化到游戏对象的过程中,url无效代码>异常被抛出,但仅在TomEE
的控制台中抛出。我要做的是将此错误发送到客户端
如果我使用一个扩展了WebApplicationException
的异常而不是通用异常,那么我会在客户机中得到一个异常,但不是关于url有效性的异常。相反,在反序列化之后,gameData.url
为NULL,当我尝试使用gameData
中的数据创建游戏实例时,setter将被称为像gameToBeCreated.set(NULL)
,当实际从客户端发送url时,我将得到异常url是必需的
,但是语法不好。从客户端发送时,它不为NULL
那么,我是否可以在发生自动解组时拦截抛出的异常并将其转发给客户端?使用JAXB拦截异常
ValidationEventHandler
用于拦截解组期间发生的异常。如果要收集发生的所有错误,可以使用ValidationEventCollector
Java模型
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement
public class Foo {
private String bar;
public String getBar() {
return bar;
}
public void setBar(String bar) {
throw new RuntimeException("Always throw an error");
}
}
演示
import java.io.StringReader;
import javax.xml.bind.*;
import javax.xml.bind.util.ValidationEventCollector;
public class Demo {
private static String XML = "<foo><bar>Hello World</bar></foo>";
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Foo.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
unmarshaller.unmarshal(new StringReader(XML));
ValidationEventCollector vec = new ValidationEventCollector();
unmarshaller.setEventHandler(vec);
unmarshaller.unmarshal(new StringReader(XML));
System.out.println(vec.getEvents().length);
}
}
与JAX-RS集成
MessageBodyReader
您可以创建MessageBodyReader
的实例,在该实例中,您可以在解组过程中利用ValidationEventHandler
。下面的链接给出了如何实现MessageBodyReader
的示例
ContextResolver
稍微高一点,您可以实现ContextResolver
,并返回Unmarshaller
,并在其上添加相应的ValidationEventHandler
。它看起来类似于以下内容:
import javax.ws.rs.core.*;
import javax.ws.rs.ext.*;
import javax.xml.bind.*;
import javax.xml.bind.helpers.DefaultValidationEventHandler;
@Provider
public class UnmarshallerResolver implements ContextResolver<Unmarshaller> {
@Context Providers providers;
@Override
public Unmarshaller getContext(Class<?> type) {
try {
ContextResolver<JAXBContext> resolver = providers.getContextResolver(JAXBContext.class, MediaType.APPLICATION_XML_TYPE);
JAXBContext jaxbContext;
if(null == resolver || null == (jaxbContext = resolver.getContext(type))) {
jaxbContext = JAXBContext.newInstance(type);
}
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
unmarshaller.setEventHandler(new DefaultValidationEventHandler());
return unmarshaller;
} catch(JAXBException e) {
throw new RuntimeException(e);
}
}
}
导入javax.ws.rs.core.*;
导入javax.ws.rs.ext.*;
导入javax.xml.bind.*;
导入javax.xml.bind.helpers.DefaultValidationEventHandler;
@提供者
公共类UnmarshallerResolver实现ContextResolver{
@上下文提供者;
@凌驾
公共解组器getContext(类类型){
试一试{
ContextResolver resolver=providers.getContextResolver(JAXBContext.class,MediaType.APPLICATION\u XML\u TYPE);
JAXBContext JAXBContext;
if(null==resolver | | null==(jaxbContext=resolver.getContext(type))){
jaxbContext=jaxbContext.newInstance(类型);
}
Unmarshaller Unmarshaller=jaxbContext.createUnmarshaller();
setEventHandler(新的DefaultValidationEventHandler());
返回解组器;
}捕获(JAXBEException e){
抛出新的运行时异常(e);
}
}
}
似乎没有内置的解决方案
我的解决方案(必须采用您的代码,并且可以改进(当然,提交响应代码)):
创建错误列表类
@XmlTransient
private ArrayList<ErrorList> errorList = new ArrayList<ErrorList>();
这个类将用于存储错误,这些错误发生在游戏模型类的setter中
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Response;
public class ErrorList {
private String errorString;
private String errorObject;
public ErrorList() {
}
public ErrorList(String errorString, String errorObject) {
this.setErrorString(errorString);
this.setErrorObject(errorObject);
}
public String getErrorString() {
return errorString;
}
public void setErrorString(String errorString) {
this.errorString = errorString;
}
public String getErrorObject() {
return errorObject;
}
public void setErrorObject(String errorObject) {
this.errorObject = errorObject;
}
public Response sendError() {
throw new WebApplicationException(
Response.status(400)
.header("Access-Control-Allow-Origin", "*")
.entity(this.getErrorString() + " caused by " + this.getErrorObject())
.build());
}
}
向游戏模型类添加新的列表属性
@XmlTransient
private ArrayList<ErrorList> errorList = new ArrayList<ErrorList>();
向游戏模型类添加一个方法,该方法检查错误列表是否已填充
public static void checkErrorList(Game gameData) {
if (gameData.errorList != null && !gameData.errorList.isEmpty()) {
gameData.errorList.get(0).sendError();
}
}
最后
现在您可以添加
checkErrorList(yourGameInstance);
例如,在开始的时候使用您的方法
public static Response createRow(Game gameData) {
checkErrorList(gameData);
...
}
无论何时设置某些内容,都应该在尝试将数据发送到数据库之前再次调用它
public static Response createRow(Game gameData, String newUrl) {
checkErrorList(gameData);
...
gameData.setUrl(newUrl);
checkErrorList(gameData);
}
它不像内置函数那样花哨,但至少它完成了任务
是的,我是(JAVA/)JAXB的新手
可以通过省略ErrorList类并在game类中使用一维ArrayList来简化该方法。不建议抛出异常来执行已知可能结果的流控制和验证逻辑。抛出异常类似于其他语言中的goto语句,告诉流控制我想自动转到这里。听起来您可以在响应中包含URL状态/验证,并调用类似于以下内容的isUrlValid方法来完成所需的操作:
public void setUrl(String url){
this.url = url;
}
public boolean isUrlValid(){
String regex = "^(https?|ftp|file)://[-a-zA-Z0-9+&@#/%?=~_|!:,.;]*[-a-zA-Z0-9+&@#/%=~_|]";
Pattern pattern = Pattern.compile(regex);
System.out.println("URL: " + this.url);
Matcher matcher = pattern.matcher(this.url);
return matcher.matches();
}
public boolean isUrlEmpty(){
return this.url == null || this.url.length() == 0;
}
答复:
@PUT
@Path("/{name}")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Response createRow(
@PathParam("name") String name,
Game gameData){
if(!gameData.isUrlEmpty() && gameDate.isUrlValid()){
Game.createRow(gameData);
}
String json = //convert Game entity to json or include error msgs in JSON
return Response.ok(json, MediaType.APPLICATION_JSON).build();
}是否有其他选项不包括实现
MessageBodyReader
?我想使用自动解组并以某种方式将抛出的异常发送给客户端。@SorinAdrianCarbunaru-实现ContextResolver
可能更适合您。我们正在使用TomEE,不幸的是,Apache CXF不支持@Provider
。。。一位比我经验丰富的同事尝试以编程方式注册提供程序,但失败了…RestEasy 3.0.7.FINAL不理解ContextResolver请参阅:MessageBodyReader方法虽然有效,但我认为这是可行的,但正如您所说,这不是一个“奇特”的解决方案。我仍在等待与自动解组相关的答案。要么告诉我如何以我想要的方式解决问题,要么就是简单地确认我想要的是不可能的。问题是我们想要在setter上保持验证。。。我们相信,做验证是一个设定者的职责……似乎有很多工作和额外的复杂性,尽管我想我们都有不同的信念:)根据你的例子,如果你继续沿着这条路走下去,我肯定会