How to define URL of Ocelot API Gateway - docker

I have three ASP.NET Core WebAPI services Customer, Subscribe, Unscubscribe with swashbuckle and docker compose project
All is working well
I have added Ocelot API Gateway (ASP.NET core Project) with Ocelot installed.
Access customer service via own address https:///api/Customer works great. But from gateway I don't know which url should I use for example this customer service
I have tried many variants like:
http:///api/
http:///api/a/customer
http:///a/api/customer
but all of them returns 404.
May be problem with that Gateway is http not https?
Program.cs
public static void Main(string[] args)
{
BuildWebHost(args).Run();
}
public static IWebHost BuildWebHost(string[] args)
{
var builder = WebHost.CreateDefaultBuilder(args);
builder.ConfigureServices(s => s.AddSingleton(builder))
.ConfigureAppConfiguration(
ic => ic.AddJsonFile(Path.Combine("configuration",
"configuration.json")))
.UseStartup<Startup>();
var host = builder.Build();
return host;
}
Startup.cs
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);
services.AddOcelot(Configuration);
}
// 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();
}
Configurations:
configuration.json:
{
"ReRoutes": [
{
"DownstreamPathTemplate": "/",
"DownstreamScheme": "http",
"DownstreamPort": 80,
"DownstreamHost": "customer.api",
"UpstreamPathTemplate": "/a/",
"UpstreamHttpMethod": [ "Get", "Post", "Put", "Delete", "Options" ]
},
{
"DownstreamPathTemplate": "/{everything}",
"DownstreamScheme": "http",
"DownstreamPort": 80,
"DownstreamHost": "customer.api",
"UpstreamPathTemplate": "/a/{everything}",
"UpstreamHttpMethod": [ "Get", "Post", "Put", "Delete", "Options" ]
},
{
"DownstreamPathTemplate": "/",
"DownstreamScheme": "http",
"DownstreamPort": 80,
"DownstreamHost": "subscribe.api",
"UpstreamPathTemplate": "/b/",
"UpstreamHttpMethod": [ "Get", "Post", "Put", "Delete", "Options" ]
},
{
"DownstreamPathTemplate": "/{everything}",
"DownstreamScheme": "http",
"DownstreamPort": 80,
"DownstreamHost": "subscribe.api",
"UpstreamPathTemplate": "/b/{everything}",
"UpstreamHttpMethod": [ "Get", "Post", "Put", "Delete", "Options" ]
},
{
"DownstreamPathTemplate": "/",
"DownstreamScheme": "http",
"DownstreamPort": 80,
"DownstreamHost": "unsubscribe.api",
"UpstreamPathTemplate": "/c/",
"UpstreamHttpMethod": [ "Get", "Post", "Put", "Delete", "Options" ]
},
{
"DownstreamPathTemplate": "/{everything}",
"DownstreamScheme": "http",
"DownstreamPort": 80,
"DownstreamHost": "unsubscribe.api",
"UpstreamPathTemplate": "/c/{everything}",
"UpstreamHttpMethod": [ "Get", "Post", "Put", "Delete", "Options" ]
}
],
"GlobalConfiguration": {}
}
docker-compose.yml:
services:
customer.api:
image: ${DOCKER_REGISTRY}customer.api
build:
context: .
dockerfile: Customer.API\Dockerfile
subscribe.api:
image: ${DOCKER_REGISTRY}subscribe.api
build:
context: .
dockerfile: NewsSubscibe.API\Dockerfile
unsubscribe.api:
image: ${DOCKER_REGISTRY}unsubscribe.api
build:
context: .
dockerfile: NewsUnSubscribe.API\Dockerfile
gateway:
image: gateway
build:
context: ./OcelotAPIGateway
dockerfile: Dockerfile
depends_on:
- customer.api
- subscribe.api
- unsubscribe.api

You need to add UseOcelot().Wait(); in Configure method of Startup:
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseMvc();
app.UseOcelot().Wait();
}

Related

I get 404 error after configuring AspNetCoreRateLimit library in my code

