简体   繁体   中英

Grabbing the range values from the HttpHeader

For pagination purposes, our UI guy is specifying the items ranges in the Http header as follows:

Range: items=0-15

In subsequent requests the ranges from the web can be either

Range: items=16-31
Range: items=32-45

...etc...etc

In my controller SearchRequestController.java, I am trying to extract these ranges so that I can send it over to the Solr server to return the values in the requested chucks by setting the start and the offset.

What I am doing is as follows(In SearchRequestController.java):

@RequestMapping("/billsSearch")
@ResponseBody
public SearchResult searchBills(HttpServletRequest request,@RequestBody SearchRequest searchRequest){
    String rangeRequest = request.getHeader("Range");
    //(1)Parse the rangeRequest using some mechanism
    //(2)Set the start using the first value of the range
    //(3) Add the second value to the start to get the offset
    searchRequest.setStart(startValue);
    searchRequest.setOffset(offsetValue);
    return searchHelper(request,searchRequest);
}

There are a couple of questions that I have: Is this the best way to request data in a chunked fashion from the server?

How do I extract the ranges from the request header?

I am assuming the request.getHeader("Range") will return " items=16-31 ".

Is regular expression the best way to grab 16 and 31 from this string?

If I decide to use regular expression, wouldn't the expression break if I change the range header to be billItems=16-31?

I am a regex n00b, so it is quite possible I am thinking of this in a wrong way.

Are there alternatives to regex to parse range information like this from the http headers within SpringMVC?

Simple range header parser that conforms to http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.35

class Range {
    Integer start;
    Integer end;
    Integer suffixLength;
}

public class Main {

    public static void main(String[] args) {
        String input = "bytes=33-22,-333,20-,-55,43-0002";
        for (Range range : decodeRange(input)) {
            if (range.suffixLength == null) {
                System.out.printf("Range: %d->%s\n", range.start, range.end == null ? "enf of file" : range.end);
            } else {
                System.out.printf("Last %d bytes\n", range.suffixLength);
            }
        }
    }

    public static List<Range> decodeRange(String rangeHeader) {
        List<Range> ranges = new ArrayList<>();
        String byteRangeSetRegex = "(((?<byteRangeSpec>(?<firstBytePos>\\d+)-(?<lastBytePos>\\d+)?)|(?<suffixByteRangeSpec>-(?<suffixLength>\\d+)))(,|$))";
        String byteRangesSpecifierRegex = "bytes=(?<byteRangeSet>" + byteRangeSetRegex + "{1,})";
        Pattern byteRangeSetPattern = Pattern.compile(byteRangeSetRegex);
        Pattern byteRangesSpecifierPattern = Pattern.compile(byteRangesSpecifierRegex);
        Matcher byteRangesSpecifierMatcher = byteRangesSpecifierPattern.matcher(rangeHeader);
        if (byteRangesSpecifierMatcher.matches()) {
            String byteRangeSet = byteRangesSpecifierMatcher.group("byteRangeSet");
            Matcher byteRangeSetMatcher = byteRangeSetPattern.matcher(byteRangeSet);
            while (byteRangeSetMatcher.find()) {
                Range range = new Range();
                if (byteRangeSetMatcher.group("byteRangeSpec") != null) {
                    String start = byteRangeSetMatcher.group("firstBytePos");
                    String end = byteRangeSetMatcher.group("lastBytePos");
                    range.start = Integer.valueOf(start);
                    range.end = end == null ? null : Integer.valueOf(end);
                } else if (byteRangeSetMatcher.group("suffixByteRangeSpec") != null) {
                    range.suffixLength = Integer.valueOf(byteRangeSetMatcher.group("suffixLength"));
                } else {
                    throw new RuntimeException("Invalid range header");
                }
                ranges.add(range);
            }
        } else {
            throw new RuntimeException("Invalid range header");
        }
        return ranges;
    }
};

or take a look here

Is this the best way to request data in a chunked fashion from the server?

Another approach is to use request parameters:

/billsSearch?from=15&to=31

BTW you can use @RequestHeader annotation:

public SearchResult searchBills(
        @RequestBody SearchRequest searchRequest,
        @RequestHeader("Range") String range) {
    //...
}

How do I extract the ranges from the request header? [...] Is regular expression the best way to grab 16 and 31 from this string?

I would cut the trailing "items=" string, split on - character and parse two resulting numbers:

String[] ranges = range.substring("items=".length()).split("-");
int from = Integer.valueOf(ranges[0]);
int to = Integer.valueOf(ranges[1]);

Both @RequestParameter and @RequestHeader arguments are subject to type conversion if the target type is not string. If you had a Range type, you could create a Converter<String, Range> and register it with the conversionService. Then your controller would look a little cleaner:

    public SearchResult searchBills(
            @RequestBody SearchRequest searchRequest,
            @RequestHeader Range range) {
        //...
    }

This would useful if it occurred commonly in controllers. Or if it's just one place doing the parsing inside the controller method is good enough.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM