How implement a OData V4 services like official example services? - odata

The example services mean this:
http://services.odata.org/V4/Northwind/Northwind.svc/
My question is:
Why this service has a ".svc" suffix? As I know, now only two ways can implement odata v4 services on .Net platform, RESTier and WebAPI, see http://odata.github.io/, but both of them not have ".svc". In fact, wcf data services has ".svc", but wcfds not support odata v4.
This example service's response body is high optimization, like this:
HTTP/1.1 200 OK
Cache-Control: private
Content-Length: 2015
Content-Type: application/json;odata.metadata=minimal;odata.streaming=true;IEEE754Compatible=false;charset=utf-8
Expires: Sat, 24 Oct 2015 05:10:34 GMT
Vary: *
Server: Microsoft-IIS/8.0
X-Content-Type-Options: nosniff
OData-Version: 4.0;
X-AspNet-Version: 4.0.30319
...
{"#odata.context":"http://services.odata.org/V4/Northwind/Northwind.svc/$metadata","value":[{"name":"Categories","kind":"EntitySet","url":"......
just one line, like wcfds again, but my service like this:
HTTP/1.1 200 OK
Cache-Control: no-cache
Pragma: no-cache
Content-Type: application/json; odata.metadata=minimal; charset=utf-8
Expires: -1
Vary: Accept-Encoding
Server: Microsoft-IIS/7.5
OData-Version: 4.0
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
Date: Sat, 24 Oct 2015 06:56:24 GMT
Content-Length: 364
{
"#odata.context":"http://192.168.1.99:908/api/$metadata","value":[
{
"name":"Test","kind":"EntitySet","url":"Test"
},{
"name":"TDefStoreEmp","kind":"EntitySet","url":"TDefStoreEmp"
},{
"name":"TDefEmp","kind":"EntitySet","url":"TDefEmp"
},{
"name":"TDefStore","kind":"EntitySet","url":"TDefStore"
}
]
}
too many lines, how did one line?
So I suspect the example service is based on wcfds, but how it can support V4? in fact, i like wcfds, because it don't need any Controllers, i just want expose a database but don't want open 1433 port on the internet.
My english is not good, please understand and help me, thanks!

You are right, this demo service is implemented using WCF Data Service.
For web api based demo service, you can refer to :
http://services.odata.org/TripPinWebApiService
WCF Data Service for OData V4 is not officially supported, and it is recommended to use WebAPI instead.
This is called indent for JSON, and it was enabled by default.
To disable indent, please add the following to your webapi configuration code:
var formatters = ODataMediaTypeFormatters.Create();
foreach (var formatter in formatters)
{
formatter.MessageWriterSettings.Indent = false;
}
config.Formatters.InsertRange(0, formatters);
The source WCF Data Service is public visible here:
https://github.com/OData/odata.net/tree/WCFDSV4
Please note that the implementation indeed has some gap with OData V4 spec. But if you are interested, you can feel free to build it by yourself it or add new features.
As suggested, it's recommended to use WebAPI OData for setting up OData V4 service. Also, you could choose to use RESTier which resembles wcfds style more.

If you are looking for TripPin OData sample you can find it here:
https://github.com/OData/ODataSamples/tree/master/Scenarios/TripPin

Related

gzip compression doesn't work and can't get 304 in chrome

I'm working on a compression and caching mechanism in my asp.net mvc 5 app.
I'm sending files with the following cache headers:
Response.Cache.SetCacheability(HttpCacheability.Public);
Response.Cache.SetExpires(DateTime.UtcNow.AddYears(1).ToUniversalTime());
Response.Cache.SetLastModified(System.IO.File.GetLastWriteTime(serverPath).ToUniversalTime());
Response.AppendHeader("Vary", "Accept-Encoding");
IE11, Edge, Firefox, all sends the If-Modified-Since header on F5 refresh, but not Chrome. Why is that and how to workaround it? In Chrome I got 200 status code and file is loaded from cache.
The second problem I have is with enabling gzip compression.
I have a standard action filter for this:
public class CompressContentMvcAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
GZipEncodePage();
}
private bool IsGZipSupported()
{
string AcceptEncoding = HttpContext.Current.Request.Headers["Accept-Encoding"];
if (!string.IsNullOrEmpty(AcceptEncoding) &&
(AcceptEncoding.Contains("gzip") || AcceptEncoding.Contains("deflate")))
{
return true;
}
return false;
}
private void GZipEncodePage()
{
HttpResponse Response = HttpContext.Current.Response;
if (IsGZipSupported())
{
string AcceptEncoding = HttpContext.Current.Request.Headers["Accept-Encoding"];
if (AcceptEncoding.Contains("gzip"))
{
Response.Filter = //new GZipCompressionService().CreateCompressionStream(Response.Filter);
new System.IO.Compression.GZipStream(Response.Filter,
System.IO.Compression.CompressionMode.Compress);
Response.Headers.Remove("Content-Encoding");
Response.AppendHeader("Content-Encoding", "gzip");
}
else
{
Response.Filter =// new DeflateCompressionService().CreateCompressionStream(Response.Filter);
new System.IO.Compression.DeflateStream(Response.Filter,
System.IO.Compression.CompressionMode.Compress);
Response.Headers.Remove("Content-Encoding");
Response.AppendHeader("Content-Encoding", "deflate");
}
}
// Allow proxy servers to cache encoded and unencoded versions separately
Response.AppendHeader("Vary", "Content-Encoding");
}
}
I apply this filter on my action method returning application assets, but it got the Transfer-Encoding: chunked for each file, not gziped.
This filter is copied from my previous project and there it is still working at is expected. Could it be a problem with IIS server? Locally I have a IIS 10 and .NET 4.7, the the older app, where it works is hosted on IIS 8.5 and framework 4.5. Can't think of anything else. I'm googling the second day and can't find any clue.
I'm not interested in compressing in IIS.
[edit]
Header I got from response:
HTTP/1.1 200 OK
Cache-Control: public
Content-Type: text/javascript
Expires: Sat, 18 May 2019 08:58:48 GMT
Last-Modified: Thu, 10 May 2018 13:26:02 GMT
Vary: Content-Encoding
Server: Microsoft-IIS/10.0
X-AspNetMvc-Version: 5.2
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
Date: Fri, 18 May 2018 08:58:48 GMT
Transfer-Encoding: chunked
I always use Fiddler to inspect these kind of challenges.
The F5/If-Modified-Since issue.
Chrome just doesn't make a new request if the expires header has been set and its datetime value is still actual. So Chrome is respecting your expected caching behaviour. When navigating over your website via an other browser, you 'll see that also these don't send any requests for these assets. F5 is 'special', it's a forced refresh.
The chunked/gzip issue
Clear your browser cache and inspect the first response. Fiddler will show 'Response body is encoded', which means compressed (gzip or deflate).
Whether you see Transfer-Encoding chunked depends on whether the Content-Length header is present. See the difference in the responses below. If you don't want the chunked Transfer-Encoding set the Content-Length header.
Content-Type: text/javascript; charset=utf-8
Content-Encoding: gzip
Expires: Sat, 25 May 2019 13:14:11 GMT
Last-Modified: Fri, 25 May 2018 13:14:11 GMT
Vary: Accept-Encoding
Server: Microsoft-IIS/10.0
Content-Length: 5292
Content-Type: text/javascript; charset=utf-8
Transfer-Encoding: chunked
Content-Encoding: gzip
Expires: Sat, 25 May 2019 13:14:11 GMT
Last-Modified: Fri, 25 May 2018 13:14:11 GMT
Vary: Accept-Encoding
Server: Microsoft-IIS/10.0
Because you are handling the serving of assets via your own code, instead of via the IIS static files module, you have to deal with all response headers yourself.

Shopify not getting rates from custom CarrierService

I'm building a private Shopify app that returns custom shipping rates. Following the API docs, and with some help from this tutorial, I created a functional, proof-of-concept that returns some sample shipping rates in my store.
However, that proof-of-concept app is built in PHP, and the final version must be in Ruby on Rails. So I created a Rails app that returns exactly the same content as the PHP app—but for some reason the rates simply don't show up in the Shopify backend.
The only thing I notice that's different is the HTTP headers (I've tried monkeying around with them to match the PHP app, but to no avail). Is there anything obvious I'm missing here?
Here's a comparison of the HTTP response headers:
PHP:
Connection: Keep-Alive
Content-Encoding: gzip
Content-Length: 186
Content-Type: text/html; charset=UTF-8
Date: Tue, 16 May 2017 13:00:30 GMT
Keep-Alive: timeout=5, max=100
Server: Apache/2.4.18 (Ubuntu)
Vary: Accept-Encoding
Rails:
Cache-Control: max-age=0, private, must-revalidate
Connection: keep-alive
Content-Encoding: gzip
Content-Type: text/html; charset=utf-8
Date: Tue, 16 May 2017 12:57:38 GMT
ETag: W/"ce885edaa10636b3b7459dca958f44dd"
Server: nginx
Transfer-Encoding: chunked
Vary: Accept-Encoding
X-Request-Id: 54e2575e-c86a-4f44-a315-d0a3fbbc13f9
X-Runtime: 0.616974
Again, Shopify handles the PHP response fine, but fails silently on the Rails response. Is there anything in that second (Rails) block that Shopify might be rejecting?
Shopify unfortunately provides no error log or way to debug this type of issue—either they pull the rates from your app or not.
Here is my Rails rates_controller.rb:
class RatesController < ApplicationController
def index
ups_rates = {
rates: [
{
service_name: 'Endertech Overnight',
service_code: 'ETON',
total_price: '000',
currency: 'USD',
min_delivery_date: (DateTime.now + 1.days).strftime('%F %T %z'),
max_delivery_date: (DateTime.now + 2.days).strftime('%F %T %z')
},
{
service_name: 'Endertech Regular',
service_code: 'ETREG',
total_price: '000',
currency: 'USD',
min_delivery_date: (DateTime.now + 3.days).strftime('%F %T %z'),
max_delivery_date: (DateTime.now + 7.days).strftime('%F %T %z')
}
]
}
# Tested returning both application/json (default) or text/html
#render json: ups_rates
render body: ups_rates.to_json, content_type: "text/html"
end
end
I suspected maybe Shopify was caching very aggressively, so I've also tried destroying and recreating both my private app and the carrier service, as well as changing the callback_url. So far nothing has had any effect.
After a whole lot of digging, I found the answer: Shopify requests rates via POST, but my app was only responding to GET.
As a quick fix, I created a static route in routes.rb:
Rails.application.routes.draw do
post 'customrates', to: 'rates#index'
end
In retrospect, this was clearly stated in the documentation, I just hadn't noticed (after all, a rate request intuitively seems like a GET action).
For anyone else encountering this issue, try sending a POST via CURL to your callback_url, just to verify it's returning what it should:
curl -X POST http://yourcallbackurl.com

