Serving static resources from external directories - jhipster

What should be the correct way to serve images, or any other static resources like css and javascript, from a directory existing outside of the application?
I used to achieve this very easily in Spring MVC applications by using the mvc:resources element in xml configuration, or by extending WebMvcConfigurerAdapter and adding the respective resource handlers in Java configuration, then specifying the file system path where the resources were stored.
Having in mind that, as far as I can tell, jhipster doesn't use Spring MVC how could I achieve this functionality?
I tried configuring Spring MVC in jhipster but introducing the dispatcherServlet only caused the application to break (as expected, right?), and I'm still getting the hang of Angular so I'm not sure if the solution is that way.
Thanks in advance.
UPDATE:
Added my solution below.

For dev just place the file in /src/main/webapp/yourdir/ and it should be served (assuming it's a public static resource). For production, you'd need to add it to the static filter in config/WebConfigurer.java:
/**
* Initializes the static resources production Filter.
*/
private void initStaticResourcesProductionFilter(ServletContext servletContext,
EnumSet<DispatcherType> disps) {
log.debug("Registering static resources production Filter");
FilterRegistration.Dynamic staticResourcesProductionFilter =
servletContext.addFilter("staticResourcesProductionFilter",
new StaticResourcesProductionFilter());
staticResourcesProductionFilter.addMappingForUrlPatterns(disps, true, "/");
staticResourcesProductionFilter.addMappingForUrlPatterns(disps, true, "/index.html");
staticResourcesProductionFilter.addMappingForUrlPatterns(disps, true, "/images/*");
staticResourcesProductionFilter.addMappingForUrlPatterns(disps, true, "/fonts/*");
staticResourcesProductionFilter.addMappingForUrlPatterns(disps, true, "/scripts/*");
staticResourcesProductionFilter.addMappingForUrlPatterns(disps, true, "/styles/*");
staticResourcesProductionFilter.addMappingForUrlPatterns(disps, true, "/views/*");
staticResourcesProductionFilter.setAsyncSupported(true);
}

Thanks to the magic of Spring Boot, now I know you can configure MVC related stuff by extending WebMvcConfigurerAdapter. So I created my own configuration class that overrides the corresponding method, in this case addResourceHandlers and it works like a charm.
Just note that the #EnableWebMvc is NOT needed, otherwise you will mess with JHipster front end.

Related

Overriding ServiceStack Templates with Embedded Resources

Service stack documentation explains that templates for meta and operation pages may be overridden using static files.
https://github.com/ServiceStack/ServiceStack/wiki/Virtual-file-system
This works well, however, I would prefer to deploy all my resources as embedded resources.
My assumption was that by registering my EmbeddedResourceSources in the hostConfig, that they would take precedence over the existing service stack templates but that does not seem to be the case:
SetConfig(new HostConfig
{
EmbeddedResourceSources = {typeof(ApiUiResource).Assembly},
EmbeddedResourceBaseTypes = {typeof(ApiUiResource)},
});
The above works to serve all my other files, but the default pages for the meta and operations pages are still shown. Is what I am attempting to do possible?
The Config.EmbeddedResourceBaseTypes contains the order by which the embedded resource virtual files are loaded, so you'll need add yours at the start of the list to take precedence:
var config = new HostConfig
{
EmbeddedResourceSources = {typeof(ApiUiResource).Assembly},
};
config.EmbeddedResourceBaseTypes.Insert(0,typeof(ApiUiResource));
SetConfig(config);
An alternative solution is to override GetVirtualFileSources() in your AppHost to change the order which the virtual file sources are returned, see the docs for an example of this.

Angular2 and Spring Boot. How to serve front-end?

I have Spring Boot as back-end and Angular2 as front-end. I want to develop both of them separately and deploy onto Heroku.
They shouldn't have any common dependencies and should be in separate git-repos.
As I understand, there are two main ways to implement:
run npm build and copy dist folder into resource folder of Spring application so last will handle it as a static content
run server for serving exclusively Angular app which will communicate with Spring app (CORS problem appears here?) so there are two servers at sum
I think first way is a bit "dirty" since I do not think that copy folder from one project to another is any good.
And second way is overkill because I have two servers (Tomcat and Node.js, for example). Why should I have server with Angular app if I can simply put Angular inside Spring?
Is there is any more rightful way to make aforementioned?
Thanks.
In my organization, we have a lot of Spring Boot and Angular apps. When two servers are unnecessary, Spring Boot can serve up the static content from any supported URL (such as "http:" or "file:"). Simply pass this argument to your app on startup:
--spring.resources.static-locations=<url>
Spring Boot can also support Angular single-page app routing by including the following web MVC configuration. This ensures that even if the user refreshes the page in the browser, Spring Boot will still serve up the contents of index.html for other angular routes.
public class SinglePageAppWebMvcConfigurer extends WebMvcConfigurerAdapter
{
#Autowired
private ResourceProperties resourceProperties;
private String apiPath = "/api";
public SinglePageAppWebMvcConfigurer()
{
}
public SinglePageAppWebMvcConfigurer(String apiPath)
{
this.apiPath = apiPath;
}
protected String getApiPath()
{
return apiPath;
}
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry)
{
registry.addResourceHandler("/**")
.addResourceLocations(resourceProperties.getStaticLocations())
.setCachePeriod(resourceProperties.getCachePeriod()).resourceChain(true)
.addResolver(new SinglePageAppResourceResolver());
}
private class SinglePageAppResourceResolver extends PathResourceResolver
{
#Override
protected Resource getResource(String resourcePath, Resource location) throws IOException
{
Resource resource = location.createRelative(resourcePath);
if (resource.exists() && resource.isReadable()) {
return resource;
} else if (getApiPath() != null && ("/" + resourcePath).startsWith(getApiPath())) {
return null;
} else {
LoggerFactory.getLogger(getClass()).info("Routing /" + resourcePath + " to /index.html");
resource = location.createRelative("index.html");
if (resource.exists() && resource.isReadable()) {
return resource;
} else {
return null;
}
}
}
}
}
Option 1
One server process hosting REST APIs and another server process hosting Angular UI
This option is the recommended option in a MicroServices based architecture where the individual APIs (or small related group of APIs) are run and scaled separately by hosting them in separate server processes. In such scenarios, if the Angular UI is also bundled with the REST APIs, it would mean that every bug-fix or enhancement in the Angular UI would require rebuilding and redeployment of your services. And when we start doing that, it would defeat the purpose of Microservices based architecture.
Therefore, in such an architecture one or more server instances would host only the Angular UI. The Angular UI would in turn call the individual APIs through an API Gateway using some service discovery mechanism. However, Microservices based architecture is not trivial - it's complex with a lot of moving parts. Such level of complexity can be justified for large projects.
Option 2
One server process hosting both REST APIs and Angular UI
This option is the recommended option for small to medium sized projects where the user base is a few hundred users.
In such projects, create a single repository where a Maven project would contain two sub-modules - one for the REST APIs and the other for the Angular UI.
Use the Maven plugin "frontend-maven-plugin" to build the UI part. This will automatically download the specified NodeJs version and invoke appropriate npm commands to build the Angular project. Finally, using the Maven element copy the Angular dist folder to the Spring Boot static folder under the resources folder. (In the .gitignore file exclude the static folder so that the Angular distribution files are not checked into the source control along with the REST APIs).
Now, as the Java build will start, it would automatically include the static folder in the fat jar which would now serve both the APIs and the Angular UI.
So far I created applications with angular and spring-boot, using one git repository, but two different maven projects, one for the backend, one for the frontend.
With Maven than I built one fat jar with an embedded Tomcat and deployed it to Amazon EC2.
I also experimented with Heroku and you could for sure deploy the same fat jar there.
For the next project I would take another approach and deploy all static resources like html, javascript etc. to Amazon S3 and only the spring-boot app to a provider like heroku.
Frontend deployment this way seems to be much easier, faster and cheaper.
There is also a blog post about Using AWS S3 to Store Static Assets and File Uploads

