I have gotten a task that contains creating a .Net 4.8 application that contains a "HttpSelfHostServer".
I'm stuck in the quest of assigning "IServiceCollection services" to config.DependencyResolver (of type System.Web.Http.Dependencies.IDependencyResolver)
I would really like not to use autofac or other frameworks, but all guids I can find are pointing toward these frameworks. Isn't Microsoft providing a way through?
I just had to solve the same issue. This is how i did it:
First I created a new facade class to map the IServiceCollection from the host builder to the interface HttpSelfHostConfiguration supports:
using System;
using System.Collections.Generic;
using System.Web.Http.Dependencies;
using Microsoft.Extensions.DependencyInjection;
namespace IntegrationReceiver.WebApi
{
public class HttpSelfHostDependencyResolver : IDependencyResolver
{
private readonly IServiceProvider sp;
private readonly IServiceScope scope;
public HttpSelfHostDependencyResolver(IServiceProvider sp)
{
this.sp = sp;
this.scope = null;
}
public HttpSelfHostDependencyResolver(IServiceScope scope)
{
this.sp = scope.ServiceProvider;
this.scope = scope;
}
public IDependencyScope BeginScope() => new HttpSelfHostDependencyResolver(sp.CreateScope());
public void Dispose() => scope?.Dispose();
public object GetService(Type serviceType) => sp.GetService(serviceType);
public IEnumerable<object> GetServices(Type serviceType) => sp.GetServices(serviceType);
}
}
This required me to get the latest NuGet package Microsoft.Extensions.DependencyInjection.Abstractions according to an answer here: How do I see all services that a .NET IServiceProvider can provide?
I then registered my HttpSelfHostServer in the service provider with this code:
services.AddSingleton(sp => new HttpSelfHostDependencyResolver(sp));
services.AddSingleton(sp =>
{
//Starting the HttpSelfHostServer with user-level permissions requires to first run a command like
// netsh http add urlacl url=http://+:8080/ user=[DOMAINNAME]\[USERNAME]
var config = new HttpSelfHostConfiguration("http://localhost:8080");
config.Routes.MapHttpRoute("API Default", "api/{controller}/{id}", new { id = RouteParameter.Optional });
config.DependencyResolver = sp.GetRequiredService<HttpSelfHostDependencyResolver>();
return new HttpSelfHostServer(config);
});
And finally, to find my ApiController, I had to register that too in the service provider. I did that simply with:
services.AddScoped<HealthCheckController>();
For brewity, I'm just including my api controller below to illustrate how it now gets its dependencies:
using System.Net.Http;
using System.Threading.Tasks;
using System.Web.Http;
namespace IntegrationReceiver.WebApi
{
public class HealthCheckController : ApiController
{
private readonly ServiceBusRunner serviceBusRunner;
public HealthCheckController(ServiceBusRunner serviceBusRunner)
{
this.serviceBusRunner = serviceBusRunner;
}
[HttpGet]
public async Task<HttpResponseMessage> Get()
{
var response = new
{
serviceBusRunner.RunningTasks,
serviceBusRunner.MaxRunningTasks
};
return await Json(response)
.ExecuteAsync(System.Threading.CancellationToken.None);
}
}
}
This is a pretty dumb-down implementation but works for me until I can upgrade this code to net5.
I hope it helps you too!
I have a json configuration file:
{
"chat": {
"host": "http://localhost:4555"
}
}
Also I have created class :
public class ChatConf
{
public String host { get; set; }
}
In my Startup file I'm doing this:
services.AddOptions();
services.Configure<ChatConf>(Configuration.GetSection("chat"));
The question is how can I get value of host in my main Layout?
Thanks.
you need to include Microsoft.Extension.IOption in your controller to work with the IOption collection. Then you can access your class by adding it to the constructor of you controller.
here is a good explanation
My application has configurations which are loaded using parsing annotations into a file using Jackson's fasterxml annotations. For example:
public class RootConfiguration extends Configuration {
#JsonProperty
#NotEmpty
public String foo;
#JsonProperty
public BarConfiguration bar;
public class BarConfiguration extends Configuration {
#JsonProperty
public String baz;
}
}
The configuration is then injected into providers in my Module that help me bind those properties to places in the code that use them. Like so:
#Provides
#Named("config")
public RootConfiguration provideRootConfiguration(RootConfiguration configuration) {
return configuration;
}
#Provides
#Named("config.foo")
public String provideFooConfiguration(RootConfiguration configuration) {
return configuration.foo;
}
#Provides
#Named("config.bar")
public BarConfiguration provideBarConfiguration(RootConfiguration configuration) {
return configuration.bar;
}
And so on.
I'm looking for a framework to help me avoid this tedious work.
I would imagine something that looks like this:
#Configuration(value = "config", bindSubProperties = true)
public class RootConfiguration extends Configuration {
...
That would use Reflection to bind any sub fields in my class as guice Names.
I've looked into Governator's annotations for configurations but as far as I can see they need to be applied to every configuration that I want to bind, which saves me some coding, but is essentially the same (I still have to manually specify the path for each and every configuration I want to bind).
Before I roll out my own implementation for this, is there something that will give me what I need?
Note: I'm using this for a Dropwizard project so the constraint on using Jackson to map the configuration to POJOs is rather tight (unless I move the application configuration outside of the config yaml).
I don't know of any tool that would do this for you, but you could do it yourself pretty easily with something like this:
void bindConfiguration() {
for (Field field : RootConfiguration.class.getFields() {
bindConfiguration(TypeLiteral.get(field.getGenericType()), field);
}
}
<T> void bindConfiguration(TypeLiteral<T> type, Field field) {
bind(type)
.annotatedWith(Names.named("config." + field.getName()))
.toProvider(new ConfigurationProvider<T>(field))
.in(Singleton.class);
}
class ConfigurationProvider<T> implements Provider<T> {
private final Field field;
#Inject RootConfiguration configuration;
ConfigurationProvider(Field field) {
this.field = field;
}
#Override
public T get() {
return (T) field.get(configuration);
}
}
I start my web application with spring boot. It use a simple main class to start an embedded tomcat server:
#Configuration
#EnableAutoConfiguration
#ComponentScan
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
I want to configure the server in the way that he can handle angularjs html5mode that will be activated with
$locationProvider.html5Mode(true);
Relevant postings from other users shows that you need to redirect to the root. the html5 mode remove the hashbag from the url. If you refresh the page the server doesnt find the page cause he do not handle the hash. see: AngularJS - Why when changing url address $routeProvider doesn't seem to work and I get a 404 error
Use this controller to forward the URI to index.html in order to preserve AngularJS routes. Source https://spring.io/blog/2015/05/13/modularizing-the-client-angular-js-and-spring-security-part-vii
#Controller
public class ForwardController {
#RequestMapping(value = "/**/{[path:[^\\.]*}")
public String redirect() {
// Forward to home page so that route is preserved.
return "forward:/";
}
}
In this solution ForwardController forwards only paths, which are not defined in any other Controller nor RestController. It means if you already have:
#RestController
public class OffersController {
#RequestMapping(value = "api/offers")
public Page<OfferDTO> getOffers(#RequestParam("page") int page) {
return offerService.findPaginated(page, 10);
}
}
both controllers are going to work properly - #RequestMapping(value = "api/offers") is checked before #RequestMapping(value = "/**/{[path:[^\\.]*}")
I had same problem. As far as I know, in html5 mode, angularjs don't resolve hash but entered url or url added through pushState.
The problem was that PathResourceResolver map directories but not files. Because it intended to serve requested files from directory but not to rewrite urls. For app it's mean, if you refresh your browser window or type url like http://example.com/mystate, it's query "/mystate" from the server. If spring don't know url, they return 404. One of the solutions is map every possible state to index.html like here (source, btw look at webjars - it's great!). But in my case I can safely map "/**" to index.html and therefore my solution is to override PathResourceResolver#getResource:
#Configuration
#EnableConfigurationProperties({ ResourceProperties.class })
public class WebMvcConfig extends WebMvcConfigurerAdapter {
#Autowired
private ResourceProperties resourceProperties = new ResourceProperties();
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
Integer cachePeriod = resourceProperties.getCachePeriod();
registry.addResourceHandler("/static/**")
.addResourceLocations("classpath:/static/")
.setCachePeriod(cachePeriod);
registry.addResourceHandler("/**")
.addResourceLocations("classpath:/static/index.html")
.setCachePeriod(cachePeriod).resourceChain(true)
.addResolver(new PathResourceResolver() {
#Override
protected Resource getResource(String resourcePath,
Resource location) throws IOException {
return location.exists() && location.isReadable() ? location
: null;
}
});
}
}
I found a solution I can live with it.
#Controller
public class ViewController {
#RequestMapping("/")
public String index() {
return "index";
}
#RequestMapping("/app/**")
public String app() {
return "index";
}
}
The angularjs app has to be under the subdomain app. If you do not want that you could create a subdomain like app.subdomain.com that mapps to your subdomain app. With this construct you have no conflicts with webjars, statis content and so on.
A small adjustment to a previous code which works to me.
// Running with Spring Boot v1.3.0.RELEASE, Spring v4.2.3.RELEASE
#Configuration
#EnableConfigurationProperties({ ResourceProperties.class })
public class WebMvcConfig extends WebMvcConfigurerAdapter {
#Autowired
private ResourceProperties resourceProperties = new ResourceProperties();
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
Integer cachePeriod = resourceProperties.getCachePeriod();
final String[] staticLocations = resourceProperties.getStaticLocations();
final String[] indexLocations = new String[staticLocations.length];
for (int i = 0; i < staticLocations.length; i++) {
indexLocations[i] = staticLocations[i] + "index.html";
}
registry.addResourceHandler(
"/**/*.css",
"/**/*.html",
"/**/*.js",
"/**/*.json",
"/**/*.bmp",
"/**/*.jpeg",
"/**/*.jpg",
"/**/*.png",
"/**/*.ttf",
"/**/*.eot",
"/**/*.svg",
"/**/*.woff",
"/**/*.woff2"
)
.addResourceLocations(staticLocations)
.setCachePeriod(cachePeriod);
registry.addResourceHandler("/**")
.addResourceLocations(indexLocations)
.setCachePeriod(cachePeriod)
.resourceChain(true)
.addResolver(new PathResourceResolver() {
#Override
protected Resource getResource(String resourcePath,
Resource location) throws IOException {
return location.exists() && location.isReadable() ? location
: null;
}
});
}
}
You can forward all not found resources to your main page by providing custom ErrorViewResolver.
All you need to do is to add this to your #Configuration class:
#Bean
ErrorViewResolver supportPathBasedLocationStrategyWithoutHashes() {
return new ErrorViewResolver() {
#Override
public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status, Map<String, Object> model) {
return status == HttpStatus.NOT_FOUND
? new ModelAndView("index.html", Collections.<String, Object>emptyMap(), HttpStatus.OK)
: null;
}
};
}
I finally get my Angular 5 application working with spring boot with or without spring-boot-starter-tomcat as provided (embedded) or not!
/**
* Needed for html5mode (PathLocationStrategy in Angular). Every path except api/* and resources (css, html, js, woff, etc..)
* should be redirect to index.html and then should angular managed routes (which could be correct or non existing).
*/
#RestController
#RequestMapping
public class ForwardController {
#GetMapping(value = "/**/{[path:[^\\.]*}")
public ModelAndView forward() {
return new ModelAndView("/index.html");
}
}
I just encountered the similar issue where I wanted to configure Resources and at the same time I wanted to use AngularJS Html5 mode enabled.
In my case my static files were served from /public route so I used the following request mapping on my index action and it all works fine.
#RequestMapping(value = {"", "/", "/{[path:(?!public).*}/**"}, method = GET)
public String indexAction() {
return "index";
}
I had the same problem while using angular Html5Mode.
The solution that worked for me was to configure error page for 404 in web.xml assigning the path to my Index view in my case "/".
<error-page>
<error-code>404</error-code>
<location>/</location>
</error-page>
Similarly, you can try configuring error page in spring boot. for reference, you can check this link.
Spring boot and custom 404 error page
1- first you create new Controller then copy and paste simple below code
#Controller
public class viewController {
#RequestMapping(value = "/**/{[path:[^\\.]*}")
public String redirect() {
// Forward to home page so that route is preserved.
return "forward:/";
}
}
3- remove 2 below item from angular app
$locationProvider.hashPrefix('!');
$urlRouterProvider.otherwise("/");
2- in angular application you must add $locationProvider.html5Mode(true); to app route
3- Don't forget to place the base tag before any http request in your index.html file
<head>
<base href="/"> /* Or whatever your base path is */
//call every http request for style and other
...
</head>
it's work fine for me
I am trying to use ServiceStack Razor in my project. I set up a very simple DTO:
namespace ModelsWeb.Diagnostics
{
[Route("/echo")]
[Route("/echo/{Text}")]
public class Echo
{
public string Text { get; set; }
}
public class EchoResponse
{
public ResponseStatus ResponseStatus { get; set; }
public string Result { get; set; }
}
}
And a service to go with it:
namespace Rest.Services
{
public class EchoService : Service
{
public object Any(Echo request)
{
return new EchoResponse {Result = request.Text};
}
}
}
Note that the DTO and the service are in different namespaces. This is because I'm building two applications at once -- the server and the thick client -- and I put all the DTOs in a separate class library that they both depend on. This way, the client can reference just that class library, and no other server-side code. I am using Razor to provide a Web interface to some of the server functionality.
Anyway, I also wrote a simple view for my Echo service:
#using ServiceStack.Razor
#using ModelsWeb.Diagnostics
#inherits ViewPage<EchoResponse>
#{
ViewBag.Title = "Echo Response";
Layout = "BasePage";
}
<h1>You typed: #Model.Result</h1>
When I type "http://localhost:62061/echo/hello2" into the browser, I get an error on my log:
Cannot implicitly convert type 'ServiceStack.Razor.Compilation.RazorDynamicObject'
to 'ModelsWeb.Diagnostics.EchoResponse'
However, the template still works, and I see the expected result in the browser. What's going on here ? Am I doing anything wrong ? If not, how can I suppress this exception ?