ASP.NET Web API and MonoDroid

I am trying to call a ASP.NET Web API that I have hosted in IIS from MonoDroid. The service is fine and I can call it from different endpoints. The problem is that in MonoDroid I get invalid cast exception when I try to do this.
var s = response.GetResponseStream();
var j = (JsonObject)JsonObject.Load(s);
System.InvalidCastException: comes back on the load part.
I have done some reading and people seem to say to try to switch the Web API to use JsonNetFormatter class. I tried that and still no luck.
Anybody have any ideas on what I can try?
UPDATE
Here is payload
<ArrayOfAlbum xmlns:xsi="w3.org/2001/XMLSchema-instance";
xmlns:xsd="w3.org/2001/XMLSchema">;
<Album>
<AlbumPK>f09d14cf-3bab-44c8-b614-2b7cf728efd4</AlbumPK>
<Name>Colorado</Name>
<UserName>firstUser</UserName>
<ParentAlbumFK xsi:nil="true" />
<DateCreated>2012-03-12T19:47:54.493</DateCreated>
</Album>
</ArrayOfAlbum>
And
[{"AlbumPK":"f09d14cf-3bab-44c8-b614-2b7cf728efd4","Name":"Colorado",
"UserName":"emorin","ParentAlbumFK":null,
"DateCr‌​eated":"2012-03-12T19:47:54.493"}]
Response from Fiddler changing the accept header to JSON.
HTTP/1.1 200 OK
Cache-Control: no-cache
Pragma: no-cache
Transfer-Encoding: chunked
Content-Type: application/json; charset=utf-8
Expires: -1
Server: Microsoft-IIS/7.0
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
Date: Tue, 17 Apr 2012 19:45:47 GMT
97
[{ "AlbumPK":"f09d14cf-3bab-44c8-614-2b7cf728efd4","Name":"Colorado",
"UserName":"emorin","ParentAlbumFK":null,
"DateCreated":"2012-03-12T19:47:54.493"}]
0
It seems that your server is sending data in the chunked encoding and perhaps the MonoDroid is having problem understanding chunked encoding. Try turning it off in IIS.