Servicestack Multitenancy dynamic plugins

We are moving from an on premise-like application to a multi tenant cloud application.
for my web application we made a very simple interface based on IPlugin, to create a plugin architecture. (customers can have/install different plugins)
public interface IWebPlugin : IPlugin
{
string ContentBaseUrl { set; get; }
}
We have some plugins that would normally be loaded in on startup. Now i'm migrating the code to load at the beginning of a request (the Register function is called on request start), and scope everything inside this request.
It's not ideal but it would bring the least impact on the plugin system for now.
I could scope the Container by making an AppHost child container which would stick to the request:
Container IHasContainer.Container
{
get
{
if (HasStarted)
return ChildContainer;
return base.Container;
}
}
public Container ChildContainer
{
get { return HttpContext.Current.Items.GetOrAdd<Container>("ChildContainer", c => Container.CreateChildContainer()); }
}
problem case
Now im trying to make plugins work that actually add API services.
appHost.Routes.Add<GetTranslations>("/Localizations/translations", ApplyTo.Get);
But this service is unreachable (and not visible in metadata). How do i make it reachable?
I see you execute the following in ServiceController AfterInit. Re-executing this still wouldnt make it work.
//Copied from servicestack repo
public void AfterInit()
{
//Register any routes configured on Metadata.Routes
foreach (var restPath in appHost.RestPaths)
{
RegisterRestPath(restPath);
//Auto add Route Attributes so they're available in T.ToUrl() extension methods
restPath.RequestType
.AddAttributes(new RouteAttribute(restPath.Path, restPath.AllowedVerbs)
{
Priority = restPath.Priority,
Summary = restPath.Summary,
Notes = restPath.Notes,
});
}
//Sync the RestPaths collections
appHost.RestPaths.Clear();
appHost.RestPaths.AddRange(RestPathMap.Values.SelectMany(x => x));
appHost.Metadata.AfterInit();
}
solution directions
Is there a way i could override the route finding? like extending RestHandler.FindMatchingRestPath(httpMethod, pathInfo, out contentType);
Or could i restart the path compilation/caching? (would be enough for now that the service would be reachable tenant wide )
All configuration in ServiceStack should be contained within AppHost.Configure() and remain immutable thereafter. It's not ThreadSafe to modify ServiceStack's Static Configuration at runtime like trying to modify registered routes or Service Metadata which needs to be registered once at StartUp in AppHost.Configure().
It looks as though you'll need to re-architect your solution so all Routes are registered on Startup. If it helps Plugins can implement IPreInitPlugin and IPostInitPlugin interfaces to execute custom logic before and after Plugins are registered. They can also register a appHost.AfterInitCallbacks to register custom logic after ServiceStack's AppHost has been initialized.
Not sure if it's applicable but at runtime you can "hi-jack Requests" in ServiceStack by registering a RawHttpHandler or a PreRequestFilter, e.g:
appHost.RawHttpHandlers.Add(httpReq =>
MyShouldHandleThisRoute(httpReq.PathInfo)
? new CustomActionHandler((req, res) => {
//Handle Route
});
: null);
Simple answer seems to be, no. The framework wasn't build to be a run-time plugable system.
You will have to make this architecture yourself on top of ServiceStack.
Routing solution
To make it route to these run-time loaded services/routes it is needed to make your own implementation.
The ServiceStack.HttpHandlerFactory checks if a route exist (one that is registered on init). so here is where you will have to start extending. The method GetHandlerForPathInfo checks if it can find the (service)route and otherwise return a NotFoundHandler or StaticFileHandler.
My solution consists of the following code:
string contentType;
var restPath = RestHandler.FindMatchingRestPath(httpMethod, pathInfo, out contentType);
//Added part
if (restPath == null)
restPath = AppHost.Instance.FindPluginServiceForRoute(httpMethod, pathInfo);
//End added part
if (restPath != null)
return new RestHandler { RestPath = restPath, RequestName = restPath.RequestType.GetOperationName(), ResponseContentType = contentType };
technically speaking IAppHost.IServiceRoutes should be the one doing the routing. Probably in the future this will be extensible.
Resolving services
The second problem is resolving the services. After the route has been found and the right Message/Dto Type has been resolved. The IAppHost.ServiceController will attempt to find the right service and make it execute the message.
This class also has init functions which are called on startup to reflect all the services in servicestack. I didn't found a work around yet, but ill by working on it to make it possible in ServiceStack coming weeks.
Current version on nuget its not possible to make it work. I added some extensibility in servicestack to make it +- possible.
Ioc Solution out of the box
For ioc ServiceStack.Funq gives us a solution. Funq allows making child containers where you can register your ioc on. On resolve a child container will, if it can't resolve the interface, ask its parent to resolve it.
Container.CreateChildContainer()

