How can I see the total matching requests using OData/web api while still paginating? - asp.net

When leveraging a Web Api endpoint hosted on a different server, what is the best way to have pagination to avoid excessive serialization and deserialization while still being able to see the total # of matches that were found in the Get request?
Web Api:
[Queryable]
public IQueryable<Customer> Get(string input) {
return _repo.GetCustomers(input).AsQueryable();
}
Controller Code:
HttpResponseMessage response = client.GetAsync("api/customer?input=test&$top=2&$skip=0").Result; // test string
if (response.IsSuccessStatusCode) {
CustomerSearchResultViewModel resultViewModel = new CustomerSearchResultViewModel();
resultViewModel.Customers = response.Content.ReadAsAsync<IEnumerable<Customer>>().Result.ToList();
resultViewModel.TotalResults = resultViewModel.Customers.Count;
}
In this example, the TotalResults will be capped at 2 rather than returning the total # of matches found before pagination.
Is there a way to get the total results out by leveraging OData? If that is not possible, how can I best preserve OData functionality while getting out the number I need?

You can use the OData $inlinecount option. The request would look like,
api/customer?input=test&$top=2&$skip=0&$inlinecount=allpages.
Now, we don't support sending the value of count with the default json and xml formatters out-of-the-box. Your client code is the best example why we have decided not to send it by default. The reason is that the client wouldn't know what to do with the extra count property. Our OData formatter supports $inlinecount option by default as the OData format has clear rules for where the count value fits in the response.
That said, you can do a simple change to support count in your responses. Your client code also has to change though. Refer to this answer for details.

Related

How to list all parameters available to query via API?

As a end-point user of an API, how can I list all parameters available to pass the query? In my case (stats about Age of Empires 2 matches), the website describing the API has a list with some of them but it seems there are more available.
To provide more context, I'm extracting the following information:
GET("https://aoe2.net/api/matches?game=aoe2de&count=1000&since=1632744000&map_type=12")
but for some reason the last condition, map_type=12 does nothing (output is the same as without it). I'm after the list of parameters available, so I can extract what I want.
PD: this post is closely related but does not focus on API. Perhaps this makes a difference, as the second answer there seems to suggest.
It is not possible to find out all available (undocumented) query parameters for a query, unless the API explicitly provides such a method or you can find out how the API server processes the query.
For instance, if the API server code is open source, you could find out from the code how the query is processed. Provided that you find the code also.
The answers in the post you linked are similarly valid for an API site as well as for one that provides content for a web browser (a web server can be both).
Under the hood, there is not necessarily any difference between an API server or a server that provides web content (html) in terms of how queries are handled.
As for the parameters seemingly without an effect, it seems that the API in question does not validate the query parameters, i.e., you can put arbitrary parameters in the query and the server will simply ignore parameters that it is not specifically programmed to use.
The documentation on their website is all any of us have to go by https://aoe2.net/#api
You can't just add your own parameters to the URL and expect it to return a value back as they have to have coded it to work that way.
Your best bet is to just extract as much data as you can by increasing the count parameter, then loop through the JSON response and extract the map_type from there.
JavaScript example:
<script>
json=[{"match_id":"1953364","lobby_id":null,"game_type":0},
{"match_id":"1961217","lobby_id":null,"game_type":0},
{"match_id":"1962068","lobby_id":null,"game_type":1},
{"match_id":"1962821","lobby_id":null,"game_type":0},
{"match_id":"1963814","lobby_id":null,"game_type":0},
{"match_id":"1963807","lobby_id":null,"game_type":0},
{"match_id":"1963908","lobby_id":null,"game_type":0},
{"match_id":"1963716","lobby_id":null,"game_type":0},
{"match_id":"1964491","lobby_id":null,"game_type":0},
{"match_id":"1964535","lobby_id":null,"game_type":12},];
for(var i = 0; i < json.length; i++) {
var obj = json[i];
if(obj.game_type==12){
//do something with game_type 12 json object
console.log(obj);
}
}
</script>

HTTPServletRequest getting requested URL

