JAX-RS/Jersey如何自定义错误处理?
-
06-09-2019 - |
题
我正在使用 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 Bad Request”)?记录错误怎么办?也许在自定义标头中添加问题描述(“错误的日期格式”)以帮助故障排除?或者返回包含详细信息的完整错误响应以及 5xx 状态代码?
解决方案
有几种方法来定制用JAX-RS的错误处理的行为。这里有三个的更容易的方法。
第一种方法是创建一个扩展WebApplicationException异常类。
示例:
public class NotAuthorizedException extends WebApplicationException {
public NotAuthorizedException(String message) {
super(Response.status(Response.Status.UNAUTHORIZED)
.entity(message).type(MediaType.TEXT_PLAIN).build());
}
}
和抛出这个新创建的异常你只需:
@Path("accounts/{accountId}/")
public Item getItem(@PathParam("accountId") String accountId) {
// An unauthorized user tries to enter
throw new NotAuthorizedException("You Don't Have Permission");
}
注意,你并不需要声明异常的throws子句,因为WebApplicationException是一个运行时异常。这将返回一个401响应给客户机。
在第二和更简单的方法是直接在代码简单地构造WebApplicationException的一个实例。这种方法的工作原理,只要你没有实现自己的应用程序异常。
示例:
@Path("accounts/{accountId}/")
public Item getItem(@PathParam("accountId") String accountId) {
// An unauthorized user tries to enter
throw new WebApplicationException(Response.Status.UNAUTHORIZED);
}
此代码太返回401给客户机。
当然,这只是一个简单的例子。您可以在必要时异常复杂得多,并且可以生成你需要什么都HTTP响应代码。
其它一种方法是包装已有异常,也许使用实现与@Provider注解的ExceptionMapper接口的小包装类的ObjectNotFoundException。这告诉JAX-RS运行时,如果包装的异常升高时,返回在ExceptionMapper定义的响应代码。
其他提示
@Provider
public class BadURIExceptionMapper implements ExceptionMapper<NotFoundException> {
public Response toResponse(NotFoundException exception){
return Response.status(Response.Status.NOT_FOUND).
entity(new ErrorResponse(exception.getClass().toString(),
exception.getMessage()) ).
build();
}
}
创建上面的类。这将处理404(NotFoundException),并在这里toResponse方法,你可以给你的自定义响应。与此类似的还有ParamException等,这些你需要映射到提供定制响应。
泽西抛出时它未能解组参数以一个解决方案是创建处理这些类型的异常的ExceptionMapper一个com.sun.jersey.api.ParamException:
@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();
}
}
您也可以写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响应)琐碎,使用这种类允许因子出参数处理中一般可能包括日志记录等
一个明显的解决方案:参加一个字符串,转换为日期自己。你可以定义你想要的格式,捕获异常,要么重新抛出或自定义错误的方式被发送。 用于解析,SimpleDateFormat的应该正常工作。
我肯定有办法挂钩的数据类型处理过,但简单的代码或许有点是你在这种情况下需要。
我也喜欢 斯塔克斯曼 可能会将该 QueryParam 实现为 String,然后处理转换,并根据需要重新抛出。
如果区域设置特定的行为是所需和预期的行为,您将使用以下命令返回 400 BAD REQUEST 错误:
throw new WebApplicationException(Response.Status.BAD_REQUEST);
请参阅 JavaDoc javax.ws.rs.core.Response.Status 以获得更多选择。
@QueryParam文档说
”注释参数,字段或属性的类型T必须 之一:
1)一个原始类型,点击 2)具有接受单个构造 String参数结果 3)命名的valueOf或fromString静态方法 接受单个字符串参数(参见,例如, Integer.valueOf(字符串))点击 4)已注册的实现 javax.ws.rs.ext.ParamConverterProvider JAX-RS扩展SPI那 能够返回的一个实例javax.ws.rs.ext.ParamConverter“从 字符串”转换为类型。结果 5)是List,Set和 SortedSet的,其中T满足2,3或4个以上。所结果的 集合是只读的。 “
如果您想要控制响应进入用户时String形式查询参数不能转换到您的类型T,你可以扔掉WebApplicationException。 Dropwizard自带您可以使用您需要以下*帕拉姆类。
BooleanParam,DateTimeParam,IntParam,LongParam,LocalDateParam,NonEmptyStringParam,UUIDParam。请参见 https://开头github.com/dropwizard/dropwizard/tree/master/dropwizard-jersey/src/main/java/io/dropwizard/jersey/params
如果您需要乔达日期时间,只需使用Dropwizard的 DateTimeParam 。
如果上面的列表不适合您的需求,通过扩展AbstractParam定义自己。覆盖的解析方法。如果需要在错误响应身体控制,倍率误差的方法。
这科达黑尔在此好文章是在 HTTP:// codahale。 COM /什么 - 让 - 新泽西州有趣的参数类/
import io.dropwizard.jersey.params.AbstractParam;
import java.util.Date;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
public class DateParam extends AbstractParam<Date> {
public DateParam(String input) {
super(input);
}
@Override
protected Date parse(String input) throws Exception {
return new Date(input);
}
@Override
protected Response error(String input, Exception e) {
// customize response body if you like here by specifying entity
return Response.status(Status.BAD_REQUEST).build();
}
}
日期(字符串ARG)构造函数被弃用。如果你对Java的8否则乔达日期时间,建议我将使用Java 8日期类。
这是正确的行为实际上。泽西将设法找到您的输入处理程序,并会尝试从所提供的输入构造一个对象。在这种情况下,它会尝试创建一个提供给构造函数的值X的新Date对象。由于这是一个无效的日期,按照惯例泽西将返回404
你可以做的是重写,并把出生日期为字符串,然后尝试解析,如果你没有得到你想要的东西,你可以自由受到任何异常映射机制来扔你想要的任何异常(有若干个)。
只是作为一个扩展@Steven拉文回答的情况下,要打开浏览器登录窗口。我发现很难正确地返回响应( 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"); }