ServiceStack: Change base path of all routes in self-hosted application

I have a self-hosted application with many routes set up. Rather than going through each one and changing the route to be /api/<route> where <route> is the existing route, I was wondering if I can prefix each route with /api when I start the application? I know its possible in an IIS hosted enviroment by setting it in the web.config but I am not sure if its possible in a self-hosted environment?
#EliGassert's answer is right for ServiceStack v4 self-hosted applications. This is the requirements to change the base path of all routes in a ServiceStack v3 self-hosted application.
ServiceStack v3:
In you AppHost Configure method, you need to set ServiceStackHanderFactoryPath to the desired prefix.
public override void Configure(Container container)
{
SetConfig(new EndpointHostConfig {
ServiceStackHandlerFactoryPath = "api"
});
}
When you set your listener you must also append the prefix:
appHost.Start("http://*:9000/api/");
Hope that helps.
According To this ServiceStack article you just need to set it through the config, like this:
public override void Configure(Container container)
{
SetConfig(new HostConfig { HandlerFactoryPath = "api" });
}
Combine that with this answer from Mythz and you got yourself a self-hosted app at /api/:
_apphost.Start("http://localhost:1337/api/");
Note: this seems to have worked for the self-hosted API, but then it fails to serve up its razor pages. So this isn't quite right. Still, leaving the answer for now until a full solution is found.