When I remove the rate related codes, the app works fine. If I write these codes, I get 404 error.
If await webHost.RunAsync(); , await IpPolicy.SeedAsync(); If I remove the await keyword in the , I get the error localhost refused to connect. How can I solve this problem?
Program.cs
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddDbContext(builder);
builder.Services.AddControllersWithViews();
builder.Services.AddValidatorsFromAssemblyContaining<Program>();
builder.Services.AddFluentValidationAutoValidation().AddFluentValidationClientsideAdapters();
builder.Services.AddMapster();
builder.Services.AddIdentity();
builder.Services.AddCookieConfiguration();
builder.Services.AddOptions();
builder.Services.AddMemoryCache();
builder.Services.Configure<IpRateLimitOptions>(builder.Configuration.GetSection("IpRateLimiting"));
builder.Services.Configure<IpRateLimitPolicies>(builder.Configuration.GetSection("IpRateLimitPolicies"));
builder.Services.AddInMemoryRateLimiting();
builder.Services.AddSingleton<IIpPolicyStore, MemoryCacheIpPolicyStore>();
builder.Services.AddSingleton<IRateLimitCounterStore, MemoryCacheRateLimitCounterStore>();
builder.Services.AddHttpContextAccessor();
builder.Services.AddSingleton<IRateLimitConfiguration, RateLimitConfiguration>();
using IHost webHost = builder.Build();
using (var scope = webHost.Services.CreateScope())
{
IIpPolicyStore IpPolicy = scope.ServiceProvider.GetRequiredService<IIpPolicyStore>();
await IpPolicy.SeedAsync();
}
await webHost.RunAsync();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Home/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.UseIpRateLimiting();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
RoleSeed.Seed(app);
app.Run();
appsettings.json
{
"ConnectionStrings": { "SqlCon": "" },
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"IpRateLimiting": {
"EnableEndpointRateLimiting": true,
"StackBlockedRequests": false,
"HttpStatusCode": 429,
//"IpWhitelist": [ "127.0.0.12" ],
//"EndpointWhitelist": [ "*:/api/customers" ],
"GeneralRules": [
{
"Endpoint": "*",
"Period": "10 s",
"Limit": 20
}
]
},
"IpRateLimitPolicies": {
"IpRules": [
{
"Ip": "*",
"Rules": [
{
"Endpoint": "*",
"Period": "10m",
"Limit": 150
}
]
}
]
}
}
i changed the service order and then i got this error how can i solve it?

Running in docker receive "The 'ClientId' option must be provided"

