Java 如何处理setter在JAXB自动解组器上从请求体引发的异常?

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{

我将以下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.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上保持验证。。。我们相信,做验证是一个设定者的职责……似乎有很多工作和额外的复杂性,尽管我想我们都有不同的信念:)根据你的例子,如果你继续沿着这条路走下去,我肯定会