ImageResizer and ASP.NET Core Web Application (.NET Framework) - imageresizer

Where can I find a full sample of an ImageResizer (ImageResizing.net) standalone website based on ASP.NET Core Web Application (.NET Framework) ?
"frameworks": {
"net461": { }
},

Here's a working PoC that simulates what ImageResizer + AzureReader2 do.
Startup.cs
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
namespace ImageResizerSvc
{
public class Startup
{
// This method gets called by the runtime. Use this method to add services to the container.
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddSingleton(x =>
{
var config = new ImageResizer.Configuration.Config();
// install plugins, e.g.
// new PrettyGifs().Install(config);
return config;
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseMvc(routes =>
{
routes.MapRoute("default", "{controller=Home}/{action=Index}/{id?}");
routes.MapRoute("imageresizer", "{*path}",
defaults: new { controller = "Images", action = "Resizer" });
});
}
}
}
ImagesController.cs
using ImageResizer;
using Microsoft.AspNetCore.Mvc;
using Microsoft.WindowsAzure.Storage;
using System;
using System.IO;
using System.Net;
using System.Threading.Tasks;
namespace ImageResizerSvc.Controllers
{
public class ImagesController : Controller
{
private readonly ImageResizer.Configuration.Config _imageResizerConfig;
public ImagesController(ImageResizer.Configuration.Config imageResizerConfig)
{
_imageResizerConfig = imageResizerConfig ?? throw new ArgumentNullException(nameof(imageResizerConfig));
}
public async Task<IActionResult> Resizer()
{
// Init storage account
var connectionString = "UseDevelopmentStorage=true";
CloudStorageAccount.TryParse(connectionString, out CloudStorageAccount cloudStorageAccount);
var cloudBlobClient = cloudStorageAccount.CreateCloudBlobClient();
// Get blob ref
var storagePath = cloudBlobClient.BaseUri.ToString().TrimEnd('/', '\\');
var blobPath = Request.Path.Value.Trim('/', '\\');
var blobUri = new Uri($"{storagePath}/{blobPath}");
using (var sourceStream = new MemoryStream(4096))
{
try
{
var blob = await cloudBlobClient.GetBlobReferenceFromServerAsync(blobUri);
await blob.DownloadToStreamAsync(sourceStream);
sourceStream.Seek(0, SeekOrigin.Begin);
}
catch (StorageException e)
{
// Pass to client
if (Enum.IsDefined(typeof(HttpStatusCode), e.RequestInformation.HttpStatusCode))
{
return StatusCode(e.RequestInformation.HttpStatusCode, e.RequestInformation.HttpStatusMessage);
}
throw;
}
var destinationStream = new MemoryStream(4096);
var instructions = new Instructions(Request.QueryString.Value);
var imageJob = _imageResizerConfig.Build(new ImageJob(sourceStream, destinationStream, instructions));
destinationStream.Seek(0, SeekOrigin.Begin);
return File(destinationStream, imageJob.ResultMimeType);
}
}
}
}
Then you can use it by going to http://localhost/{container}/{blobPath.ext}?{imageResizer_queryString}

Imageflow.NET Server is the .NET Core equivalent to ImageResizer, but is much faster and produces much smaller image files. See https://github.com/imazen/imageflow-dotnet-server
If you want to do your own middleware, use Imageflow.NET directly. See https://github.com/imazen/imageflow-dotnet
[Disclaimer: I am the author of both ImageResizer and Imageflow]

Related

How to have a Self Hosting signalR server running as as NetCore Console App

I would like to create a SignalR Self hosting Server within a console app using .NetCore.
I am completely new to web development and .Net Core but would like to use SignalR as a real-time web based protocol. No web page is required, and so I would like to have a console app.
I have successfully tested the .Net Framework example below and would like to replicate this using .Net Core 3.1, so that it can run on Linux. However I cannot find any suitable examples.
using System;
using Microsoft.AspNet.SignalR;
using Microsoft.Owin.Hosting;
using Owin;
using Microsoft.Owin.Cors;
namespace SignalRSelfHost
{
class Program
{
static void Main(string[] args)
{
// This will *ONLY* bind to localhost, if you want to bind to all addresses
// use http://*:8080 to bind to all addresses.
// See http://msdn.microsoft.com/library/system.net.httplistener.aspx
// for more information.
string url = "http://localhost:8088";
using (WebApp.Start<Startup>(url))
{
Console.WriteLine("Server running on {0}", url);
Console.ReadLine();
}
}
}
class Startup
{
public void Configuration(IAppBuilder app)
{
app.UseCors(CorsOptions.AllowAll);
app.MapSignalR();
}
}
public class MyHub : Hub
{
public void Send(string name, string message)
{
Clients.All.addMessage(name, message);
Clients.All.addMessage(name, "World");
}
}
}
In an attempt to use Owin to create a server console app I have the following code and this compiles, however complains about no server service being registered when I run the program. Could someone please advise what to add to have a web server without web page? The example I copied specified UseKestrel() but I think this is for a web page, so I think I need something else.
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
namespace OwinConsole
{
public class Startup
{
// For more information on how to configure your application, visit http://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
}
public void Configure(IApplicationBuilder app)
{
app.UseOwin(pipeline =>
{
pipeline(next => OwinHello);
});
}
public Task OwinHello(IDictionary<string, object> environment)
{
string responseText = "Hello World via OWIN";
byte[] responseBytes = Encoding.UTF8.GetBytes(responseText);
// OWIN Environment Keys: http://owin.org/spec/spec/owin-1.0.0.html
var responseStream = (Stream)environment["owin.ResponseBody"];
var responseHeaders = (IDictionary<string, string[]>)environment["owin.ResponseHeaders"];
responseHeaders["Content-Length"] = new string[] { responseBytes.Length.ToString(CultureInfo.InvariantCulture) };
responseHeaders["Content-Type"] = new string[] { "text/plain" };
return responseStream.WriteAsync(responseBytes, 0, responseBytes.Length);
}
}
}
using System.IO;
using Microsoft.AspNetCore.Hosting;
namespace OwinConsole
{
public class Program
{
public static void Main(string[] args)
{
var host = new WebHostBuilder()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseStartup<Startup>()
.Build();
host.Run();
}
}
}
Thanks.
For those who want to achive this in .NET 6:
To create a simple server as a console application, you have to create a new empty ASP.NET Core project. In .NET 6 you don't need the 'startup.cs' anymore. You just need to change a few things in 'Program.cs' to configure SignalR.
Program.cs
var builder = WebApplication.CreateBuilder(args);
//builder.Services.AddRazorPages();
builder.Services.AddSignalR();
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
//app.MapRazorPages();
app.MapHub<ChatHub>("/chatHub");
app.Run();
Add a Hub
public class ChatHub : Hub
{
public async Task SendMessage(string user, string message)
{
Console.WriteLine("Received message, sending back echo");
await Clients.All.SendAsync("ReceiveMessage", user, message);
}
}
Client (console application)
For example:
using Microsoft.AspNetCore.SignalR.Client;
namespace Client
{
public class Program
{
private static HubConnection _connection;
public static async Task Main(string[] args)
{
_connection = new HubConnectionBuilder()
.WithUrl("https://localhost:7116/chatHub")
.Build();
_connection.Closed += async (error) =>
{
await Task.Delay(new Random().Next(0, 5) * 1000);
await _connection.StartAsync();
};
await ConnectAsync();
bool stop = false;
while (!stop)
{
Console.WriteLine("Press any key to send message to server and receive echo");
Console.ReadKey();
Send("testuser", "msg");
Console.WriteLine("Press q to quit or anything else to resume");
var key = Console.ReadLine();
if (key == "q") stop = true;
}
}
private static async Task ConnectAsync()
{
_connection.On<string, string>("ReceiveMessage", (user, message) =>
{
Console.WriteLine("Received message");
Console.WriteLine($"user: {user}");
Console.WriteLine($"message: {message}");
});
try
{
await _connection.StartAsync();
}
catch(Exception e)
{
Console.WriteLine("Exception: {0}", e);
}
}
private static async void Send(string user, string msg)
{
try
{
await _connection.InvokeAsync("SendMessage", user, msg);
}
catch (Exception e)
{
Console.WriteLine($"Exception: {e}");
}
}
}
}
The client will connect to the server, after that in a loop you can send a message to the server by pressing any key and the server will send you the same message back.
In 'launchSettings.json' (Server) you can find the applicaitonUrl
As mentioned by Noah, my solution was based on
[https://learn.microsoft.com/en-us/aspnet/core/tutorials/signalr?view=aspnetcore-5.0&tabs=visual-studio]
But instead was built as a console app referencing Microsoft.AspNetCore.App (2.2.8).
ChatHub
using Microsoft.AspNetCore.SignalR;
using System.Threading.Tasks;
namespace SignalRServer
{
public class ChatHub : Hub
{
public async Task SendMessage(string user, string message)
{
await Clients.All.SendAsync("ReceiveMessage", user, message);
}
}
}
Startup
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
namespace SignalRServer
{
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.AddRazorPages();
services.AddSignalR();
}
// 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();
}
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.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
//endpoints.MapRazorPages();
endpoints.MapHub<ChatHub>("/chatHub");
});
}
}
}
Program
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
namespace SignalRServer
{
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
webBuilder.UseUrls("http://localhost:2803");
});
}
}

