Question
· Jan 11, 2017

Web application with dispatch class (%CSP.REST) also serving static files

Suppose I have a web application named "/my/api", with a dispatch class configured (a subclass of %CSP.REST), and I want to be able to respond to:

GET /my/api/something/:id

by loading an object with the specified ID and returning it as JSON

GET /my/api/another-thing/data.js

by returning the contents of a file (data.js) in a configured location in the filesystem, ideally based on the "CSP files phsyical path" for /my/api. (The thought is that at some point in the future the dispatch class might change to do something more complicated server-side, and that it would be nice for such a change to simply be a matter of code rather than system configuration.)

Does anyone know of a simple, maintainable way to do this in a %CSP.REST subclass, perhaps using %CSP.StreamServer? To keep configuration simple, I'd prefer to have it managed within the dispatch class rather than configuring multiple web applications. There are many different routes of both types under /my/api/, and really the default should be to look for a static file unless some other method is configured to handle the route.

Thanks in advance!

Discussion (9)2
Log in or sign up to continue

Actually, this seems to just work (as part of a %CSP.REST subclass, used in a properly configured web application - the Management Portal UI disables the "CSP Files Physical Path" field if you enter a dispatch class, but will save it if you add the physical path first):

XData UrlMap [ XMLNamespace = "http://www.intersystems.com/urlmap" ]
{
<Routes>
<!-- ... various Routes/Forwards like in a typical REST API ... -->
<!-- Other routes: pass through to file system -->
<Route Url="/(.*)" Method="GET" Call="ServeStaticFile" />
</Routes>
}

ClassMethod ServeStaticFile(pPath As %String) As %Status
{
    #dim %request As %CSP.Request
    Do %request.Set("FILE",%request.Application_pPath)
    Quit ##class(%CSP.StreamServer).Page()
}

If there's nothing more to it than that... smiley

Did you answer your own question here? If so, DC now allows you to add an Answer to your own, and even mark it as the accepted answer. But I have a followup question: did you determine whether your solution is "legal" (i.e. a quirk of Management Portal), or is it a bug and likely to get "fixed" in a future release (thus breaking your app)?

I did, but I added it as a comment rather than an answer, so I can't mark it as the accepted answer. Regardless, I followed up this morning, and have been advised that the Management Portal's behavior is a bug and may be "fixed" in the future. The preferred solution would be additional configuration, either at the webserver level or adding more web applications.

I normally use a Web Application for serving CSP pages and static files and another for the REST calls. I configure one under the other like:

  • /csp/myapp
  • /csp/myapp/rest

And I configure both with the same Group ID, with session cookie and set the UseSession parameter on the Dispatcher class. That way, once logged in through CSP, the rest calls will just work without requiring login.

Kind regards,

Amir Samary

As an update on this topic, the approach described in earlier comments is also handy for serving a built Angular application using PathLocationStrategy (https://angular.io/api/common/PathLocationStrategy) as an alternative to webserver configuration. Our dispatch class for this purpose has:

XData UrlMap [ XMLNamespace = "http://www.intersystems.com/urlmap" ]
{
<Routes>
<Route Url="/(.*)" Method="GET" Call="ServeStaticFile" />
</Routes>
}

ClassMethod ServeStaticFile(pPath As %String) As %Status
{
    #dim %request As %CSP.Request
    If '$Match(pPath,"^(assets/.*|.*\.(js|map|html|css|woff|woff2))$") {
        Set pPath = "index.html"
    }
    Do %request.Set("FILE",%request.Application_pPath)
    Quit ##class(%CSP.StreamServer).Page()
}