2009-02-24 29 views
199

Tôi đang học JAX-RS (aka, JSR-311) sử dụng Jersey. Tôi đã tạo thành công một nguồn gốc và đang chơi xung quanh với các thông số:JAX-RS/Jersey làm thế nào để tùy chỉnh xử lý lỗi?

@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 
    } 
} 

này hoạt động tuyệt vời, và xử lý bất kỳ định dạng trong miền địa phương hiện nay được hiểu bởi ngày (String) constructor (như YYYY/mm/đ và dd/mm/YYYY). Nhưng nếu tôi cung cấp một giá trị không hợp lệ hoặc không được hiểu, tôi sẽ nhận được phản hồi 404.

Ví dụ:

GET /hello?name=Mark&birthDate=X 

404 Not Found 

Làm thế nào tôi có thể tùy chỉnh hành vi này? Có thể một mã phản hồi khác (có thể là "400 Yêu cầu Xấu")? Điều gì về việc đăng nhập một lỗi? Có thể thêm mô tả sự cố ("định dạng ngày không hợp lệ") trong tiêu đề tùy chỉnh để hỗ trợ khắc phục sự cố? Hoặc trả lại toàn bộ phản hồi Lỗi cùng với chi tiết, cùng với mã trạng thái 5xx?

Trả lời

253

Có một số cách tiếp cận để tùy chỉnh hành vi xử lý lỗi với JAX-RS. Dưới đây là ba cách dễ dàng hơn.

Cách tiếp cận đầu tiên là tạo một lớp Ngoại lệ mở rộng WebApplicationException.

Ví dụ:

public class NotAuthorizedException extends WebApplicationException { 
    public NotAuthorizedException(String message) { 
     super(Response.status(Response.Status.UNAUTHORIZED) 
      .entity(message).type(MediaType.TEXT_PLAIN).build()); 
    } 
} 

Và để ném này mới được tạo ra ngoại lệ bạn chỉ đơn giản là:

@Path("accounts/{accountId}/") 
    public Item getItem(@PathParam("accountId") String accountId) { 
     // An unauthorized user tries to enter 
     throw new NotAuthorizedException("You Don't Have Permission"); 
} 

Chú ý, bạn không cần phải khai báo ngoại lệ trong một ném khoản vì WebApplicationException là một thời gian chạy Ngoại lệ. Thao tác này sẽ trả về phản hồi 401 cho ứng dụng khách.

Cách tiếp cận thứ hai và dễ dàng hơn là chỉ cần xây dựng một cá thể của WebApplicationException trực tiếp trong mã của bạn. Cách tiếp cận này hoạt động miễn là bạn không phải triển khai ngoại lệ ứng dụng của riêng mình.

Ví dụ:

@Path("accounts/{accountId}/") 
public Item getItem(@PathParam("accountId") String accountId) { 
    // An unauthorized user tries to enter 
    throw new WebApplicationException(Response.Status.UNAUTHORIZED); 
} 

Mã này quá trả về một 401 cho khách hàng.

Tất nhiên, đây chỉ là một ví dụ đơn giản. Bạn có thể làm cho Ngoại lệ phức tạp hơn nhiều nếu cần và bạn có thể tạo mã phản hồi http mà bạn cần.

Một cách tiếp cận khác là bọc một ngoại lệ hiện có, có lẽ là một ObjectNotFoundException với lớp bao bọc nhỏ thực hiện giao diện ExceptionMapper được chú thích bằng chú giải @Provider. Điều này cho biết thời gian chạy JAX-RS, rằng nếu ngoại lệ bọc được nâng lên, trả về mã phản hồi được xác định trong ExceptionMapper.

+3

Trong ví dụ của bạn, cuộc gọi đến siêu() nên hơi khác nhau:.. siêu (Response.status (Status.UNAUTHORIZED) thực thể (message) .type ("text/plain") xây dựng()); Cảm ơn sự thấu hiểu. –

+0

Tôi nghĩ rằng phương pháp này sẽ không được thực hiện trong ví dụ của anh ta. – deamon

+61