URL Globalization in asp.net core 5

I am about to change my asp.net core Globalization by URL.
Just like Microsoft official site:
https://www.microsoft.com/zh-cn/ is for the Chinese version.
https://www.microsoft.com/en-us/ is for the English version.
What I want to do is something different:
https://www.aaa.com/zh-hans/index.html is for the Chinese version.
https://www.aaa.com/en/index.html is for the English version.
As you see above, I need to add a .html suffix also.
I found some tutorials on google.
Here is my code:
public void ConfigureServices(IServiceCollection services)
{
services.AddLocalization(options => options.ResourcesPath = "Resources");
services.AddMvc().AddViewLocalization(Microsoft.AspNetCore.Mvc.Razor.LanguageViewLocationExpanderFormat.Suffix,
options => options.ResourcesPath = "Resources").AddSessionStateTempDataProvider();
}
public void Configure(IApplicationBuilder app)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
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.UseRouting();
app.UseStaticFiles();
var SupportedCultures = new List<CultureInfo> {
new CultureInfo("en"),
new CultureInfo("zh-Hans"),
new CultureInfo("zh-Hant")
};
var options = new RequestLocalizationOptions
{
DefaultRequestCulture = new RequestCulture("en"),
SupportedCultures = SupportedCultures,
SupportedUICultures = SupportedCultures,
};
app.UseRequestLocalization(options);
var requestProvider = new RouteDataRequestCultureProvider();
options.RequestCultureProviders.Insert(0, requestProvider);
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(name: "default", pattern: "{culture}/{controller=Home}/{action=Index}.html", defaults: new { culture = "en", controller = "Home", action = "Index" });
});
}
However, after the website ran, it reports an error:
An unhandled exception occurred while processing the request.
AmbiguousMatchException: The request matched multiple endpoints. Matches:
Sample.Controllers.HomeController.Index (Sample)
Sample.Controllers.HomeController.Error (Sample)
Sample.Controllers.HomeController.Privacy (Sample)
Microsoft.AspNetCore.Routing.Matching.DefaultEndpointSelector.ReportAmbiguity(CandidateState[] candidateState)
It seems that's because I added a .html suffix in MapControllerRoute.
I am not quite sure whether my code is right for most of the tutorial is for asp.net core 2 or even for asp.net core 1. Most of the code now not work in asp.net core 5 anymore.
What's wrong with my code? Thank you.
HomeController:
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using Sample.Models;
namespace Sample.Controllers
{
public class HomeController : Controller
{
private readonly ILogger<HomeController> _logger;
public HomeController(ILogger<HomeController> logger)
{
_logger = logger;
}
public IActionResult Index()
{
return View();
}
public IActionResult Privacy()
{
return View();
}
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public IActionResult Error()
{
return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
}
}
}

