Why is ServiceStack caching in Service, not FilterAttribute? - caching

In MVC and most other service frameworks I tried, caching is done via attribute/filter, either on the controller/action or request, and can be controlled through caching profile in config file. It seems offer more flexibility and also leave the core service code cleaner.
But ServiceStack has it inside the service. Are there any reason why it's done this way?
Can I add a CacheFilterAttribute, but delegate to service instead?
ToOptimizedResultUsingCache(base.Cache,cacheKey,()=> {
// Delegate to Request/Service being decorated?
});
I searched around but couldn't find an answer. Granted, it probably won't make much difference because the ServiceStack caching via delegate method is quite clean. And you seldom change caching strategy on the fly in real world. So this is mostly out of curiosity. Thanks.

Because the caching pattern involves, checking first to see if it is cached, if not to then execute the service, populate the cache, then return the result.
A Request Filter doesn't allow you to execute the service and a Response Filter means that the Service will always execute (i.e. mitigating the usefulness of the Cache), so the alternative would require a Request + Response filter combination where the logic would be split into 2 disjointed parts. Having it inside the Service, lets you see and reason about how it works and what exactly is going on, it also allows full access to calculate the uniqueHashKey used and exactly what and when (or even if) to Cache, which is harder to control with a generic black-box caching solution.
Although we are open to 'baking-in' built-in generic caching solutions (either via an attribute or ServiceRunner / base class). Add a feature request if you'd like to see this, specifying the preferred functionality/use-case (e.g. cache based on Time / Validity / Cache against user-defined Aggregate root / etc).

Related

Browser Cache Private S3 Resources

