URL Globalization in asp.net core 5 - asp.net-core

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 });
}
}
}

Related

Swashbuckle extension version switch

I am new to asp.net core and I am using swagger. I downloaded it by following the steps Install-Package Swashbuckle.AspNetCore -Version 5.6.3.Then add middleware
services.AddSwaggerGen();Then add app.UseSwagger(c =>
{
c.SerializeAsV2 = true;
});
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
}); to the ge
nerated JSON document and Swagger UI.
Finally add the header information as per the documentation.
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo
{
Version = "v1",
Title = "ToDo API",
Description = "A simple example ASP.NET Core Web API",
TermsOfService = new Uri("https://example.com/terms"),
Contact = new OpenApiContact
{
Name = "Shayne Boyer",
Email = string.Empty,
Url = new Uri("https://twitter.com/spboyer"),
},
License = new OpenApiLicense
{
Name = "Use under LICX",
Url = new Uri("https://example.com/license"),
}
});
});
These are all requirements to follow the documentation. But I need to extend the requirements now, I need to add the version information of the API, similar to the API V1 API V2 version switch. I have referenced some sources but don't have the complete code, can you guys help me? Any help is excellent! !
Are you trying to switch versions like this?
First I created 2 versions of folders and controllers.Therefore, the namespace of each controller corresponds to its folder, like this:
V1 version:
namespace WebApplication129.Controllers.V1
{
[ApiController]
[Route("api/v1/[controller]")]
public class HomeController : ControllerBase
{
[Route("test")]
[HttpGet]
public string Test()
{
return "v1 test";
}
}
}
V2 version:
namespace WebApplication129.Controllers.V2
{
[ApiController]
[Route("api/v2/[controller]")]
public class HomeController : ControllerBase
{
[Route("test")]
[HttpGet]
public string Test()
{
return "v2 test";
}
}
}
Then create an agreement to inform Swagger, in this way, we can control how Swagger generates Swagger documents, thereby controlling the UI.
Create the following class:
public class GroupingByNamespaceConvention : IControllerModelConvention
{
public void Apply(ControllerModel controller)
{
var controllerNamespace = controller.ControllerType.Namespace;
var apiVersion = controllerNamespace.Split(".").Last().ToLower();
if (!apiVersion.StartsWith("v")) { apiVersion = "v1"; }
controller.ApiExplorer.GroupName = apiVersion;
}
}
Now we must apply the convention. For that we go to AddControllers in ConfigureServices and add the convention:
services.AddControllers(options =>
{
options.Conventions.Add(new GroupingByNamespaceConvention());
});
The final complete startup.cs configuration is as follows:
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.OpenApi.Models;
using System;
using WebApplication129.Controllers.conf;
namespace WebApplication129
{
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(options =>
{
options.Conventions.Add(new GroupingByNamespaceConvention());
});
services.AddSwaggerGen(config =>
{
var titleBase = "Test API";
var description = "This is a Web API for Test operations";
var TermsOfService = new Uri("https://xxxxxx");
var License = new OpenApiLicense()
{
Name = "Test"
};
var Contact = new OpenApiContact()
{
Name = "Test",
Email = "Test#hotmail.com",
Url = new Uri("https://xxxxxx")
};
config.SwaggerDoc("v1", new OpenApiInfo
{
Version = "v1",
Title = titleBase + " v1",
Description = description,
TermsOfService = TermsOfService,
License = License,
Contact = Contact
});
config.SwaggerDoc("v2", new OpenApiInfo
{
Version = "v2",
Title = titleBase + " v2",
Description = description,
TermsOfService = TermsOfService,
License = License,
Contact = Contact
});
});
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseSwagger();
app.UseSwaggerUI(config =>
{
config.SwaggerEndpoint("/swagger/v1/swagger.json", "API v1");
config.SwaggerEndpoint("/swagger/v2/swagger.json", "API v2");
});
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
}

ASP.NET Core MVC Enforce SEO 2021