Trong kịch bản được đề cập trong câu hỏi, bạn sẽ không có cơ hội để ném một ngoại lệ, vì Jersey sẽ tăng ngoại lệ vì nó sẽ không thể tạo đối tượng Date của giá trị đầu vào. Có cách nào để ngăn chặn ngoại lệ Jersey? Có một giao diện ExceptionMapper, tuy nhiên nó cũng chặn các ngoại lệ được ném bởi phương thức này (trong trường hợp này). –

11

Một giải pháp rõ ràng: lấy một String, tự chuyển thành Ngày. Bằng cách đó bạn có thể xác định định dạng mà bạn muốn, bắt ngoại lệ và ném lại hoặc tùy chỉnh lỗi được gửi. Để phân tích cú pháp, SimpleDateFormat sẽ hoạt động tốt.

Tôi chắc chắn có nhiều cách để xử lý các loại dữ liệu, nhưng có lẽ một chút mã đơn giản là tất cả những gì bạn cần trong trường hợp này.

5

Tôi cũng thích StaxMan có lẽ sẽ triển khai QueryParam dưới dạng Chuỗi, sau đó xử lý chuyển đổi, làm lại khi cần thiết.

Nếu hành vi locale cụ thể là hành vi mong muốn và mong đợi, bạn sẽ sử dụng sau đây để trả lại lỗi 400 BAD REQUEST:

throw new WebApplicationException(Response.Status.BAD_REQUEST);

Xem javadoc cho javax.ws.rs.core.Response.Status cho các tùy chọn hơn.

26

Bạn cũng có thể viết một lớp thể tái sử dụng cho các biến QueryParam-chú thích

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()); 
    } 
} 

sau đó sử dụng nó như thế này:

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

Mặc dù việc xử lý lỗi là tầm thường trong trường hợp này (ném một phản ứng 400), sử dụng lớp này cho phép bạn xử lý thông số yếu tố nói chung có thể bao gồm việc ghi nhật ký, v.v.

+0

Tôi đang cố gắng thêm một trình xử lý truy vấn tùy chỉnh ở Jersey (di chuyển từ CXF), điều này trông khá giống với những gì tôi đang làm nhưng tôi không biết cách cài đặt/tạo nhà cung cấp mới. Lớp học trên của bạn không cho tôi thấy điều này. Tôi đang sử dụng JodaTime DateTime đối tượng cho QueryParam và không có một nhà cung cấp để giải mã chúng. Có dễ dàng như phân lớp nó, cho nó một hàm tạo String và xử lý nó không? –

+1

Chỉ cần tạo một lớp như lớp 'DateParam' ở trên bao bọc một' org.joda.time.DateTime' thay vì 'java.util.Calendar'. Bạn sử dụng nó với '@ QueryParam' thay vì' DateTime'. –

+1

Nếu bạn đang sử dụng Joda DateTime, áo đi kèm với DateTimeParam để bạn có thể sử dụng trực tiếp. Không cần phải viết của riêng bạn. Xem https://github.com/dropwizard/dropwizard/blob/master/dropwizard-jersey/src/main/java/io/dropwizard/jersey/params/DateTimeParam.java – Srikanth

63
@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(); 
} 
} 

Tạo lớp học trên. Điều này sẽ xử lý 404 (NotFoundException) và ở đây trong phương thức toResponse bạn có thể trả lời tùy chỉnh của bạn. Tương tự như vậy có ParamException, vv mà bạn sẽ cần phải ánh xạ để cung cấp các câu trả lời tùy chỉnh.

+0

Bạn có thể sử dụng ExceptionMapper cũng như các trường hợp ngoại lệ chung – userRaj

+0

Điều này sẽ xử lý các lỗi WebApplicationExceptions do Khách hàng JAX-RS ném ra, ẩn nguồn gốc lỗi. Tốt hơn là có ngoại lệ tùy chỉnh (không bắt nguồn từ WebApplicationException) hoặc ném WebApplications với Phản hồi hoàn chỉnh. WebApplicationExceptions do JAX-RS gửi Khách hàng phải được xử lý trực tiếp tại cuộc gọi, nếu không phản hồi của một dịch vụ khác được chuyển qua như là phản hồi dịch vụ của bạn mặc dù đó là lỗi máy chủ nội bộ chưa được giải quyết. –

33