gzip compression with action filter not working

I am using a actionfilter to gzip compress my responses from actions inside controlers
This is my action filter:
Public Class CompressAttribute
Inherits ActionFilterAttribute
Implements IActionFilter
Public Overrides Sub OnActionExecuting(filterContext As ActionExecutingContext)
Dim request As HttpRequestBase = filterContext.HttpContext.Request
Dim acceptEncoding As String = request.Headers("Accept-Encoding")
If String.IsNullOrEmpty(acceptEncoding) Then
Return
End If
acceptEncoding = acceptEncoding.ToUpperInvariant()
Dim response As HttpResponseBase = filterContext.HttpContext.Response
If acceptEncoding.Contains("DEFLATE") Then
response.AppendHeader("Content-encoding", "deflate")
response.AppendHeader("Vary", "Accept-Encoding")
response.Filter = New WebCompressionFilter(response.Filter, CompressionMode.Compress)
ElseIf acceptEncoding.Contains("GZIP") Then
response.AppendHeader("Content-encoding", "gzip")
response.AppendHeader("Vary", "Accept-Encoding")
response.Filter = New WebCompressionFilter(response.Filter, CompressionMode.Compress)
End If
End Sub
End Class
I apply it to the actions inside my controlers.
The problem is that it works just fine on my desktop in development environment but on my live server (in shared hosting) it does not gzip encode.
Instead it does chunked transfer encoding....
Why?
this is my responses:
development environment:
HTTP/1.1 200 OK
Server: ASP.NET Development Server/10.0.0.0
Date: Sun, 25 Mar 2012 04:12:29 GMT
X-AspNet-Version: 4.0.30319
p3p: CP=\'NOI ADM DEV PSAi COM NAV OUR OTR STP IND DEM\'
X-AspNetMvc-Version: 3.0
Content-Encoding: gzip
Cache-Control: private
Content-Type: text/html; charset=utf-8
Content-Length: 13411
Connection: Close
live server:
HTTP/1.1 200 OK
Cache-Control: private
Content-Type: text/html; charset=utf-8
Server: Microsoft-IIS/7.5
p3p: CP=\'NOI ADM DEV PSAi COM NAV OUR OTR STP IND DEM\'
X-AspNetMvc-Version: 3.0
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
Date: Sun, 25 Mar 2012 04:13:09 GMT
Transfer-Encoding: chunked
X-OSSProxy: OSSProxy 1.3.331.322 (Build 331.322 Win32 en-us)(Oct 11 2011 17:53:42)
Connection: close
Is there any reason why you are trying to do this manually in your own code? This functionality already exists in IIS:
Make sure you have installed Dynamic Content Compression on the machine (bullet 5 in the link) and enabled it in IIS. I have never had any issues with it once these steps are completed. Once you do this there is also no need to have your attribute anymore, IIS will handle it for you implicitly on both static and dynamic content. Less code to maintain is always a good thing!

ASP.NET MVC3 IIS7.5: Cache-Control maxage is always 0 (not good for client-side caching)

I was testing my website with Fiddler and noticed that web server always returns
Cache-Control: private, s-maxage=0
for dynamic content (MVC actions). This prevents pages from being cached on client side. I wonder if it is problem of MVC or IIS. How can I fix it? I really need client-side caching to work.
Thank you!
P.S. Below is the full set of response headers:
HTTP/1.1 200 OK
Cache-Control: private, s-maxage=0
Content-Type: text/html; charset=utf-8
Content-Encoding: gzip
Vary: Accept-Encoding
Server: Microsoft-IIS/7.5
X-AspNetMvc-Version: 3.0
X-AspNet-Version: 4.0.30319
X-UA-Compatible: IE=edge,Chrome=1
Date: Sun, 20 Nov 2011 23:07:46 GMT
Content-Length: 2050
Use the OutputCacheAttribute on your controllers and/or actions to set the cache policy for that controller's actions or a particular action.
[OutputCache( Location = OutputCacheLocation.Client, Duration = 600 )]
public class HomeController : Controller
{
..
}

Resources