Everyone needs to do common SEO rules, but there is no widely published way in ASP.NET Core MVC to:
Redirect http to https
Redirect non-www to www
Rewrite URLs as lower case
Remove trailing /
In ASP.NET these were rewrite rules in web.config. The common answer is adding the following in Startup.cs which doesn't enforce or rewrite:
services.AddRouting(options => options.LowercaseUrls = true);
services.AddRouting(options => options.AppendTrailingSlash = true);
I think the best way is adding middleware with the app.Use, but I can't find a proven example. Here's my Startup.CS:
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Matrixforce
{
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.AddControllersWithViews();
}
// 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();
}
// CUSTOM redirect to 404 page not found
app.Use(async (context, next) =>
{
await next();
if (context.Response.StatusCode == 404)
{
context.Request.Path = "/404/";
await next();
}
});
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
}
}
}
Vitali Karmanov on Github has a good example of requirements 3 and 4 but NOT 1 and 2. Now I just need an example of the first two rules to add to the custom Helper.
/Helpers/RedirectLowerCaseRule.cs
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Rewrite;
using Microsoft.Net.Http.Headers;
using System.Linq;
using System.Net;
namespace Matrixforce.Helpers
{
public class RedirectLowerCaseRule : IRule
{
public int StatusCode { get; } = (int)HttpStatusCode.MovedPermanently;
public void ApplyRule(RewriteContext context)
{
HttpRequest request = context.HttpContext.Request;
PathString path = context.HttpContext.Request.Path;
PathString pathbase = context.HttpContext.Request.PathBase;
HostString host = context.HttpContext.Request.Host;
if ((path.HasValue && path.Value.Any(char.IsUpper)) || (host.HasValue && host.Value.Any(char.IsUpper)) || (pathbase.HasValue && pathbase.Value.Any(char.IsUpper)))
{
HttpResponse response = context.HttpContext.Response;
response.StatusCode = StatusCode;
response.Headers[HeaderNames.Location] = (request.Scheme + "://" + host.Value + request.PathBase + request.Path).ToLower() + request.QueryString;
context.Result = RuleResult.EndResponse;
}
else
{
context.Result = RuleResult.ContinueRules;
}
}
}
public class RedirectEndingSlashCaseRule : IRule
{
public int StatusCode { get; } = (int)HttpStatusCode.MovedPermanently;
public void ApplyRule(RewriteContext context)
{
HttpRequest request = context.HttpContext.Request;
HostString host = context.HttpContext.Request.Host;
string path = request.Path.ToString();
if (path.Length > 1 && path.EndsWith("/"))
{
request.Path = path.Remove(path.Length - 1, 1);
HttpResponse response = context.HttpContext.Response;
response.StatusCode = StatusCode;
response.Headers[HeaderNames.Location] = (request.Scheme + "://" + host.Value + request.PathBase + request.Path).ToLower() + request.QueryString;
context.Result = RuleResult.EndResponse;
}
else
{
context.Result = RuleResult.ContinueRules;
}
}
}
}
Startup.cs
using Matrixforce.Helpers;
app.UseRewriter(new RewriteOptions().Add(new RedirectLowerCaseRule()).Add(new RedirectEndingSlashCaseRule()));

How do I reflect a dotnet web api endpoint that uses query string parameters in SwagggerUI?