Combine IdentityServer4 and ASP.NET Core Identity Framework

Hi everyone I have a problem with a combination of IdentityServer4 and Identity Framework.
I have 3 projects in my solution.
First, it is an OAuth project with IdentityServer4. This project has the next configuration:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using IdentityServer4;
using IdentityServer4.Models;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Platform.OAuth.Data;
using Platform.OAuth.Data.Models;
namespace Platform.OAuth
{
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.AddDbContext<ApplicationContext>(options =>
{
options.UseSqlServer(
Configuration.GetConnectionString("DefaultConnection"),
x => x.MigrationsAssembly(typeof(ApplicationContext).Assembly.FullName));
});
services.AddIdentity<ApplicationUser, ApplicationRole>()
.AddEntityFrameworkStores<ApplicationContext>();
services.AddControllers();
services.AddIdentityServer()
.AddDeveloperSigningCredential()
.AddInMemoryPersistedGrants()
.AddInMemoryClients(new List<Client>
{
new Client
{
ClientId = "api-client",
ClientName = "API Client",
AllowedGrantTypes = GrantTypes.HybridAndClientCredentials,
RequireConsent = false,
ClientSecrets =
{
new Secret("secret".Sha256())
},
RedirectUris = { "http://localhost:5002/signin-oidc" },
PostLogoutRedirectUris = { "http://localhost:5002/signout-callback-oidc" },
AllowedScopes =
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
"api1"
},
AllowOfflineAccess = true
}
})
.AddInMemoryApiScopes(new List<ApiScope>
{
new ApiScope("api1", "My API")
})
.AddAspNetIdentity<ApplicationUser>();
services.AddAuthentication();
}
// 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.UseIdentityServer();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
}
Second, it is API project that contains some API which I want to protect. This project has next configuration:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.IdentityModel.Tokens;
namespace Api
{
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.AddControllers();
services.AddAuthorization(options =>
{
options.FallbackPolicy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
});
services.AddAuthentication("Bearer")
.AddJwtBearer("Bearer", options =>
{
options.Authority = "https://localhost:44392"; /// <-- OAuth project url
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateAudience = false
};
});
}
// 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.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
}
Thrid, It is client project that makes calls for both API.
static async Task Main(string[] args)
{
var client = new HttpClient();
var disco = await client.GetDiscoveryDocumentAsync("https://localhost:44392");
if (disco.IsError)
{
Console.WriteLine(disco.Error);
return;
}
var tokenResponse = await client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest
{
Address = disco.TokenEndpoint,
ClientId = "api-client",
ClientSecret = "secret",
Scope = "api1"
});
if (tokenResponse.IsError)
{
Console.WriteLine(tokenResponse.Error);
return;
}
Console.WriteLine(tokenResponse.Json);
client.SetBearerToken(tokenResponse.AccessToken);
var testResponse = await client.GetAsync("https://localhost:44392/WeatherForecast");
if(testResponse.IsSuccessStatusCode)
{
var content = await testResponse.Content.ReadAsStringAsync();
Console.WriteLine(JArray.Parse(content));
}
var apiClient = new HttpClient();
apiClient.SetBearerToken(tokenResponse.AccessToken);
Console.WriteLine("==============================================================");
var response = await apiClient.GetAsync("https://localhost:44369/WeatherForecast");
if (!response.IsSuccessStatusCode)
{
Console.WriteLine(response.StatusCode);
}
else
{
var content = await response.Content.ReadAsStringAsync();
Console.WriteLine(JArray.Parse(content));
}
}
Both web API projects have default WeatherForecastController which I've protected by AuthorizeAttribute And when the client makes a request with a token to API, the action returns data, but when a request for OAuth, action returns 404 error. I think it is good for the OAuth project but not for the API because API and 'OAuth' projects don't have authorized users. But why the API return data?
Receiving 404 should not be related with any auth issue, since it is the HTTP "not found" code, retrieved precisely when the resource you tried to access is not found with the URL that was used.
Concerning the data received from the API endpoint, it is a successful response, right? I will assume it is.
Following your client code snippet, it seems to me that you get a token from the Identity Server and perform a GET request with the token set. Since the token is valid, the client is authenticated and the API recognizes it.
If you set the [Authorize] attribute without any specified policy, the default applies. The default is: if you are authenticated, you are also authorized; this means that providing a valid token is enough to be authorized - which you do.
I would say that this is why you get a successful response. More information here.