I am using servlets to allow clients to do CRUD Operations on a list. However I have one servlet, but it's possible to have multiple URL's get to this servlet because I have a wildcard character in the URL-Pattern.
http://localhost:8080/WebServiceDesignStyles3ProjectServer/SpyListCollection
This is the generic way to send a request to the servlet. However, for certain operations
http://localhost:8080/WebServiceDesignStyles3ProjectServer/SpyListCollection/{name}
Is a valid way to send a request to the servlet. I need to be able to get the last portion of that URL. It was told that I should be using getHeader("Accept") to be retrieving that. I've had success using getRequestURI(), but I was hoping someone could provide an example using getHeader(). Or at least an explanation describing the differences of the two.
Thank you for your time,
Kirie
You could split the request path by the separator(/) and check the last part.
String reqURI = req.getRequestURI();
String[] parts = reqURI.split("/");
if (parts[parts.length - 1].equals("SpyListCollection") {
//Generic operation
} else {
String operation = parts[parts.length - 1];
}

How should I implement a COUNT verb in my RESTful web service?

I've written a RESTful web service that supports the standard CRUD operations, and that can return a set of objects matching certain criteria (a SEARCH verb), but I'd like to add a higher-order COUNT verb, so clients can count the resources matching search criteria without having to fetch all of them.
A few options that occur to me:
Ignoring the HTTP specification and returning the object count in the response body of a HEAD request.
Duplicating the SEARCH verb's logic, but making a HEAD request instead of a GET request. The server then would encode the object count in a response header.
Defining a new HTTP method, COUNT, that returns the object count in the response body.
I'd prefer the API of the first approach, but I have to strike that option because it's non-compliant. The second approach seems most semantically correct, but the API isn't very convenient: clients will have to deal with response headers, when most of the time they want to be able to do something easy like response.count. So I'm leaning toward the third approach, but I'm concerned about the potential problems involved with defining a new HTTP method.
What would you do?
The main purpose of rest is to define a set of resources that you interact with using well defined verbs. You must thus avoid to define your own verbs. The number of resources should be considered as a different resource, with its own uri that you can simply GET.
For example:
GET resources?crit1=val1&crit2=val2
returns the list of resources and
GET resources/count?crit1=val1&crit2=val2
Another option is to use the conneg: e.g. Accept: text/uri-list returns the resources list and Accept: text/plain returns only the count
You can use HEAD without breaking the HTTP specification and you can indicate the count by using an HTTP Range header in the response:
HEAD /resource/?search=lorem
Response from the service, assuming that you return the first 20 results by default:
...
Content-Range: resources 0-20/12345
...
This way you transfer the amount of resources to the client within the header of the response message without the need to return a message body.
Update:
The solution suggested Yannick Loiseau will work fine. Just wanted to provide one other alternative approach which can be used to achieve what you need without the need to define a new resource of verb.
You can use GET and add the count into the body of the message. Then, if you API allows clients to request a range of results, you can use that in order to limit the size of message body to a minimum (since you only want the count). One way to do that would be to request an empty range (from 0 to 0), for example:
GET /resource/?search=lorem&range=0,0
The service could then respond as follows, indicating that there are 1234 matching resources in the result set:
<?xml version="1.0" encoding="UTF-8" ?>
<resources range="0-0/1234" />
Ignoring the HTTP specification and returning the object count in the response body of a HEAD request.
IMHO, this is a very bad idea. It may not work simply because you might have intermediaries that don't ignore the HTTP spec.
Defining a new HTTP method, COUNT, that returns the object count in the response body.
There is no problem with this approach. HTTP is extendable and you can define your own verbs. Some firewalls prohibit this, but they are usually also prohibit POST and DELETE and X-HTTP-Method-Override header is widely supported.
Another option, to add a query param to your url, something like: ?countOnly=true

REST uri for POST and return(GET)

Sorry for the strange title. Here is my situation.
I have a table of products with the name and display order of each product. The client can change the display order of the products. The table is generated using jQuery.tmpl and the data is pulled using GET under WCF. The products pulled from the db are by CategoryID.
When the user changes the display order of a product in the grid the product needs to be updated using POST. Once the data has been updated the server needs to send back an updated json object to update the table.
QUESTION: How do I structure my POST uri for this scenario? Here is what I have now.
[OperationContract]
[WebInvoke(
Method = "POST",
RequestFormat = WebMessageFormat.Json,
ResponseFormat = WebMessageFormat.Json,
BodyStyle = WebMessageBodyStyle.Bare,
UriTemplate = "product/form/{categoryId}")]
[return: MessageParameter(Name = "products")]
List<Product> UpdateProduct(string categoryId);
I believe my uri for updating the resource is correct as I am updating a single product by a category id. However, I want to return a new set of products based on the changes made by the POST and not have to make a separate GET call.
Not sure if this is 'right'. Those restafarians have me spooked!
Thanks.
UPDATE
I started thinking a bit more about my code above and realized there is more going on here.
The reality of my situation is that I am trying to update a specific product by ProductID and THEN return a list of products by CategoryID. Essentially a POST and a GET. So would my URI look like this?
[WebInvoke(
Method = "POST",
UriTemplate = "product/form/{productId}/products/{categoryId}")]
[return: MessageParameter(Name = "products")]
List<Product> UpdateProduct(string productId, string categoryId);
With my method like this?
public static List<Product> UpdateProduct(string productId, string categoryId)
{
ProductManager.UpdateProduct(int.Parse(productId));
return ProductManager.GetProducts(int.Parse(categoryId));
}
UPDATE2
This question has been addressed here with a link Daniel provided. Although handling everything in one POST call appears to make sense, I don't think it conforms to the spirit of REST and using Uri's as resources. Using a POST and then a GET call appears to be the answer. Thanks to Daniel. His comments are good.
A POST or PUT request to update the display order should not return back anything, except for the status code that indicates the status of the request. You should issue a separate GET request to receive the new list of top 15/25/50/etc products:
If a new resource is created, the origin server MUST inform the user agent
via the 201 (Created) response. If an existing resource is modified,
either the 200 (OK) or 204 (No Content) response codes SHOULD be sent
to indicate successful completion of the request. If the resource
could not be created or modified with the Request-URI, an appropriate
error response SHOULD be given that reflects the nature of the
problem.
From: Section 9.6 of the HTTP 1.1 Specification
You may also be interested in checking out the HTTP Spec (Sections 9.5 and 9.6) and the following Stack Overflow post:
Is it ok by REST to return content after POST?
That said, I can think of two ways to update the display order of the products:
Send a move-up or move-down request for a particular product_id. This request can be invoked whenever an up/down button is clicked.
Send the complete order_rank list for a whole category.
I believe the latter is more practical, but you may want to expose both operations. For the first approach the POST verb seems appropriate, but for the second one I would use a PUT.
For the first approach, I would imagine URIs like the following:
POST /products/{productId}/move-up
POST /products/{productId}/move-down
The above suggests that we're creating a "move-up" or a "move-down" command. The verb POST means "create" and the noun (the action) is "move-up" or "move-down" command, which acts on a particular product_id.
For the second approach, I would see a URI like the following:
PUT /categories/{categoryId}/order-rank
To which we can pass a JSON/XML/etc representation for each product_id with the order_rank number. The verb PUT means "update" and the noun is the "order-rank" for a particular category_id.

Process raw HTTP request content

I am doing an e-commerce solution in ASP.NET which uses PayPal's Website Payments Standard service. Together with that I use a service they offer (Payment Data Transfer) that sends you back order information after a user has completed a payment. The final thing I need to do is to parse the POST request from them and persist the info in it. The HTTP request's content is in this form :
SUCCESS
first_name=Jane+Doe
last_name=Smith
payment_status=Completed
payer_email=janedoesmith%40hotmail.com
payment_gross=3.99
mc_currency=USD
custom=For+the+purchase+of+the+rare+book+Green+Eggs+%26+Ham
Basically I want to parse this information and do something meaningful, like send it through e-mail or save it in DB. My question is what is the right approach to do parsing raw HTTP data in ASP.NET, not how the parsing itself is done.
Something like this placed in your onload event.
if (Request.RequestType == "POST")
{
using (StreamReader sr = new StreamReader(Request.InputStream))
{
if (sr.ReadLine() == "SUCCESS")
{
/* Do your parsing here */
}
}
}
Mind you that they might want some special sort of response to (ie; not your full webpage), so you might do something like this after you're done parsing.
Response.Clear();
Response.ContentType = "text/plain";
Response.Write("Thanks!");
Response.End();
Update: this should be done in a Generic Handler (.ashx) file in order to avoid a great deal of overhead from the page model. Check out this article for more information about .ashx files
Use an IHttpHandler and avoid the Page model overhead (which you don't need), but use Request.Form to get the values so you don't have to parse name value pairs yourself. Just pretend you're in PHP or Classic ASP (or ASP.NET MVC, for that matter). ;)
I'd strongly recommend saving each request to some file.
This way, you can always go back to the actual contents of it later. You can thank me later, when you find that hostile-endian, koi-8 encoded, [...], whatever it was that stumped your parser...
Well if the incoming data is in a standard form encoded POST format, then using the Request.Form array will give you all the data in a nice to handle manner.
If not then I can't see any way other than using Request.InputStream.
If I'm reading your question right, I think you're looking for the InputStream property on the Request object. Keep in mind that this is a firehose stream, so you can't reset it.

Resources