Making a micro-service architecture with Ocelot I started to build a test service in a separate solution. Everything is working and I can get the mock response to https://localhost:5101/service/stats/collected.
Then in another solution I'm making a fresh new webapi project. Then I follow the getting started at Ocelot's official website.
Configuring the .json file to use it as a GW I got a 500 from the project if I try to hit https://localhost:5001/api/stats/collected and I cannot figure out why ?
Here the main files for the APIGW :
ocelot.json
{
"ReRoutes": [
{
"DownstreamPathTemplate": "/service/stats/collected",
"DownstreamScheme": "https",
"DownstreamHostAndPorts": [
{
"Host": "localhost",
"Port": 5101
}
],
"UpstreamPathTemplate": "/api/stats/collected"
}
],
"GlobalConfiguration": {
"BaseUrl": "https://localhost:5001"
}
}
Program.cs
using System.IO;
using System.Net;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Ocelot.DependencyInjection;
using Ocelot.Middleware;
namespace APIGateway.Base
{
public class Program
{
public static void Main(string[] args)
{
new WebHostBuilder()
.UseKestrel(
options =>
{
options.Listen(IPAddress.Loopback, 5001, listenOptions =>
{
listenOptions.UseHttps("localhost.pfx", "qwerty123");
});
options.AddServerHeader = false;
}
)
.UseContentRoot(Directory.GetCurrentDirectory())
.ConfigureAppConfiguration((hostingContext, config) =>
{
config
.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath)
.AddJsonFile("appsettings.json", true, true)
.AddJsonFile($"appsettings.{hostingContext.HostingEnvironment.EnvironmentName}.json", true, true)
.AddJsonFile("ocelot.json")
.AddEnvironmentVariables();
})
.ConfigureServices(s => {
s.AddOcelot();
})
.ConfigureLogging((hostingContext, logging) =>
{
//add your logging
})
.UseIISIntegration()
.Configure(app =>
{
app.UseOcelot().Wait();
})
.Build()
.Run();
}
}
}
Startup.cs
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
namespace StatsService.Base
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseMvc();
}
}
}
UPDATE:
I find out that disabling the SSL on each project by commenting the options in the method UseKestrel make my GW works.
How can I setup up this to have a secure connexion between my GW and the Service ? Localhost and Prod ?
As you are using "localhost" as the hostname, I'd guess that you are using a self signed certificate for localhost. In that case you might have to add
"DangerousAcceptAnyServerCertificateValidator": true
to your ReRoutes configuration (see https://ocelot.readthedocs.io/en/latest/features/configuration.html#ssl-errors).
Other options would be:
using a valid certificate (quite hard to achieve for localhost)
register the self signed certificate in your user's certificate store as a trustworthy certificate.
I Have to changed ReRoute to Route then my routes redirect correctly.Please Use Route in your ocelot configuration file
You project is enabled for SSL, you have to disabled the SSL and remove the route with HTTPS in the launchSettings.json in all of your WEB API project. It will work successfully.
Related
I have used Ocelot with Asp.Net Core 2.1 and its working but when using with Asp.Net Core 3.1 it is not working at all. In my opinion, it is not picking up the "ocelot.json" file. Following is my Program.cs file:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
namespace ApiGateway
{
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureAppConfiguration((hostingContext, config) =>
{
config
.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{hostingContext.HostingEnvironment.EnvironmentName}.json", optional: true, reloadOnChange: true)
.AddJsonFile($"ocelot.{hostingContext.HostingEnvironment.EnvironmentName}.json")
.AddEnvironmentVariables();
})
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
}
And below is Startup.cs file :
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Ocelot.DependencyInjection;
using Ocelot.Middleware;
namespace ApiGateway
{
public class Startup
{
private IConfiguration configuration;
public Startup(IConfiguration configuration)
{
this.configuration = configuration;
}
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddOcelot(configuration);
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapGet("/", async context => {
await context.Response.WriteAsync("API GATEWAY FUNCIONANDO");
});
});
app.UseOcelot().Wait();
}
}
}
Below is ocelot.Development.json file :
{
"ReRoutes": [
{
"DownstreamPathTemplate": "/TestApi/api/test",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "localhost",
"Port": "80"
}
],
"UpstreamPathTemplate": "/getTest"
},
{
"DownstreamPathTemplate": "/TestMicroS/api/values",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "localhost",
"Port": "82"
}
],
"UpstreamPathTemplate": "/getValues"
}
],
"GlobalConfiguration": {
}
}
I have referred all above configuration from YouTube but it is not working. Local website is working but when I try to hit the UpStream path it is giving me localhost not found 404 error. Please assist me to resolve this.
Thanks in advance.
Try renaming ReRoutes to Routes in your JSON file.
I had similar issue recently,
apart from the ReRoutes to Routes , I had another problem in
In the ocelote.json file
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "localhost",
"Port": 7083
}
The port number which I had configured was for https and not for http in launchsettings.json file of the project
"applicationUrl": "https://localhost:7083",
I changed it to https to make it work
"DownstreamScheme": "https",
"DownstreamHostAndPorts": [
{
"Host": "localhost",
"Port": 7083
}
I get an error of "Ambiguity between 'Startup.Configuration' and 'Startup.Configuration'" on my startup class. I don't know what I have done to cause this issue. All I did was create a DBContext class and this error occured. Please see my code below:
Startup.cs
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.SpaServices.AngularCli;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.EntityFrameworkCore;
using Microsoft.AspNetCore.Identity;
using System;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using System.Text;
using IssueTracker.Data;
namespace IssueTracker
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
// In production, the Angular files will be served from this directory
services.AddSpaStaticFiles(configuration =>
{
configuration.RootPath = "ClientApp/dist";
});
// Enabling CORS
services.AddCors(options =>
{
options.AddPolicy("EnableCORS", builder =>
{
builder.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod().AllowCredentials().Build();
});
});
// Connect to the Database
services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(Configuration.getConnectionString("DefaultConnection")));
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseCors("EnableCORS");
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseSpaStaticFiles();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller}/{action=Index}/{id?}");
});
app.UseSpa(spa =>
{
// To learn more about options for serving an Angular SPA from ASP.NET Core,
// see https://go.microsoft.com/fwlink/?linkid=864501
spa.Options.SourcePath = "ClientApp";
if (env.IsDevelopment())
{
spa.UseAngularCliServer(npmScript: "start");
}
});
}
}
}
ApplicationDbContext
using IssueTracker.Models;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
namespace IssueTracker.Data
{
public class ApplicationDbContext : IdentityDbContext<IdentityUser>
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options) {}
// Creating the roles
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<IdentityRole>().HasData(
new { Id = "1", name="Admin", NoralizeName = "ADMIN" },
new { Id = "1", name="Customer", NoralizeName = "CUSTOMER" },
new { Id = "1", name="Moderator", NoralizeName = "MODERATOR" }
);
}
public DbSet<ProductModel> Products { get; set; }
}
}
Could you please tell me what is wrong here? Thank you.
I had the same error and simply closing and reopening my IDE (Visual Studio Code) solved it. I hope it helps
One reason for this error might be that you have accidently made a copy of Startup.cs file.
I coppied solution file for backup purposes then I got that error. When I delete the backup file problem solved.
I've got a .net Core 2.1 MVC application that hosts an Angular 6 application. I am using Visual Studio 2017 15.7.3. I'm trying to set up Windows Authentication but am having issues. I've followed the documentation and my Program.cs and Startup.cs are below:
Program.cs:
using Microsoft.AspNetCore.Server.HttpSys;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Logging;
using NLog.Web;
using System;
namespace CoreIV_Admin_Core
{
public class Program
{
public static void Main(string[] args)
{
var logger = NLog.Web.NLogBuilder.ConfigureNLog("nlog.config").GetCurrentClassLogger();
try
{
logger.Debug("init main");
CreateWebHostBuilder(args).Build().Run();
}
catch (Exception ex)
{
//NLog: catch setup errors
logger.Error(ex, "Stopped program because of exception");
throw;
}
finally
{
// Ensure to flush and stop internal timers/threads before application-exit (Avoid segmentation fault on Linux)
NLog.LogManager.Shutdown();
}
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
//.UseHttpSys(options =>
//{
// options.Authentication.Schemes = AuthenticationSchemes.NTLM | AuthenticationSchemes.Negotiate;
// options.Authentication.AllowAnonymous = false;
//})
.ConfigureLogging(logging =>
{
logging.ClearProviders();
logging.SetMinimumLevel(Microsoft.Extensions.Logging.LogLevel.Information);
})
.UseNLog(); // NLog: setup NLog for Dependency injection
}
}
Startup.cs:
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Server.HttpSys;
using Microsoft.AspNetCore.SpaServices.Webpack;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
namespace CoreIV_Admin_Core
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration
{
get;
}
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.Configure<CookiePolicyOptions>(options =>
{
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddAuthentication(HttpSysDefaults.AuthenticationScheme);
services.AddMvc()
.SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseWebpackDevMiddleware(new WebpackDevMiddlewareOptions
{
HotModuleReplacement = true
});
}
else
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseCookiePolicy();
app.UseHttpContext();
app.UseAuthentication();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
routes.MapSpaFallbackRoute(
name: "spa-fallback",
defaults: new
{
controller = "Home",
action = "Index"
});
});
}
}
}
If I uncomment the .UseHttpSys section in program.cs and press play in Visual Studio to debug, it almost immediately stops the debug session and the following error is in the Event log:
"Application 'MACHINE/WEBROOT/APPHOST/myapp' with physical root
'C:\Users\me\myapp' created process with commandline 'C:\Program Files
(x86)\Microsoft Visual
Studio\2017\Professional\Common7\IDE\Extensions\Microsoft\Web
Tools\ProjectSystem\VSIISExeLauncher.exe -argFile
"C:\Users\me\AppData\Local\Temp\tmpFCA6.tmp"' but failed to listen on
the given port '28353'".
If I try with .UseHttpSys commented out, the debugging works but I am unable to be properly authenticated.
Camilo thanks for your comment, it helped point me in the right direction. I removed the UseHttpSys section from program.cs and added .UseIISIntegration(). I also changed services.AddAuthentication(HttpSysDefaults.AuthenticationScheme) to services.AddAuthentication(IISDefaults.AuthenticationScheme) in my startup.cs. I then ticked Enable Windows Authentication in the Debug section of the project properties and chose "IIS Express" for the Launch and it all worked. I'm new to .net core with windows authentication so I don't know if I got it all set up exactly correct but it works and hopefully, this post helps point others in the right direction.
The microsoft docs example shows how to configure using PostConfigurationOptions but in this scenario if the configuration were to change the GoogleHandler IOptionsMonitor dependency will not receive the update.
AFAIK you should be able to just configure the options but I must be doing something wrong as I get the following exception:
ArgumentException: The 'ClientId' option must be provided.
Parameter name: ClientId
Microsoft.AspNetCore.Authentication.OAuth.OAuthOptions.Validate()
I have recreated the problem in a fresh ASP.NET Core 2.0 Web Application:
Startup.cs
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication.Google;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.Authorization;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
namespace TestWebApplication {
public class Startup {
public IConfiguration Configuration { get; }
public Startup(IConfiguration configuration) {
Configuration = configuration;
}
public void ConfigureServices(IServiceCollection services) {
services
.AddAuthentication(o => {
o.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
o.DefaultChallengeScheme = GoogleDefaults.AuthenticationScheme;
})
.AddCookie()
.AddGoogle()
.Services.Configure<GoogleOptions>(Configuration.GetSection("Authentication:Google"));
services.AddMvc(o => {
o.Filters.Add(new AuthorizeFilter(new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build()));
});
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env) {
app.UseDeveloperExceptionPage()
.UseStaticFiles()
.UseMvc(routes => {
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
}
}
Program.cs
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
namespace TestWebApplication {
public class Program {
public static void Main(string[] args) {
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.Build()
.Run();
}
}
}
appsettings.json
{
"Authentication": {
"Google": {
"ClientId": "xxxxxx",
"ClientSecret": "xxxxxxx"
}
}
}
If I use services.BuildServiceProvider().GetService<Microsoft.Extensions.Options.IOptionsMonitor<GoogleOptions>>().CurrentValue.ClientId the clientId is configured correctly.
Looking at the AuthenticationBuilder.cs implementation solved it.
AuthenticationSchemeOptions is a named dependency so it should be configured like so:
services
.AddAuthentication(o => {
o.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
o.DefaultChallengeScheme = GoogleDefaults.AuthenticationScheme;
})
.AddCookie()
.AddGoogle()
.Services.Configure<GoogleOptions>(GoogleDefaults.AuthenticationScheme, Configuration.GetSection("Authentication:Google"));
TL;DR What is today the correct way to setup HTTPS with ASP.NET Core 2.0?
I would like to configure my project to use https and a certificate like they have shown at BUILD 2017. I have tried several settings but nothing worked. After some research, I am even more confused. It seems that there are many ways to configure URLs and ports… I have seen appsettings.json, hosting.json, via code, and in launchsettings.json we can also set the URL and port.
Is there a "standard" way to do it?
Here is my appsettings.development.json
{
"Kestrel": {
"Endpoints": {
"Localhost": {
"Address": "127.0.0.1",
"Port": "40000"
},
"LocalhostWithHttps": {
"Address": "127.0.0.1",
"Port": "40001",
"Certificate": {
"HTTPS": {
"Source": "Store",
"StoreLocation": "LocalMachine",
"StoreName": "My",
"Subject": "CN=localhost",
"AllowInvalid": true
}
}
}
}
}
}
But it always takes the url and the port from launchsettings.json when I start from the command line with dotnet run or when I start with the debugger from Visual Studio.
This is my Program.cs and Startup.cs
public class Program
{
public static void Main(string[] args)
{
BuildWebHost(args).Run();
}
public static IWebHost BuildWebHost(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.Build();
}
public class Startup
{
public IConfiguration Configuration { get; }
public string Authority { get; set; } = "Authority";
public string ClientId { get; set; } = "ClientId";
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public void ConfigureServices(IServiceCollection services)
{
services.Configure<MvcOptions>(options => options.Filters.Add(new RequireHttpsAttribute()));
JsonConvert.DefaultSettings = () => new JsonSerializerSettings() {
NullValueHandling = NullValueHandling.Ignore
};
services.AddSingleton<IRepository, AzureSqlRepository>(x => new AzureSqlRepository(Configuration.GetConnectionString("DefaultConnection")));
services.AddSingleton<ISearchSplitService, SearchSplitService>();
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options => new JwtBearerOptions {
Authority = this.Authority,
Audience = this.ClientId
});
services.AddMvc();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseWebpackDevMiddleware(new WebpackDevMiddlewareOptions() { HotModuleReplacement = true, ReactHotModuleReplacement = true, HotModuleReplacementEndpoint = "/dist/__webpack_hmr" });
}
app.UseStaticFiles();
app.UseAuthentication();
app.UseMvc(routes => {
routes.MapRoute(
name: "default",
template: "{controller=Home}/{id?}");
routes.MapSpaFallbackRoute(
name: "spa-fallback",
defaults: new { controller = "Home", action = "Index" });
});
}
}
As I have said, I was not able to get it working in any constallation. What is today the correct way to setup HTTPS with ASP.NET Core 2.0?
Unfortunately, the way of configuration-based way of setting up HTTPS that has been shown in various videos or tutorials before the launch of ASP.NET Core 2.0 didn’t make it into the final release.
For 2.0, the only way to configure HTTPS is in code, by explicitly setting up the Kestrel listeners, as explained in this announcement, and using ListenOptions.UseHttps to enable HTTPS:
var host = new WebHostBuilder()
.UseKestrel(options =>
{
options.ListenAnyIP(443, listenOptions =>
{
listenOptions.UseHttps("server.pfx", "password");
});
})
.UseStartup<Startup>()
.Build();
Unfortunately, at the time of release, the official documentation also did not cover this properly and advertised the configuration-based way that wasn’t implemented. This has been fixed since.
Starting with ASP.NET Core 2.1, configuration based HTTPS setup will be possible, as originally promised. This will likely look like this, as explained by Tratcher on GitHub:
"Kestrel": {
"Endpoints": {
"HTTPS": {
"Url": "https://*:443",
"Certificate": {
"Path": "server.pfx",
"Password": "password"
}
}
}
}
In your particular example, the code-based configuration would look like the following. Note that if you don’t want to use a certificate file, you need to manually retrieve the certificate from the certificate store first.
.UseKestrel(options =>
{
// listen for HTTP
options.ListenLocalhost(40000);
// retrieve certificate from store
using (var store = new X509Store(StoreName.My))
{
store.Open(OpenFlags.ReadOnly);
var certs = store.Certificates.Find(X509FindType.FindBySubjectName,
"localhost", false);
if (certs.Count > 0)
{
var certificate = certs[0];
// listen for HTTPS
options.ListenLocalhost(40001, listenOptions =>
{
listenOptions.UseHttps(certificate);
});
}
}
})