Why does my URL get processed before I press Enter?

I'm working through Adam Freeman's book "Pro ASP.Net Core 3" (8th edition). I am running an example of a page that uses information in a database. The Configure() method in my Startup class includes an endpoint for "/sum/{count:1000000000}". The base URL is http://localhost:5000. In Google Chrome, I type "http://localhost:5000/sum", and as soon as I type the "m", the web page is requested. If I want to get the calculation for 10 by requesting http://localhost:5000/sum/10, the page for 1000000000 is requested first. I can imagine that in a real-world application, that could end up being a problem. How do I avoid that?
Here's my startup file:
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Platform.Services;
using Microsoft.EntityFrameworkCore;
using Platform.Models;
using Microsoft.Extensions.Hosting;
using Microsoft.AspNetCore.Hosting;
namespace Platform {
public class Startup {
public Startup(IConfiguration config) {
Configuration = config;
}
private IConfiguration Configuration { get; set; }
public void ConfigureServices(IServiceCollection services) {
services.AddDistributedSqlServerCache(opts =>
{
opts.ConnectionString
= Configuration["ConnectionStrings:CacheConnection"];
opts.SchemaName = "dbo";
opts.TableName = "DataCache";
});
services.AddResponseCaching();
services.AddSingleton<IResponseFormatter, HtmlResponseFormatter>();
services.AddDbContext<CalculationContext>(opts =>
{
opts.UseSqlServer(Configuration["ConnectionStrings:CalcConnection"]);
});
services.AddTransient<SeedData>();
}
public void Configure(IApplicationBuilder app,
IHostApplicationLifetime lifetime, IWebHostEnvironment env,
SeedData seedData) {
app.UseDeveloperExceptionPage();
app.UseResponseCaching();
app.UseStaticFiles();
app.UseRouting();
app.UseEndpoints(endpoints => {
endpoints.MapEndpoint<SumEndpoint>("/sum/{count:int=1000000000}");
endpoints.MapGet("/", async context => {
await context.Response.WriteAsync("Hello World!");
});
});
bool cmdLineInit = (Configuration["INITDB"] ?? "false") == "true";
if (env.IsDevelopment() || cmdLineInit) {
seedData.SeedDatabase();
if (cmdLineInit) {
lifetime.StopApplication();
}
}
}
}
}