How to use Restrict attribute in service stack

Is there any documentation on use of [Restrict] attribute with service stack?
Not finding any documentation, I started trying to figure this out. I discovered you have to enable restrictions in AppHost.cs Configure event with
var endpointHostConfig = new EndpointHostConfig
{
EnableAccessRestrictions = true,
};
Then I added attributes to my request DTO:
[Route("Hello/World", "GET")]
[Restrict(EndpointAttributes.InternalNetworkAccess)]
This does not work...looks like that removes all 'default' restrictions and replaces it with just that one restriction? Using this instead seems to work:
[Restrict(InternalOnly = true)]
When I do a GET from the local lan it works, but from remote it does not. Interesting, the 'detailed stack error' it gives from remote is:
The following restrictions were not met: '\n -[InternalNetworkAccess, Secure, HttpHead, HttpPost, HttpPut, HttpDelete,
HttpOther, OneWay, Soap11, Soap12, Xml, Jsv, ProtoBuf, Csv, Html, Yaml, MsgPack, FormatOther, AnyEndpoint]'
Note, it does not even list HttpGet as one of the possiblities - which does work. Also mentions Secure and not InSecure...neither of which I am specifically requiring.
Can we get some clarification on exactly how this is supposed to work? What if I wanted to require SSL - how would I specify that?
What if I wanted to require SSL in production, but not staging on all services for this endpoint? (Realizing this may be a completely different way to configure).
The [Restrict] attribute feature is in the latest version of ServiceStack. Currently the only documentation for this exists in the Security wiki page.
Here are some EndpointAttributes restrictions tests that test the validation of the restriction attributes, and some different service configurations you can use.
The way it works is that it's restricted to anything that's specified, so if you want to enable SSL and leave everything else as unrestricted, you would only add:
[Restrict(EndpointAttributes.Secure)]
public class SslOnly { }
It also supports specifying multiple combinations of environments that are allowed, e.g. You can enforce HTTP internally, but HTTPS externally with:
[Restrict(EndpointAttributes.Secure | EndpointAttributes.External,
EndpointAttributes.InSecure | EndpointAttributes.InternalNetworkAccess)]
public class SslExternalAndInsecureInternal { }
Note: each environment is combined with Enum flags and delimited with a ,.
But it doesn't let you distinguish between debug and release builds, to enable this you would need to use C# conditional compilation symbols.
E.g only allow HTTP for Debug builds and HTTPS for production Release builds:
#if DEBUG
[Restrict(EndpointAttributes.InSecure)]
#else
[Restrict(EndpointAttributes.Secure)]
#endif
public class MyRequestDto { ... }

Resources