Jersey ném một com.sun.jersey.api.ParamException khi nó không unmarshall các thông số để có một giải pháp là tạo ra một ExceptionMapper để xử lý các loại ngoại lệ:

@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(); 
    } 
} 
+0

Tôi nên tạo công cụ lập bản đồ này cho Jersey để đăng ký ở đâu? – Patricio

+1

Tất cả những gì bạn phải làm là thêm chú thích @Provider, xem tại đây để biết thêm chi tiết: http://stackoverflow.com/questions/15185299/jax-rs-jersey-exceptionmappers-user-defined-exception –

4

tài liệu @QueryParam nói

" The type T of the annotated parameter, field or property must either:

1) Be a primitive type
2) Have a constructor that accepts a single String argument
3) Have a static method named valueOf or fromString that accepts a single String argument (see, for example, Integer.valueOf(String))
4) Have a registered implementation of javax.ws.rs.ext.ParamConverterProvider JAX-RS extension SPI that returns a javax.ws.rs.ext.ParamConverter instance capable of a "from string" conversion for the type.
5) Be List, Set or SortedSet, where T satisfies 2, 3 or 4 above. The resulting collection is read-only. "

Nếu bạn muốn kiểm soát phản hồi nào sẽ chuyển sang người dùng khi tham số truy vấn trong biểu mẫu Chuỗi không thể chuyển đổi thành loại T, bạn có thể ném WebApplicationException. Dropwizard đi kèm với các lớp * Param bạn có thể sử dụng cho nhu cầu của bạn.

BooleanParam, DateTimeParam, IntParam, LongParam, LocalDateParam, NonEmptyStringParam, UUIDParam. Xem https://github.com/dropwizard/dropwizard/tree/master/dropwizard-jersey/src/main/java/io/dropwizard/jersey/params

Nếu bạn cần Ngày giờ Joda, chỉ cần sử dụng Dropwizard DateTimeParam.

Nếu danh sách trên không phù hợp với nhu cầu của bạn, hãy xác định danh sách của riêng bạn bằng cách mở rộng AbstractParam. Ghi đè phương pháp phân tích cú pháp. Nếu bạn cần kiểm soát cơ thể phản hồi lỗi, phương pháp lỗi ghi đè.

tốt bài báo từ Coda Hale về vấn đề này là ở http://codahale.com/what-makes-jersey-interesting-parameter-classes/

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(); 
    } 
} 

ngày (String arg) không được chấp nhận. Tôi sẽ sử dụng các lớp ngày tháng Java 8 nếu bạn đang sử dụng Java 8. Nếu không, thời gian ngày joda được khuyến nghị.

1

Đây là hành vi chính xác thực sự. Jersey sẽ cố gắng tìm một trình xử lý cho đầu vào của bạn và sẽ cố gắng xây dựng một đối tượng từ đầu vào được cung cấp. Trong trường hợp này, nó sẽ cố tạo một đối tượng Date mới với giá trị X được cung cấp cho hàm tạo. Vì đây là ngày không hợp lệ, theo quy ước Jersey sẽ trả lại 404.

Những gì bạn có thể làm là viết lại và đặt ngày sinh làm Chuỗi, sau đó thử phân tích cú pháp và nếu bạn không có được thứ mình muốn, bạn miễn phí để ném bất kỳ ngoại lệ nào bạn muốn bởi bất kỳ cơ chế ánh xạ ngoại lệ nào (có một số).

-5
abtrack class Responce 
{ 
private String message ; 
private int code ; 

public String getMessage(){ 
return this.message ; 
} 
public void setMessage(String message){ 
this.message =message ; 
} 


public String getCode(){ 
return this.code ; 
} 
public void setCode(String code){ 
this.code =code ; 
} 

} 

@XmlRootElement(name='MyResponce') 
class MyResponce extends Responce { 

} 

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

    @GET 
    @Produces("text/html") 
    public MyResponce get(
     MyResponce myResponce = new MyResponce(); 
     @QueryParam("name") String name, 
     @QueryParam("birthDate") Date birthDate) throw WSException { 
      try { 
}catch(Exception) 
myResponce.setCode(400); 
myResponce.setMessage("Exception") 
    } 
return myResponce ; 

} 
Các vấn đề liên quan