Localization in .NET Core API

I am trying to create localization Middleware in .NET Core API 2.2, I was following Microsoft's official instruction but I don't quite understand, how it works? I don't have views, so I must localize models with data annotations right?
I want to get language values through Accept-Language HTTP Header
here is my Middleware:
public void ConfigureServices(IServiceCollection services)
{
services.AddLocalization(options => options.ResourcesPath = "Resources");
services.AddMvc()
.AddDataAnnotationsLocalization();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
var supportedCultures = new[] { "en-US", "ka-Ge" };
var localizationOptions = new RequestLocalizationOptions().SetDefaultCulture(supportedCultures[0])
.AddSupportedCultures(supportedCultures)
.AddSupportedUICultures(supportedCultures);
app.UseRequestLocalization(localizationOptions);
}
Model:
public class PersonsModel
{
[Display(Name = "Name")]
public string Name { get; set; }
}
I have Resx Files in Resourses folders: PersonsController.en-Us
please edit/improve/correct/adjust
references:
https://damienbod.com/2015/10/21/asp-net-5-mvc-6-localization/
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/localization?view=aspnetcore-3.1#implement-a-strategy-to-select-the-languageculture-for-each-request
https://ml-software.ch/posts/writing-a-custom-request-culture-provider-in-asp-net-core-2-1
ASP.NET Core Request Localization Options
No, you need to apply somewhere (in your LocalizationMiddleware) IStringLocalizer, so you can create a getFunction with LocalizedString as return type.
That way you can use localization in your application wherever you want it.
Based on the culture-information of your current thread, and how you have defined your SharedResource, you'll get the corresponding value for the key.
You can find a solution below:
Checklist
Configure services in the DI container
a. Register the service in
the Startup-method (see created separate classDIConfiguration)
b. Configure and apply options of Localization in the
Configure-method
Define Localizationoptions (CustomRequestCultureProvider Class)
Build up your middleware in the appropriate project (business logic)
Define SharedResource.class and resx-files in the same project
Inject Middleware in business logic-class (handler)
 
