Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DataResponse<T> to support "304 Not Modified" #640

Closed
andrus opened this issue Jul 8, 2023 · 0 comments
Closed

DataResponse<T> to support "304 Not Modified" #640

andrus opened this issue Jul 8, 2023 · 0 comments
Milestone

Comments

@andrus
Copy link
Contributor

andrus commented Jul 8, 2023

Let's add a mechanism to return "304 Not Modified" versions of DataResponse<T>. While it is always possible to control the response status via JAX-RS Response object, there's a benefit to preserving DataResponse<T> in the API method signature, as it allows documentation tools like Swagger to guess the response type.

So while we are not (yet) building full cache control support in Agrest, let's at least allow the users to implement conditional requests without changing the endpoint method signatures.

Changes required:

  • DataResponseBodyWriter and SimpleResponseBodyWriter must ensure an empty body if the status is 304
  • AgResponse should support setting cache control (or arbitrary) headers
  • ResponseStatusFilter should append those headers to response
  • Headers must be settable from stage callbacks (i.e. should be set at the Context level)

Here is a simplied example of a custom endpoint that recognizes ETags. Real-life example would need to provide a smarter etag calculation function, and also set a reasonable Cache-Control header.

public static class Resource {

    @Context
    private Configuration config;

    @GET
    public DataResponse<P1> conditional(@HeaderParam("If-None-Match") String eTag) {
        return AgJaxrs
                .select(P1.class, config)
                .routingStage(SelectStage.APPLY_SERVER_PARAMS, c -> stopIfMatch(c, eTag))
                .stage(SelectStage.ENCODE, this::cacheControl)
                .get();
    }

    ProcessorOutcome stopIfMatch(SelectContext<?> context, String eTag) {
        if (!"my-tag".equals(eTag)) {
            return ProcessorOutcome.CONTINUE;
        }
        
        context.setResponseStatus(HttpStatus.NOT_MODIFIED);
        // even though we are not modified, per HTTP standards, we must return the original ETag
        context.addResponseHeader("etag", eTag);
        return ProcessorOutcome.STOP;
    }

    void cacheControl(SelectContext<?> context) {
        // calculate response etag 
        context.addResponseHeader("etag", "my-tag");
    }
}
andrus added a commit that referenced this issue Jul 8, 2023
* preliminary refactoring - move response creation logic to the pipeline from the context
andrus added a commit that referenced this issue Jul 8, 2023
* preliminary refactoring - making context status code nullable,
  but response code - explicit (instead of "0" we used before for the defaults)
andrus added a commit that referenced this issue Jul 8, 2023
* preliminary refactoring - move response creation logic to the pipeline from the context
andrus added a commit that referenced this issue Jul 8, 2023
* 304 tests
* no body for 304 responses
* pass headers via Agrest callbacks
andrus added a commit that referenced this issue Jul 8, 2023
* 304 tests
* no body for 304 responses
* pass headers via Agrest callbacks
@andrus andrus added this to the 5.0.M18 milestone Jul 8, 2023
@andrus andrus closed this as completed Jul 8, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant