我正在使用Jersey学习JAX-RS(又名JSR-311)。我已经成功地创建了一个根资源,并正在摆弄参数:

@Path("/hello")
public class HelloWorldResource {

    @GET
    @Produces("text/html")
    public String get(
        @QueryParam("name") String name,
        @QueryParam("birthDate") Date birthDate) {

         // Return a greeting with the name and age
    }
}

这工作得很好,并处理当前区域内Date(String)构造函数能理解的任何格式(如YYYY/mm/dd和mm/dd/YYYY)。但如果我提供的值无效或无法理解,就会得到404响应。

例如:

GET /hello?name=Mark&birthDate=X

404 Not Found

如何自定义此行为?也许是一个不同的响应代码(可能是“400个坏请求”)?记录一个错误怎么样?也许可以在自定义报头中添加问题的描述(“糟糕的日期格式”)以帮助排除故障?或者返回一个完整的带有详细信息的Error响应,以及一个5xx状态码?


当前回答

如果你想打开浏览器登录窗口,就把@史蒂文拉文的答案作为一个扩展。我发现很难从过滤器中正确返回响应(MDN HTTP身份验证),如果用户还没有经过身份验证

这帮助我构建响应来强制浏览器登录,注意头的额外修改。这将把状态代码设置为401,并设置导致浏览器打开用户名/密码对话框的报头。

// The extended Exception class
public class NotLoggedInException extends WebApplicationException {
  public NotLoggedInException(String message) {
    super(Response.status(Response.Status.UNAUTHORIZED)
      .entity(message)
      .type(MediaType.TEXT_PLAIN)
      .header("WWW-Authenticate", "Basic realm=SecuredApp").build()); 
  }
}

// Usage in the Filter
if(headers.get("Authorization") == null) { throw new NotLoggedInException("Not logged in"); }

其他回答

如果你想打开浏览器登录窗口,就把@史蒂文拉文的答案作为一个扩展。我发现很难从过滤器中正确返回响应(MDN HTTP身份验证),如果用户还没有经过身份验证

这帮助我构建响应来强制浏览器登录,注意头的额外修改。这将把状态代码设置为401,并设置导致浏览器打开用户名/密码对话框的报头。

// The extended Exception class
public class NotLoggedInException extends WebApplicationException {
  public NotLoggedInException(String message) {
    super(Response.status(Response.Status.UNAUTHORIZED)
      .entity(message)
      .type(MediaType.TEXT_PLAIN)
      .header("WWW-Authenticate", "Basic realm=SecuredApp").build()); 
  }
}

// Usage in the Filter
if(headers.get("Authorization") == null) { throw new NotLoggedInException("Not logged in"); }

我太喜欢StaxMan可能会实现QueryParam作为一个字符串,然后处理转换,重新抛出必要的。

如果特定于区域设置的行为是期望和预期的行为,您将使用以下命令返回400 BAD REQUEST错误:

抛出新的WebApplicationException(Response.Status.BAD_REQUEST);

有关更多选项,请参阅JavaDoc中的javax.ws.rs.core.Response.Status。

还可以为带queryparam注释的变量编写可重用类

public class DateParam {
  private SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");

  private Calendar date;

  public DateParam(String in) throws WebApplicationException {
    try {
      date = Calendar.getInstance();
      date.setTime(format.parse(in));
    }
    catch (ParseException exception) {
      throw new WebApplicationException(400);
    }
  }
  public Calendar getDate() {
    return date;
  }
  public String format() {
    return format.format(value.getTime());
  }
}

然后这样使用它:

private @QueryParam("from") DateParam startDateParam;
private @QueryParam("to") DateParam endDateParam;
// ...
startDateParam.getDate();

尽管在这种情况下错误处理很简单(抛出一个400响应),但是使用这个类可以让您在一般情况下分解参数处理,包括日志记录等。

Jersey在解组参数失败时抛出com.sun.jersey.api.ParamException异常,因此一个解决方案是创建一个ExceptionMapper来处理这些类型的异常:

@Provider
public class ParamExceptionMapper implements ExceptionMapper<ParamException> {
    @Override
    public Response toResponse(ParamException exception) {
        return Response.status(Status.BAD_REQUEST).entity(exception.getParameterName() + " incorrect type").build();
    }
}

这实际上是正确的行为。Jersey将尝试为您的输入找到一个处理程序,并将尝试从提供的输入构造一个对象。在这种情况下,它将尝试使用提供给构造函数的值X创建一个新的Date对象。由于这是一个无效日期,按照惯例Jersey将返回404。

您可以重写并将出生日期作为字符串,然后尝试解析,如果您没有得到想要的结果,您可以通过任何异常映射机制(有几种)自由抛出任何您想要的异常。