Configure services in the DI container
a. Register the service in the Startup-method (see created separate classDIConfiguration)
b. Configure and apply options of Localization in the Configure-method
using …
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.SpaServices.AngularCli;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
readonly string MyAllowSpecificOrigins = "_myAllowSpecificOrigins";
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();services.AddCors(options =>
{
options.AddPolicy(name: MyAllowSpecificOrigins,
builder =>
{
builder.WithOrigins("http://localhost:4200")
.AllowAnyHeader()
.AllowAnyMethod()
.WithExposedHeaders("Content-Disposition");
});
});
// In production, the Angular files will be served from this directory
services.AddSpaStaticFiles(configuration =>
{
configuration.RootPath = "ClientApp/dist";
});
DiConfiguration.ConfigureServices(Configuration, services);
AutoMapperConfiguration.ConfigureAutoMapper(services);
services.AddMediatorAndBehaviour();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
var supportedCultures = new[] { "nl", "en", "fr", "de" };
var localizationOptions = new RequestLocalizationOptions()
.SetDefaultCulture(supportedCultures[0])
.AddSupportedCultures(supportedCultures)
.AddSupportedUICultures(supportedCultures);
localizationOptions.RequestCultureProviders.Clear();
localizationOptions.RequestCultureProviders.Add(new CustomRequestCultureProvider(localizationOptions));
app.UseRequestLocalization(localizationOptions);
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.UseHttpsRedirection();
app.UseStaticFiles();
if (!env.IsDevelopment())
{
app.UseSpaStaticFiles();
}
app.UseAuthentication();
app.UseRouting();
app.UseAuthorization();
app.UseCors(MyAllowSpecificOrigins);
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{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");
}
});
app.UseHttpsRedirection();
}
}
 
using System;
using System.Text;
…
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.IdentityModel.Tokens;
public static class DiConfiguration
{
public static void ConfigureServices(IConfiguration configuration, IServiceCollection services)
{
// Configure Database services
services.AddDbContextPool<IsisContext>(options => options.UseSqlServer(configuration.GetConnectionString("IsisDatabase")));
services.AddScoped<IIsisUnitOfWork, IsisUnitOfWork>();
…
services.AddSingleton<ILocalizationMiddleware, LocalizationMiddleware>();
services.AddControllers();
services.AddMemoryCache();
services.AddOptions();
…
services.AddLocalization(options => options.ResourcesPath = "Resources");
}
}
Define Localizationoptions (CustomRequestCultureProvider Class)
using …;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Localization;
using System;
using System.Linq;
using System.Threading.Tasks;
public class CustomRequestCultureProvider : IRequestCultureProvider
{
public CustomRequestCultureProvider(RequestLocalizationOptions options)
{
}
public Task<ProviderCultureResult> DetermineProviderCultureResult(HttpContext httpContext)
{
try
{
var transmittedLanguageCode = httpContext.Request.GetTypedHeaders().AcceptLanguage;
string transmittedLanguageCodeString = transmittedLanguageCode.LastOrDefault().ToString();
return Task.FromResult(new ProviderCultureResult(transmittedLanguageCodeString));
}
catch (Exception)
{
return Task.FromResult(new ProviderCultureResult(LanguagesDefinition.NL)); // scenario NL is the default
}
}
}
 
3. Build up your middleware in the appropriate project (business logic)
using Microsoft.Extensions.Localization;
namespace ….Business.LocalizationService
{
public interface ILocalizationMiddleware
{
public LocalizedString GetLocalizedString(string keyForResourceTable);
}
}
using Microsoft.Extensions.Localization;
using System.Reflection;
namespace ….Business.LocalizationService
{
public class LocalizationMiddleware : ILocalizationMiddleware
{
private readonly IStringLocalizer localizer;
public LocalizationMiddleware(IStringLocalizerFactory factory)
{
localizer = factory.Create("SharedResource", Assembly.GetExecutingAssembly().FullName);
}
public LocalizedString GetLocalizedString(string keyForResourceTable) { return localizer[keyForResourceTable]; }
}
}
 
4; Define (SharedResource.class optional) Resources-folder and resx-files in the same project
 
5. Inject Middleware in business logic-class (handler)
public class SomeClass
{
public AdvancedSearchResultDtoToCsvMap(ILocalizationMiddleware localizationMiddleware)
{
Map(m => m.Id).Ignore();
Map(m => m.ArticleCode).Index(0).Name(localizationMiddleware.GetLocalizedString("articlecode").Value);
Map(m => m.Fullname).Index(1).Name(localizationMiddleware.GetLocalizedString("fullname").Value);
…
}
}