Можно ли установить ETags с помощью JAX-RS, не прибегая к объектам Response?
В один из немногих вопросов (с ответами). Я нашел в SO относительно JAX-RS и кеширования, ответ на создание ETags (для кэширования) установка некоторых значений объекта Response. Как в следующем:
@GET
@Path("/person/{id}")
public Response getPerson(@PathParam("id") String name, @Context Request request){
Person person = _dao.getPerson(name);
if (person == null) {
return Response.noContent().build();
}
EntityTag eTag = new EntityTag(person.getUUID() + "-" + person.getVersion());
CacheControl cc = new CacheControl();
cc.setMaxAge(600);
ResponseBuilder builder = request.evaluatePreconditions(person.getUpdated(), eTag);
if (builder == null) {
builder = Response.ok(person);
}
return builder.cacheControl(cc).lastModified(person.getUpdated()).build();
}
Проблема в том, что мы не будем работать, поскольку мы используем те же методы для SOAP и REST-сервисов, аннотируя методы с помощью @WebMethod (SOAP), @GET (и все, что нам может понадобиться, чтобы разоблачить сервис). Предыдущая служба будет выглядеть так (без создания заголовков):
@WebMethod
@GET
@Path("/person/{id}")
public Person getPerson(@WebParam(name="id") @PathParam("id") String name){
return _dao.getPerson(name);
}
Есть ли какой-либо способ - через некоторую дополнительную конфигурацию - установить эти заголовки? Это первый раз, когда я обнаружил, что использование объектов Response на самом деле имеет определенную выгоду от просто автоматического преобразования...
Мы используем Apache CXF.
Ответы
Ответ 1
Да, вы могли бы использовать перехватчики для достижения этого, если бы вы могли генерировать E-тег ПОСЛЕ создания вашего объекта ответа.
public class MyInterceptor extends AbstractPhaseInterceptor<Message> {
public MyInterceptor () {
super(Phase.MARSHAL);
}
public final void handleMessage(Message message) {
MultivaluedMap<String, Object> headers = (MetadataMap<String, Object>) message.get(Message.PROTOCOL_HEADERS);
if (headers == null) {
headers = new MetadataMap<String, Object>();
}
//generate E-tag here
String etag = getEtag();
//
String cc = 600;
headers.add("E-Tag", etag);
headers.add("Cache-Control", cc);
message.put(Message.PROTOCOL_HEADERS, headers);
}
}
Если этот способ не является жизнеспособным, я бы использовал оригинальное решение, которое вы разместили, и просто добавьте свой объект Person в конструктор:
Person p = _dao.getPerson(name);
return builder.entity(p).cacheControl(cc).lastModified(person.getUpdated()).build();
Ответ 2
или это может быть так же просто, как отправить код "ошибки"... в зависимости от того, что вы хотите сделать.
@Path("/{id}")
@GET
@Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
public ProductSearchResultBean getProductById(@PathParam("id") Integer productId, @QueryParam("expand") List<String> expand, @Context HttpServletRequest request, @Context HttpServletResponse response) throws IOException {
ProductSearchResultBean productDetail = loadProductDetail(productId, expand);
EntityTag etag = new EntityTag(((Integer)(productDetail.toString().hashCode())).toString());
String otherEtag = request.getHeader("ETag");
if(etag.getValue().equals(otherEtag)){
response.sendError(304, "not Modified");
}
response.addHeader("ETag", etag.getValue());
return productDetail;
}
То, как я все-таки занимался этим вопросом. Удачи! (Используйте Spring MVC вместо этого... есть фильтр, который делает ВСЕ для вас... даже делает хороший ETag:))
Ответ 3
Вы можете использовать для этого фильтр ответов. Я разработал библиотеку запахов, которая делает именно то, что вы ищете: https://github.com/tobilarscheid/jaxrs-etag-filter