ASP.NET with Microsoft-identity-web. If run webapp locally using dotnet app.dll everything is ok, but if package into an image and run in docker or kubernetes, each request will occur error:
fail: Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware[1]
An unhandled exception has occurred while executing the request.
System.ArgumentNullException: IDW10106: The 'ClientId' option must be provided.
at Microsoft.Identity.Web.MergedOptionsValidation.Validate(MergedOptions options)
at Microsoft.Identity.Web.MicrosoftIdentityWebAppAuthenticationBuilderExtensions.<>c__DisplayClass5_0.<AddMicrosoftIdentityWebAppInternal>b__3(OpenIdConnectOptions options, IServiceProvider serviceProvider, IOptionsMonitor'1 mergedOptionsMonitor, IOptionsMonitor'1 msIdOptionsMonitor, IOptions'1 msIdOptions)
at Microsoft.Extensions.Options.ConfigureNamedOptions'5.Configure(String name, TOptions options)
at Microsoft.Extensions.Options.OptionsFactory'1.Create(String name)
at Microsoft.Extensions.Options.OptionsMonitor'1.<>c__DisplayClass10_0.<Get>b__0()
at System.Lazy'1.ViaFactory(LazyThreadSafetyMode mode)
--- End of stack trace from previous location ---
at System.Lazy'1.CreateValue()
at Microsoft.AspNetCore.Authentication.AuthenticationHandler'1.InitializeAsync(AuthenticationScheme scheme, HttpContext context)
at Microsoft.AspNetCore.Authentication.AuthenticationHandlerProvider.GetHandlerAsync(HttpContext context, String authenticationScheme)
at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware.<Invoke>g__Awaited|6_0(ExceptionHandlerMiddleware middleware, HttpContext context, Task task)
appsettings.json:
{
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"Domain": "xxx",
"TenantId": "xxx",
"ClientId": "xxx",
"CallbackPath": "/signin-oidc",
"ClientSecret": "xxx",
"ClientCertificates": []
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"MicrosoftGraph": {
"BaseUrl": "https://graph.microsoft.com/v1.0",
"Scopes": "user.read"
}
}
Dockerfile:
FROM mcr.microsoft.com/dotnet/aspnet:6.0
COPY ./apprelease /home/app

Vercel and Nextjs: redirect subdomain to path dynamically

I have a nextjs project with a :client param which represents a client, like this:
domain.com/:client
And I have multiple clients... so I need to do this rewrite:
:client.domain.com -> domain.com/:client
For example for clients:
google.domain.com -> domain.com/google
netflix.domain.com -> domain.com/netflix
...
Inside the same project.
Any way to do that?
You can use the redirects option in the vercel.json, as Maxime mentioned.
However, it requires 1 extra key.
For example, if your app is available at company.com:
{
...
redirects: [
{
"source": "/",
"has": [
{
"type": "host",
"value": "app.company.com"
}
],
"destination": "/app"
}
]
}
More info:
Example Guide
vercel.json docs
Create a config in your project root with next.config.js
If this file exists add the following snippet to it, Mind you, we used example.com in place of domain .com as Body cannot contain "http://domain. com" in stackoverflow
// next.config.js
module.exports = {
async redirects() {
return [
{
source: "/:path*",
has: [
{
type: "host",
value: "client.example.com",
},
],
destination: "http://example.com/client/:path*",
permanent: false,
},
];
},
};
To confirm it's also working in development, try with localhost
module.exports = {
reactStrictMode: true,
// async rewrites() {
async redirects() {
return [
{
source: "/:path*",
has: [
{
type: "host",
value: "client.localhost",
},
],
destination: "http://localhost:3000/client/:path*",
permanent: false,
},
];
},
};
Dynamic Redirect
To make it dynamic, we'll create an array of subdomains
const subdomains = ["google", "netflix"];
module.exports = {
async redirects() {
return [
...subdomains.map((subdomain) => ({
source: "/:path*",
has: [
{
type: "host",
value: `${subdomain}.example.com`,
},
],
destination: `https://example.com/${subdomain}/:path*`,
permanent: false,
})),
];
},
}
You can read more from the official next.js doc redirects or rewrite

Nuxt ssr asyncData axios request not works on page reload

After 5-6 hours of searching for the solution, I start to give up. I have a project with docker, node & nuxt.I would like to share meta tags on FB and for this, I need SSR && vue-meta and for vue-meta I need asyncData.
it works with different api!
async asyncData({ params, $axios, error }) {
const product = await $axios.$get(`https://api.nuxtjs.dev/posts/1`);
return { product };
},
It is working when i navigate to here with nuxt routing, but if i reload the page (just here) then i get a RuntimeError from the nuxt ssr.
SO THE REAL PROBLEM IS I GET A RuntimeError ON PAGE RELOAD ON LOCALHOST
async asyncData({ params, $axios, error }) {
//axios knows the api
//http://localhost:8080/
const product = await $axios.$get(`products/${params.id}`);
return { product };
},
If I try this with Axios then I get this error:
Cannot read property 'data' of undefined
If I try this with #nuxt/http then I get this error
request to http://localhost:8080/products/some-id failed, reason: connect ECONNREFUSED 127.0.0.1:8080
I think the problem is when I push hard reload on this page, then I cannot make requests on localhost but I don't know why and how to solve it.
my nuxt-config.js
export default {
// Disable server-side rendering (https://go.nuxtjs.dev/ssr-mode)
// ssr: false,
// Global page headers (https://go.nuxtjs.dev/config-head)
head: {
title: '',
meta: [
{ charset: 'utf-8' },
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
{ hid: 'description', name: 'description', content: '' }
],
link: [
{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' },
{ rel: 'preconnect', href: 'https://fonts.gstatic.com' },
{ rel: 'stylesheet', href: 'https://fonts.googleapis.com/css2?family=Josefin+Sans:wght#300;400;600&display=swap' },
]
},
// Global CSS (https://go.nuxtjs.dev/config-css)
css: [
'#/assets/scss/styles.scss',
// '~node_modules/bootstrap/dist/css/bootstrap.css',
// '~node_modules/bootstrap-vue/dist/bootstrap-vue.css',
],
// Plugins to run before rendering page (https://go.nuxtjs.dev/config-plugins)
plugins: [
'~/plugins/notifier',
'~/plugins/axios',
'~/plugins/dateFilter',
'~/plugins/loading',
],
// Auto import components (https://go.nuxtjs.dev/config-components)
components: true,
// Modules for dev and build (recommended) (https://go.nuxtjs.dev/config-modules)
buildModules: [
'nuxt-lazysizes', //https://github.com/ivodolenc/nuxt-lazysizes
'#aceforth/nuxt-optimized-images', //https://github.com/juliomrqz/nuxt-optimized-images //only build
],
// Modules (https://go.nuxtjs.dev/config-modules)
modules: [
'#nuxtjs/axios',
'#nuxtjs/auth',
'#nuxtjs/style-resources',
'nuxt-i18n',
'bootstrap-vue/nuxt',
'#nuxt/http',
],
styleResources: {
scss: [
// './assets/scss/*.scss',
'#/assets/scss/_variables.scss',
'#/node_modules/bootstrap/scss/_functions.scss',
'#/node_modules/bootstrap/scss/_variables.scss',
'#/node_modules/bootstrap/scss/mixins/_breakpoints.scss',
]
},
bootstrapVue: {
bootstrapCSS: false,
bootstrapVueCSS: false,
icons: false,
},
axios: {
// baseURL: ``, //built by docker compose from API_PORT && API_HOST variables
},
auth: {
strategies: {
local: {
endpoints: {
login: { url: '/auth/login', method: 'post', propertyName: 'token' },
logout: false,
user: { url: '/auth/user', method: 'get', propertyName: 'user' },
},
}
},
redirect: {
login: '/login',
logout: '/',
callback: '/login',
home: '/admin/products',
},
},
// Build Configuration (https://go.nuxtjs.dev/config-build)
build: {
// babel: {
// compact: true,
// },
},
router: {
extendRoutes(routes, resolve) {
routes.push(
{
name: 'product-edit',
path: '/admin/products/edit/:id',
component: 'pages/admin/products/add.vue',
},
{
name: 'product-image-upload',
path: '/admin/products/product-image-upload/:id',
component: 'pages/admin/products/product-image-upload.vue',
},
{
name: 'gallery-image-upload',
path: '/admin/gallery-image-upload',
component: 'pages/admin/gallery-image-upload.vue',
},
);
}
},
env: {
baseUrl: `${process.env.BASE_URL}`,
imagePath: `${process.env.BASE_URL}:${process.env.API_PORT}/uploads`,
},
publicRuntimeConfig: {
baseUrl: `${process.env.BASE_URL}`,
imagePath: `${process.env.BASE_URL}:${process.env.API_PORT}/uploads`,
},
loading: '~/components/LoadingBar.vue',
i18n: {
locales: [
{ code: 'en', iso: 'en-US', file: 'en.js' },
{ code: 'hu', iso: 'hu-HU', file: 'hu.js' },
{ code: 'de', iso: 'de-DE', file: 'de.js' },
],
defaultLocale: 'en',
lazy: true,
langDir: '/i18n/',
parsePages: false,
vueI18n: {
fallbackLocale: 'en',
},
detectBrowserLanguage: {
useCookie: true,
cookieKey: 'i18n_redirected',
},
seo: false,
vueI18nLoader: true,
strategy: 'no_prefix',
},
lazySizes: {
extendAssetUrls: {
img: 'data-src',
source: 'data-srcset',
// Component with custom props
AppImage: ['source-md-url', 'image-url'],
},
},
optimizedImages: {
inlineImageLimit: 1000,
handleImages: ['jpeg', 'png', 'svg', 'webp', 'gif'],
optimizeImages: false,
optimizeImagesInDev: false,
defaultImageLoader: 'img-loader',
mozjpeg: {
quality: 80,
},
optipng: {
optimizationLevel: 3,
},
pngquant: false,
gifsicle: {
interlaced: true,
optimizationLevel: 3,
},
svgo: {
// enable/disable svgo plugins here
},
webp: {
preset: 'default',
quality: 75,
},
},
};
my docker-compose.yml
version: '3'
services:
server:
build:
context: ./server
env_file: .env
expose:
- $SERVER_PORT
ports:
- $SERVER_PORT:$SERVER_PORT
volumes:
- ./server:/usr/app
- /usr/app/node_modules
client:
build:
context: ./client
env_file: .env
environment:
NUXT_HOST: 0.0.0.0
NUXT_PORT: $CLIENT_PORT
API_HOST: 0.0.0.0
API_PORT: $SERVER_PORT
BASE_URL: $BASE_URL
volumes:
- ./client:/usr/app
- /usr/app/node_modules
expose:
- $CLIENT_PORT
ports:
- $CLIENT_PORT:$CLIENT_PORT
Update: if I start my nuxt project without Docker then everything works fine but this is not a solution for me :D
I have solved my question. I had to use nginx proxy as a container and some nuxt config.
// https://axios.nuxtjs.org/options/
privateRuntimeConfig: {
axios: {
baseURL: 'http://nginx/api' // name of the docker proxy
}
},

MVC6 Self-Hosted wwwroot content returns 404, IIS Express doesn't

I'm playing with ASP.NET5/MVC6 and I built a small web application. When I use the IIS Express debug server of Visual Studio, everything is working as expected. But when I use the "web" server profile, meaning the WebListener server, then only my MVC Controllers and Views work. However, everything stored under "wwwroot" returns a 404. What I've put there are the CSS, JS and image files.
As soon as I switch back to IIS Express, the content is fetched properly.
The full source code is available there: https://github.com/acastaner/acastaner.fr-mvc6
This is my Startup class:
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
}
public void Configure(IApplicationBuilder app)
{
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller}/{action}/{id?}",
defaults: new { controller = "Home", action = "Index" });
});
}
}
This is my project.json file:
{
"webroot": "wwwroot",
"version": "1.0.0-*",
"dependencies": {
"Microsoft.AspNet.Server.IIS": "1.0.0-beta3",
"Microsoft.AspNet.Diagnostics": "1.0.0-beta3",
"Microsoft.AspNet.Mvc": "6.0.0-beta3",
"Microsoft.AspNet.Server.WebListener": "1.0.0-beta3"
},
"frameworks": {
"aspnet50": { },
"aspnetcore50": { }
},
"bundleExclude": [
"node_modules",
"bower_components",
"**.kproj",
"**.user",
"**.vspscc"
],
"exclude": [
"wwwroot",
"node_modules",
"bower_components"
],
"commands": {
"web ": "Microsoft.AspNet.Hosting --server Microsoft.AspNet.Server.WebListener --server.urls http://localhost:5000"
}
}
I'm using Razor for the views, here's a sample of how I reference the files:
<link href="~/css/bootstrap.min.css" rel="stylesheet" type="text/css">
Is there something obvious that I totally missed?
Edit:
I did try using #Url.Content("~/css/bootstrap.min.css") but the effect is the same.
I think you need one more dependency: "Microsoft.AspNet.StaticFiles": "1.0.0-beta3" and app.UseStaticFiles(); before app.UseMvc

Resources