I'm trying to implement a dotnet web api with API versioning that uses query strings and headers. Here im using swagger to document and test the endpoints. I successfully used path versioning and reflected the endpoints in swagger. But im struggling to understand how to reflect query string & header versioning in swagger. I tried to find a solution from this article https://swagger.io/docs/specification/describing-parameters/#query-parameters but was still confused how to implement this in my dotnet web api.
My project contains 2 main controller classes with the following API versions.
WeatherForecastController.cs
namespace QueryStringVersioning.Controllers
{
[ApiController]
[ApiVersion("1.0")]
[ApiVersion("1.1", Deprecated = true)]
[ApiVersion("3.0")]
[Route ("api")] //support query string & header versioning
// [Route("api/v{version:apiVersion}/[controller]")] //support path versioning
public class WeatherForecastController : ControllerBase
{
private static readonly string[] Summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering",
"Scorching"
};
private readonly ILogger<WeatherForecastController> _logger;
public WeatherForecastController(ILogger<WeatherForecastController> logger)
{
_logger = logger;
}
[HttpGet]
public IEnumerable<WeatherForecast> Get()
{
var rng = new Random();
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateTime.Now.AddDays(index),
TemperatureC = rng.Next(-20, 55),
Summary = Summaries[rng.Next(Summaries.Length)]
})
.ToArray();
}
[HttpGet, MapToApiVersion("3.0")]
public IActionResult GetV3_0() => Ok(new string[] { "MapToApiVersion value 3.0" });
[HttpGet, MapToApiVersion("1.1")]
public IActionResult GetV1_1() => Ok(new string[] { "Depreceated MapToApiVersion value" });
}}
WeatherForecastController2.cs
namespace QueryStringVersioning.Controllers2
{
[ApiController]
[ApiVersion("2.0")]
[ApiVersion("2.1")]
[Route ("api")] //support query string & header versioning
// [Route("api/v{version:apiVersion}/[controller]")] //support path versioning
public class WeatherForecastController : ControllerBase
{
public IActionResult GetV2_0() => Ok(new string[] { "This is API Version 2.0" });
[HttpGet, MapToApiVersion("2.1")]
public IActionResult GetV2_1() => Ok(new string[] { "This is API Version 2.1" });
}}
And the startup.cs file
namespace QueryStringVersioning
{
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.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new Microsoft.OpenApi.Models.OpenApiInfo
{
Version = "v1",
Title = "API_Versioning V1",
});
c.SwaggerDoc("v1.1", new Microsoft.OpenApi.Models.OpenApiInfo
{
Version = "v1.1",
Title = "API_Versioning V1.1",
});
c.SwaggerDoc("v2", new Microsoft.OpenApi.Models.OpenApiInfo
{
Version = "v2",
Title = "API_Versioning V2"
});
c.SwaggerDoc("v2.1", new Microsoft.OpenApi.Models.OpenApiInfo
{
Version = "v2.1",
Title = "API_Versioning V2.1"
});
c.SwaggerDoc("v3", new Microsoft.OpenApi.Models.OpenApiInfo
{
Version = "v3",
Title = "API_Versioning V3"
});
c.ResolveConflictingActions (apiDescriptions => apiDescriptions.First ());
// c.OperationFilter<RemoveVersionFromParameter>();
// c.DocumentFilter<ReplaceVersionWithExactValueInPath>();
});
services.AddControllers();
services.AddMvc();
services.AddApiVersioning(option =>
{
option.ReportApiVersions = true;
option.AssumeDefaultVersionWhenUnspecified = true;
option.DefaultApiVersion = new ApiVersion(1, 0);
// Supporting multiple versioning scheme
option.ApiVersionReader = ApiVersionReader.Combine(new HeaderApiVersionReader("X-version"), new QueryStringApiVersionReader("api-version"));
});
}
// 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.MapControllers();
});
// Enable middleware to serve generated Swagger as a JSON endpoint.
app.UseSwagger();
// Enable middleware to serve swagger-ui (HTML, JS, CSS, etc.),
// specifying the Swagger JSON endpoint.
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "API_Versioning V1.0");
c.SwaggerEndpoint("/swagger/v1.1/swagger.json", "API_Versioning V1.1");
c.SwaggerEndpoint("/swagger/v2/swagger.json", "API_Versioning V2.0");
c.SwaggerEndpoint("/swagger/v2.1/swagger.json", "API_Versioning V2.1");
c.SwaggerEndpoint("/swagger/v3/swagger.json", "API_Versioning V3.0");
});
}
}
}
#michael-wang is correct. You need to also include the API Versioning API Explorer extensions. This extensions make API discovery API version aware. One of the many possible uses for this information is OpenAPI/Swagger integration.
Links to all of the applicable NuGet packages are listed on the API Versioning landing page. There is also an end-to-end example provided using Swashbuckle.

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);
…
}
}

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

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]