My application is supposed to download some same images from the internal service constantly and host them. I have implemented that by implementing an actor that is scheduled to retrieve the images. It places those under /public/analytics I have this default route set:
GET /public/*file controllers.Assets.at(path="/public", file)
But for some reason when I try to access an image via http://server/public/assets/image.png , I do not see the update. I think the problem is that it is cached. When I go inside my hosting, I see the updated image located in /public/analytics so it should be caching.
I have tried to add this to Global:
override def doFilter(action: EssentialAction): EssentialAction = EssentialAction { request =>
import play.api.http.HeaderNames
action.apply(request).map(_.withHeaders(
HeaderNames.CACHE_CONTROL -> "no-cache",
HeaderNames.PRAGMA -> "no-cache"
))
}
But the result is still the same. Futhermore I see no-cache headers when I GET the image url. I saw a suggestion to generate new url each time, but I can't do that.
Any ideas what could be possibly wrong?
As soon as you use the Assets controller, you're getting caching, compression, etc support "out of the box" - but you actually don't want it!
As you've discovered, it's a very convenient way to serve up content from other sources so perhaps the easiest way to solve this problem is to leave the caching in place, but specify a very short max-age for everything.
Looking at the source of Assets, it looks like if you specify an assets.defaultCache, you can do this; i.e. in your application.conf:
assets.defaultCache="public, max-age=30"
Which means a standards-respecting browser will only retain an asset for 30 seconds before asking for a new one. You could make this match the period of your scheduled-retrieval actor for optimal efficiency.
Related
I'm trying to implement a custom IAuthorizationPolicy in Kinto. The documentation points to https://docs.pylonsproject.org/projects/pyramid/en/latest/quick_tutorial/authorization.html, which does not make me entirely understand how to add my IAuthorizationPolicy to the Kinto app.
My solution is to make it into a plugin, and implement the includeme function like this:
def includeme(config):
custom_authorization_policy = CustomAuthorizationPolicy()
config.set_authorization_policy(custom_authorization_policy)
But later, in IAuthorizationPolicy#permits, I would like to access the request that is currently being processed. This is because I want to cache the authentication tokens, and, as I understand it, the cache can be accessed from the request.
However, the IAuthorizationPolicy#permits takes the context parameter, and on it I can't find any request or cache.
The cache, if supported, can be accessed on config.registry.cache aswell, so I'm injecting it into my Auth policy:
def includeme(config):
custom_authorization_policy = CustomAuthorizationPolicy(config.registry.cache)
config.set_authorization_policy(custom_authorization_policy)
I am pretty sure that this is a config problem, so I'll post my code and the relevant application.conf options of my play app.
I have a play server that needs to interact with another server "B" (basically multi-file upload to B). The interaction happens inside an async -Action which should result in an OK with B's response on the upload. This is the reduced code:
def authenticateAndUpload( url: String) = Action.async( parse.multipartFormData) { implicit request =>
val form = authForm.bindFromRequest.get
val (user, pass) = (form.user, form.pass)
//the whole following interaction with the other server happens in a future, i.e. login returns a Future[Option[WSCookie]] which is then used
login(user, pass, url).flatMap {
case Some(cookie) => //use the cookie to upload the files and collect the result, i.e. server responses
//this may take a few minutes and happens in yet another future, which eventually produces the result
result.map(cc => Ok(s"The server under url $url responded with $cc"))
case None =>
Future.successful(Forbidden(s"Unable to log into $url, please go back and try again with other credentials."))
}
}
I am pretty sure that the code itself works since I can see my server log which nicely prints B's responses every few seconds and proceeds until everything is correctly uploaded. The only problem is that the browser hangs up with a server overloaded message after 120s which should be a play default value - but for which config parameter?
I tried to get rid of it by setting every play.server.http. timeout option I could get my hands on and even decided to use play.ws, specific akka, and other options of which I am quite sure that they are not necessary... however the problem remains, here is my current application.config part:
ws.timeout.idle="3600s"
ws.timeout.request ="3600s"
ws.timeout.response="3600s"
play.ws.timeout.idle="3600s"
play.ws.timeout.request="3600s"
play.ws.timeout.response="3600s"
play.server.http.connectionTimeout="3600s"
play.server.http.idleTimeout="3600s"
play.server.http.requestTimeout="3600s"
play.server.http.responseTimeout="3600s"
play.server.http.keepAlive="true"
akka.http.host-connection-pool.idle-timeout="3600s"
akka.http.host-connection-pool.client.idle-timeout= "3600s"
The browser hang up happened both on Safari and Chrome, where Chrome additionally started a second communication with B after about 120 seconds - also both of these communications succeeded and produced the expected logs, only the browsers had both hang up.
I am using Scala 2.12.2 with play 2.6.2 in an SBT environment, the server is under development, pre-compiled but then started via run - I read that it may not pick up the application.conf options - but it did on some file size customizing. Can someone tell me the correct config options or my mistake on the run process?
Our API's entry point has a rel named "x:reports" (where x is a prefix defined in the HAL representation, by way of a curie - but that's not important right now).
There are several types of reports. Following "x:report" provides a set of these affordances, each with a rel of its own - one rel is named "x:proofofplay". There is a set of lookup values associated with this type of report (and only this type of report). The representation returned by following "x:proofofplay" has a rel to this set of values "x:artwork".
This results in the following hierarchy
reports
proofofplay
artwork
While the "x:artwork" resource is fairly small, it does take some time to fetch it (10 sec). So the client has opted to async load it at app launch.
In order to get the "x:artwork"'s href the client has to follow the links. I'm not sure whether this is a problem. It seems potentially unRESTful, as the client is depending on out-of-band knowledge of the path to this resource. If ever path to artwork changes (highly unlikely) the client will break (though the hrefs themselves can change with impunity).
To see why I'm concerned, the launch function looks like this:
launch: function () {
var me = this;
Rest.getLinksFromEntryPoint(function(links) {
Rest.getLinksFromHref(links["x:reports"].href, function(reportLinks){
Rest.getLinksFromHref(reportLinks["x:proofofplay"].href, function(popLinks){
me.loadArtworks(popLinks["x:artwork"].href);
});
});
});
}
This hard-coding of the path simultaneously makes me think "that's fine - it's based on a published resource model" and "I bet Roy Fielding will be mad at me".
Is this fine, or is there a better way for a client to safely navigate such a hierarchy?
The HAL answer to this is to embed the resources.
Depending a bit on your server-side technology, this should be good enough in your case because you need all the data to be there before the start of the application, and since you worry about doing this sequentially, you might parallelize this on the server.
Your HAL client should ideally treat things in _links and things in _embedded as the same type of thing, with the exception that in the second case, you are also per-populating the HTTP cache for the resources.
Our js-based client does something like this:
var client = new Client(bookMarkUrl);
var resource = await client
.follow('x:reports')
.follow('x:proofofplay')
.follow('x:artwork')
.get();
If any of these intermediate links are specified in _links, we'll follow the links and do GET requests on demand, but if any appeared in _embedded, the request is skipped and the local cache is used. This has the benefit that in the future we can add new things from _links to _embedded, and speeding up clients who don't have to be aware of this change. It's all seamless.
In the future we intend to switch from HAL's _embedded to use HTTP2 Push instead.
I'm trying to build a REST service in a Sitecore root. My application start looks like this:
void Application_Start(object sender, EventArgs e)
{
RouteTable.Routes.MapHttpRoute(
name: "DefaultApi", routeTemplate: "api/{controller}/{id}", defaults: new { id = System.Web.Http.RouteParameter.Optional });
}
And my URL looks like this:
http://{mydomain}/api/books
I have the correct controller and all that.
But Sitecore keeps redirecting me to the 404 page. I've added the path to the IgnoreUrlPrefixes node in the web.config, but to no avail. If I had to guess, I'd think that Sitecore's handler is redirecting before my code gets the chance to execute, but I really don't know.
Does anybody have any idea what might be wrong?
Your assessment is correct. You need a processor in the httpRequestBegin pipeline to abort Sitecore's processing. See the SystemWebRoutingResolver in this answer:
Sitecore and ASP.net MVC
It's also described in this article:
http://www.sitecore.net/Community/Technical-Blogs/John-West-Sitecore-Blog/Posts/2010/10/Sitecore-MVC-Crash-Course.aspx
But I'll include the code here as well. :)
public class SystemWebRoutingResolver : Sitecore.Pipelines.HttpRequest.HttpRequestProcessor
{
public override void Process(Sitecore.Pipelines.HttpRequest.HttpRequestArgs args)
{
RouteData routeData = RouteTable.Routes.GetRouteData(new HttpContextWrapper(args.Context));
if (routeData != null)
{
args.AbortPipeline();
}
}
}
Then in your httpRequestBegin configuration:
<processor type="My.SystemWebRoutingResolver, My.Classes" />
You might want to have a look at Sitecore Web Api
It's pretty much the same you are building.
Another option, which I've used to good effect, is to use the content tree, the "star" item, and a sublayout/layout combination dedicated to this purpose:
[siteroot]/API/*/*/*/*/*/*/*/*/*
The above path allows you to have anywhere between 1 and 9 segments - if you need more than that, you probably need to rethink your process, IMO. This also retains all of the Sitecore context. Sitecore, when unable to find an item in a folder, attempts to look for the catch-all star item and if present, it renders that item instead of returning a 404.
There are a few ways to go about doing the restful methods and the sublayout (or sublayouts if you want to segregate them by depth to simplify parsing).
You can choose to follow the general "standard" and use GET, PUT, and POST calls to interact with these items, but then you can't use Sitecore Caching without custom backend caching code). Alternately, you can split your API into three different trees:
[siteroot]/API/GET/*/*/*/*/*/*/*/*/*
[siteroot]/API/PUT/*/*/*/*/*/*/*/*/*
[siteroot]/API/POST/*/*/*/*/*/*/*/*/*
This allows caching the GET requests (since GET requests should only retrieve data, not update it). Be sure to use the proper caching scheme, essentially this should cache based on every permutation of the data, user, etc., if you intend to use this in any of those contexts.
If you are going to create multiple sublayouts, I recommend creating a base class that handles general methods for GET, PUT, and POST, and then use those classes as the base for your sublayouts.
In your sublayouts, you simply get the Request object, get the path (and query if you're using queries), split it, and perform your switch case logic just as you would with standard routing. For PUT, use Response.ReadBinary(). For POST use the Request.Form object to get all of the form elements and iterate through them to process the information provided (it may be easiest to put all of your form data into a single JSON object, encapsulated as a string (so .NET sees it as a string and therefore one single property) and then you only have one element in the post to deserialize depending on the POST path the user specified.
Complicated? Yes. Works? Yes. Recommended? Well... if you're in a shared environment (multiple sites) and you don't want this processing happening for EVERY site in the pipeline processor, then this solution works. If you have access to using MVC with Sitecore or have no issues altering the pipeline processor, then that is likely more efficient.
One benefit to the content based method is that the context lifecycle is exactly the same as a standard Sitecore page (logins, etc.), so you've got all the same controls as any other item would provide at that point in the lifecycle. The negative to this is that you have to deal with the entire page lifecycle load before it gets to your code... the pipeline processor can skip a lot of Sitecore's process and just get the data you need directly, making it faster.
you need to have a Pipeline initializer for Routing:
It will be like :
public class Initializer
{
public void Process(PipelineArgs args)
{
RouteCollection route = RouteTable.Routes;
route.MapHttpRoute("DefaultApi", "api/{controller}/{action}/{id}",
new { id = RouteParameter.Optional });
}
}
On config file you will have :
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
<sitecore>
<pipelines>
<initialize>
<processor type="_YourNameSpace.Initializer,_YourAssembly" />
</initialize>
</pipelines>
</sitecore>
</configuration>
Happy coding
I am trying to use HTTP to POST a file to an outside API from within a grails service. I've installed the rest plugin and I'm using code like the following:
def theFile = new File("/tmp/blah.txt")
def postBody = [myFile: theFile, foo:'bar']
withHttp(uri: "http://picard:8080/breeze/project/acceptFile") {
def html = post(body: postBody, requestContentType: URLENC)
}
The post works, however, the 'myFile' param appears to be a string rather than an actual file. I have not had any success trying to google for things like "how to post a file in grails" since most of the results end up dealing with handling an uploaded file from a form.
I think I'm using the right requestContentType, but I might have missed something in the documentation.
POSTing a file is not as simple as what you have included in your question (sadly). Also, it depends on what the API you are calling is expecting, e.g. some API expect files as base64 encoded text, while others accept them as mime-multipart.
Since you are using the rest plugin, as far as I can recall it uses the Apache HttpClient, I think this link should provide enough info to get you started (assuming you are dealing with mime-multipart). It shouldn't be too hard to change it around to work with your API and perhaps make it a bit 'groovy-ier'