Stack is:
Angular
Laravel
S3
nginx
I'm using S3 to store confidential resources of my users. Bucket access is set to private which means I can access files either by creating temporary (signed, dynamic) links or by using Storage::disk('s3')->get('path/to/resource') method and returning an actual file as a response.
I'm looking for a way to cache resources in user's browser. I have tried to set cache headers to resource response directly on AWS, but since I'm creating temporary urls, they are dynamic and cache is not working in that case.
Any suggestion is highly appreciated.
EDIT: One thing that makes the whole problem even more complex is that security of resources should be intact. It means that I need a way to cache resources, but in the same time I must prevent users from copy-pasting links and using them outside of the app (sharing with others via direct links).
Temporary links in terms of security are still not an ideal solution, since they can be shared (and accessed multiple times) within the period of time they are valid for (in my case it's 30 seconds).
Caching will work as-is (based on Cache-Control, et al.) as long as the URL stays the same. So, if your application uses the same signed URL for awhile, you'll be fine.
The problem comes when you want to update an expiration date or something. This of course has different querystring parameters, and is effectively a different URL. You need a different caching key, but the browser has no concept of this by default.
If it is acceptable for your security, you can create a Service Worker which uses just the base URL (without querystring) as the cache key. Then, future requests for the same object on the bucket will be able to used the cached response, regardless of other URL parameters.
I must prevent users from copy-pasting links and using them outside of the app (sharing with others via direct links).
This part of your requirement is impossible, and unrelated to caching. Once that URL is signed, it can be used by others.
You have just add one parameter in your code.
'ResponseCacheControl' => 'no-store'
Storage::disk('s3')->getAwsTemporaryUrl(Storage::disk('s3')->getDriver()->getAdapter(), trim($mNameS3), \Carbon\Carbon::now()->addMinutes(config('app.aws_bucket_temp_url_time')), ['ResponseCacheControl' => 'no-store']);

Cache for Angular2

I am looking for a cache implementation for an Angular2 application.
For example, we have a million Movie objects stored on a server (i.e. enough that we don't want to grab them all at once). On the server, a REST endpoint is available : getMovie(String id)
Back on the client side, the cache should provide a simple way to get a movie from Angular, something like cache.getMovie(id:string): Observable<Movie>. This will hit the REST endpoint only for the first call, and store it locally for later requests.
Angular1 has angular-cache or the $cacheFactory, with LRU support and other great functionalities.
I started implementing a basic cache using a local HashMap, but that seems like a very common need.
Is there a good in-memory cache implementation for Angular2 yet?
I would use lscache and extend it providing few underlying storages: localStorage, sessionStorage, and self-implmented memoryStorage. TypeScript definitions are already available.

Does razor.servicestack support [OutputCache] (as would be used in MVC3) or something similar?

What is the suggested way to implement output caching of service responses when using http://razor.servicestack.net?
Not used it since it's 4.5 and our current projects are 4.0, but perhaps CacheStack may be of use?
Otherwise handle it yourself via wrapping existing services with the ToOptimizedResultUsingCache extension method, and manually expiring cache keys on CRUD type operations.

ProtocolViolationException Load testing web service (GET action with content-body)

I created an ASP.NET MVC4 Web API service (REST) with a single GET action. The action currently needs 11 input values, so rather than passing all of those values in the URL, I opted to encapsulate those values into a single class type and have it passed as Content-Body. When I test in Fiddler, I specify the verb as GET, and enter the JSON text in the "Request Body" input box. This works great!
The problem is when I attempt to perform Load Testing in Visual Studio 2010 Ultimate. I am able to specify the GET action and the JSON Content-Body just fine. But when I run the Load test, VS reports exceptions of type ProtocolViolationException (Cannot send a content-body with this verb-type) in the test results. The test executes in 1ms so I suspect the exceptions are causing the test to immediately abort. What can I do to avoid those exceptions? I'd prefer to not change my API to use URL arguments just to work-around the test tooling. If I should change the API for other reasons, let me know. Thanks!
I found it easier to put this answer rather than carry on the discussions.
Sending content with GET is not defined in RFC 2616 yet it has not been prohibited. So as far as the spec is concerned we are in a territory that we have to make our judgement.
GET is canonically used to get a resource. So you are retrieving this resource using this verb with the parameters you are sending. Since GET is both safe and idempotent, it is ideal for caching. Caching usually takes place based on the resource URI - and sometimes based on various headers. The point is cache implementations - AFAIK - would not use the GET content (and to be honest I have not seen any GET with content in real world). And it would not make sense to include the content in the key generation since it reduces the scalability of the caches.
If you have parameters to send, they must be in the URI since this is part of what defines that URI. As such, I strongly believe sending content with GET is wrong.
Even when you look at implementations such as OData, they put the criteria in the URI. I cannot imagine your (or any) applications requirements is beyond OData query requirements.

Subsonic, SharedDbConnectionScope and ApplicationState

I'm looking at using Subsonic with a multi-tenant ASP.net web application. There are multiple DB's (one per client/instance). The user logs in with a domain suffix to their username (e.g. user#tenant1, user#tenant2).
The custom membership provider will then determine which database a user is using, and authenticate against it. All user-initiated calls in the webapp will be wrapped in a SharedDbConnectionScope call, however I have a question regarding caching subsonic items.
Basically each instance will have a few records that rarely change (search options/configurations). I would like to read these in the Application_Start event, and cache them into the ApplicationState.
In the Application_Start event, it would loop over each client database, use a SharedDbConnectionScope to connect to each DB, and create these cached records (e.g. Application('tenant1_search_obj') = subsonic_object
When a user loads the search page, it would then check what domain a user is in, and then retreive that search option from the cache.
Is this feasible? I'm just concerned that if I cache an object, when I retrieve it from the application cache it won't know what connection its using, and might possibly pull in the wrong data.
I'd like to avoid putting this in the session object if possible.
it's possible, but probably not a good idea since it doesn't scale at all - you're going to pop a new connection for every single client whether they show up or not.
Maybe your best bet is to "lazy load" the setting - first hit on the search page loads the config into the cache or Application settings and there it stays.
Other than that - to answer your question it is possible. If you're using SubSonic 3, just create a new provider on the fly using ProviderFactory.GetProvider(connectionString, "System.Data.SqlClient") and then execute your stuff against it.
For SubSonic 2 - SharedConnectionScope is what you want.

Resources