preface

AOP(Aspect oriented AOP’s implementation mechanism is to dynamically generate bytecodes through annotations or XML configurations. AOP’s implementation mechanism is to dynamically generate bytecodes through annotations or XML configurations. Make the bytecode corresponding to the called code be wrapped around to inject new functionality; Or use Java’s dynamic proxy mechanism to complete the enhancement of the called method.

REST Request flow

In the following figure, there are three roles in the request process: the user, the REST client, and the REST server. The request begins with the sending of the request and ends with a call to the readeEntity() method of the Response class to retrieve the Response entity.

  • The user submits the request data, the client receives the request, and the first extension point is entered:ClientRequestFilter implementation class ClientRequestFilter“Filter () method.
  • 2. After the request filter is processed, the process enters the second extension point: the aroundWriteTo() method of “WriterInterceptor implementation class”, which intercepts the client serialization operation
  • 3、”Client message body writing processor MessageBodyWriter“Perform serialization, and the process transitions from client to server
  • 4. The server receives the request and the process goes to the third extension point :”The server prerequest filter ContainerRequestFilter implementation class“Filter () method.
  • 5. After the filtering, the server matches the resource method according to the request. If the corresponding resource method is matched, the process enters the fourth extension point :”The server backend request filter ContainerRequestFilter implementation class“Filter () method
  • After the post-request filter is processed, the process moves to the fifth extension point: the ReaderInterceptor implementation class’s aroundReadFrom() method intercepts server-side deserialization
  • 7, “server MessageBodyReader processor MessageBodyReader” to complete the deserialization of client data stream. The server performs the matching resource method
  • 8. After the REST request resource is processed, the process enters the sixth extension point:The server corresponding filter ContainerResponseFilter implementation class“Filter () method
  • 9. After the filter is processed, the process enters the seventh extension point :”The server writes the WriterInterceptor implementation class“The aroundWriteTo() method intercepts the serverside serialization to the client
  • 10、”Server MessageBodyWriter processor MessageBodyWriter“Serialization is performed and the process returns to the client side.
  • 11, the client receives the response, the process enters the eighth extension point :”*** ClientResponseFilter ClientResponseFilter implementation *** “filter() method
  • 12. After the filter is processed, the client responds to the instance response and returns to the user. The user executes the response.readEntity() process to enter the ninth extension point :”The client reads the ReaderInterceptor implementation classThe aroundReadFrom() method intercepts client deserialization
  • The “client body gambler handler MessageBodyReader” deserializes an object of Java type as the return value of the readEntity() method. At this point, the complete flow of a REST request handler is complete

If an exception or resource mismatch occurs during this period, the process is terminated from the point of error

REST filter

1, ClientRequestFilter
  • The filtering method filter() defined contains an input parameter and is the context class ClientRequestFilter for the client request. Request information can be obtained from this context. Typical examples include obtaining request context.getMethod(), the address of the request resource context.geturi (), and request header information context.getheaders (). The implementation class of the filter can use this information to override methods to implement specific filtering capabilities. The ClientRequestFilter interface implementation class is as follows:

2, ContainerRequestFilter
  • The implementation class of ContainerRequestFilter can be defined as pre-processing and post-processing for the filtering slice. By default, post-processing is used. And perform the container receive request operation first. When the server receives and processes the request. The process enters the filter() method of the filter implementation class. Preprocessing, on the other hand, performs filtering before the server processes the received request. If you want to implement a preprocessed filter implementation class, define the annotation @prematching on the class name

  • The filter method defined by the server request filter() contains one input parameter, the ContainerRequestContext class ContainerRequestContext. The ContainerRequestFilter interface implementation classes are as follows:

  • The following code shows the implementation class of the ContainerRequestFilter interface. CsrfProtectionFilter is used as an example.

public class CsrfProtectionFilter implements ClientRequestFilter {

    /**
     * Name of the header this filter will attach to the request.
     */
    public static final String HEADER_NAME = "X-Requested-By";

    private static final Set<String> METHODS_TO_IGNORE;

    static {
        HashSet<String> mti = new HashSet<String>();
        mti.add("GET");
        mti.add("OPTIONS");
        mti.add("HEAD");
        METHODS_TO_IGNORE = Collections.unmodifiableSet(mti);
    }

    private final String requestedBy;

    /**
     * Creates a new instance of the filter with X-Requested-By header value set to empty string.
     */
    public CsrfProtectionFilter() {
        this("");
    }

    /**
     * Initialized the filter with a desired value of the X-Requested-By header.
     *
     * @param requestedBy Desired value of X-Requested-By header the filter
     *                    will be adding for all potentially state changing requests.
     */
    public CsrfProtectionFilter(final String requestedBy) {
        this.requestedBy = requestedBy;
    }

    @Override
    public void filter(ClientRequestContext rc) throws IOException {
        if (!METHODS_TO_IGNORE.contains(rc.getMethod()) && !rc.getHeaders().containsKey(HEADER_NAME)) {
            rc.getHeaders().add(HEADER_NAME, requestedBy);
        }
    }
}

Copy the code

In the above code,CsrfProtectionFilter defines a special header “X-requested-by” and a set of methods that CSRF ignores to monitor. In the filter() method of the filter, the header information rc.getheaders () and request method information rc.getMethod() are retrieved from the context. Then the header information contains “X-requested-by” and the method information is determined to be a secure request method, namely “GET” and “OPT” IONS” or “HEAD”. If both conditions are not true, the filter throws a runtime BadRequestException

3, ContainerResponseFilter
  • The filter method defined by the ContainerResponseFilter interface contains two input parameters: ContainerRequestContext and ContainerRespons EContext. The ContainerResponseFilter interface implementation class is shown below:

Let’s take EncodingFilter as an example.

 @Override
    public void filter(ContainerRequestContext request, ContainerResponseContext response) throws IOException {
        if(! response.hasEntity()) {return;
        }

        // add Accept-Encoding to Vary header
        List<String> varyHeader = response.getStringHeaders().get(HttpHeaders.VARY);
        if(varyHeader == null || ! varyHeader.contains(HttpHeaders.ACCEPT_ENCODING)) { response.getHeaders().add(HttpHeaders.VARY, HttpHeaders.ACCEPT_ENCODING); } / /if Content-Encoding is already set, don't do anything if (response.getHeaders().getFirst(HttpHeaders.CONTENT_ENCODING) ! = null) { return; } // retrieve the list of accepted encodings List
      
        acceptEncoding = request.getHeaders().get(HttpHeaders.ACCEPT_ENCODING); // if empty, don'
      t do anything
        if (acceptEncoding == null || acceptEncoding.isEmpty()) {
            return;
        }

        // convert encodings from String to Encoding objects
        List<ContentEncoding> encodings = Lists.newArrayList();
        for (String input : acceptEncoding) {
            String[] tokens = input.split(",");
            for (String token : tokens) {
                try {
                    ContentEncoding encoding = ContentEncoding.fromString(token);
                    encodings.add(encoding);
                } catch (ParseException e) {
                    // ignore the encoding that could not parse
                    // but log the exception
                    Logger.getLogger(EncodingFilter.class.getName()).log(Level.WARNING, e.getLocalizedMessage(), e);
                }
            }
        }
        // sort based on quality parameter
        Collections.sort(encodings);
        // make sure IDENTITY_ENCODING is at the end (since it accepted if not explicitly forbidden
        // in the Accept-Content header by assigning q=0
        encodings.add(new ContentEncoding(IDENTITY_ENCODING, -1));

        // get a copy of supported encoding (we'll be modifying this set, hence the copy)
        SortedSet<String> acceptedEncodings = Sets.newTreeSet(getSupportedEncodings());

        // indicates that we can pick any of the encodings that remained in the acceptedEncodings set
        boolean anyRemaining = false;
        // final resulting value of the Content-Encoding header to be set
        String contentEncoding = null;

        // iterate through the accepted encodings, starting with the highest quality one
        for (ContentEncoding encoding : encodings) {
            if (encoding.q == 0) {
                // ok, we are down at 0 quality
                if ("*".equals(encoding.name)) {
                    // no other encoding is acceptable
                    break;
                }
                // all the 0 quality encodings need to be removed from the accepted ones (these are explicitly
                // forbidden by the client)
                acceptedEncodings.remove(encoding.name);
            } else {
                if ("*".equals(encoding.name)) {
                    // any remaining encoding (after filtering out q=0) will be acceptable
                    anyRemaining = true;
                } else {
                    if (acceptedEncodings.contains(encoding.name)) {
                        // found an acceptable one -> we are done
                        contentEncoding = encoding.name;
                        break;
                    }
                }
            }
        }

        if (contentEncoding == null) {
            // haven't found any explicit acceptable encoding, let's see if we can just pick any of the remaining ones // (if there are any left) if (anyRemaining && ! acceptedEncodings.isEmpty()) { contentEncoding = acceptedEncodings.first(); } else { // no acceptable encoding can be sent -> return NOT ACCEPTABLE status code back to the client throw new NotAcceptableException(); } } // finally set the header - but no need to set for identity encoding if (! IDENTITY_ENCODING.equals(contentEncoding)) { response.getHeaders().putSingle(HttpHeaders.CONTENT_ENCODING, contentEncoding); }}Copy the code

The filter() method of the EncodingFilter analyzes the request header “accept-encoding” and assigns values to the response header “Vary” and “content-encoding” to achieve Content negotiation in the Encoding part.

4, ClientResponseFilter

The filter method defined by ClientResponseFilter () contains two parameters, one is the ClientRequestContext class ClientRequestContext and the other is the ClientResponseContext class ClientResponseContext . The implementation class of the ClientResponseFilter interface is shown below:

 @Override
    public void filter(ClientRequestContext request) throws IOException {
        if ("true".equals(request.getProperty(REQUEST_PROPERTY_FILTER_REUSED))) {
            return;
        }

        if (request.getHeaders().containsKey(HttpHeaders.AUTHORIZATION)) {
            return;
        }

        Type operation = null;
        if (mode == HttpAuthenticationFeature.Mode.BASIC_PREEMPTIVE) {
            basicAuth.filterRequest(request);
            operation = Type.BASIC;
        } else if (mode == HttpAuthenticationFeature.Mode.BASIC_NON_PREEMPTIVE) {
            // do nothing
        } else if (mode == HttpAuthenticationFeature.Mode.DIGEST) {
            if(digestAuth.filterRequest(request)) { operation = Type.DIGEST; }}else if (mode == HttpAuthenticationFeature.Mode.UNIVERSAL) {

            Type lastSuccessfulMethod = uriCache.get(getCacheKey(request));
            if(lastSuccessfulMethod ! = null) { request.setProperty(REQUEST_PROPERTY_OPERATION, lastSuccessfulMethod);if (lastSuccessfulMethod == Type.BASIC) {
                    basicAuth.filterRequest(request);
                    operation = Type.BASIC;
                } else if (lastSuccessfulMethod == Type.DIGEST) {
                    if(digestAuth.filterRequest(request)) { operation = Type.DIGEST; }}}}if (operation != null) {
            request.setProperty(REQUEST_PROPERTY_OPERATION, operation);
        }
    }
Copy the code