Failed to get the culture Resource in .net 3.1 - asp.net-core

Good morning,
I'm using .netCore 3.1 and in my model I have the attributeValidation of email, but the return message is always for default, I can't get the message in the expected culture.
I think that the structure folders is correct and when I inspect the middleware the resource base have this route:
Someone have any idea?
Thanks!!

I'm using .netCore 3.1 and in my model I have the attributeValidation of email, but the return message is always for default, I can't get the message in the expected culture.
I did a test with following code snippet, which work as expected, you can refer to it.
My testing model class under Models folder
public class MyModel
{
[Required(ErrorMessage = "The Email field is required")]
[EmailAddress(ErrorMessage = "The Email field is not a valid email address")]
[Display(Name = "Email")]
public string Email { get; set; }
//...
//other properties
//...
}
My resource file MyModel.fr.resx and folder structure
Configure localization
services.AddLocalization(options => options.ResourcesPath = "Resources");
services.AddControllersWithViews().AddDataAnnotationsLocalization()
.AddViewLocalization(LanguageViewLocationExpanderFormat.Suffix);
Test Result
Besides, you can refer to this doc about "globalization and localization in ASP.NET Core" and try these steps to troubleshoot the issue.
check if you correctly configure localization and data annotations localization in your app
check if browser user select and set language/culture
check if you correctly use the dot or path naming convention to organize/name your resource file(s)

Related

Swagger showing no parameters to controller actions that have paramters

Swagger not showing parameters in UI and JSON, even tho my method has parameters, This particularly happens when I add the [FromBody] tag
swagger UI no parameters
JSON file no parameters
The action method:
[HttpPost("Action")]
public IActionResult Action([FromBody] string message)
{
return Ok(message);
}
I used fresh Asp.net core 3.1 and 2.2 web app with API template to test this,
I configured it exactly like the documnetaiton
ConfigureServices:
services.AddMvc();
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "My API", Version = "v1" });
}
Configure:
app.UseSwagger();
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
});
It does work when I use other attributes like [FromRoute]&[FromHeader] so on... I looked at the examples and swagger does show parameters from a post method
I also tried objects like this :
public class Message
{
public int ID { get; set; }
public string body { get; set; }
}
With this action :
[HttpPost("Action")]
public IActionResult Action([FromBody] Message message)
{
return Ok(message);
}
same result (no parameters) but it does show the schema
So what am I doing wrong? how can I document post parameters like the examples
I had a similar problem on AspNetCore 5.0, where "complex" types would be completely ignored, both as parameters and output types.
The problem was caused by switching input/output formatters to a different JSON serializer (legacy code, due to newtonsoft being slow in certain situations). The new swashbuckle can work either with System.Text.Json or Newtonsoft, but not a 3rd solution.
So check .AddMvcOptions() in ConfigureServices, if there is anything done with InputFormatters or OutputFormatters, that might be the culprit
You should look into this code sample as you are working with OpenAPI 3.0 : http://petstore.swagger.io:8080/
OpenAPI 3.0 provides the requestBody keyword to describe request bodies. It distinguish the payload from parameters (such as query string and PATH). The requestBody is more flexible in that it lets you consume different media types, such as JSON, XML, form data, plain text, and others, and use different schemas for different media types:
https://swagger.io/docs/specification/describing-request-body/
That is default UI and if you click "Try it out" , the sample value will auto fill Request body area to help create a test request body .

ASP.Net Core - How use custom 'ModelBindingMessageProvider'

I have a simple ASP.Net Core app which uses razor. The user forms are generated by model and data annotation attributes. The error message of wrong input values are English by default. I knew that I can translate default message with help of 'ModelBindingMessageProvider'.
Below you can find a working way (in Startup.cs) by update the 'DefaultModelBindingMessageProvider':
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc(o =>
{
// see: https://stackoverflow.com/questions/40828570/asp-net-core-model-binding-error-messages-localization
o.ModelBindingMessageProvider.SetValueMustNotBeNullAccessor(u => "My custom validation error message");
});
}
I would prefer not to configure the translation directly in the startup.cs. One way to do that is using a custom class which inherits from 'ModelBindingMessageProvider'.
public class MyModelBindingMessageProvider : ModelBindingMessageProvider
{
public override Func<string, string> ValueMustNotBeNullAccessor => o => "My custom validation error message";
}
Here is my question:
Where register My own class?
Is this a good way? What is best practice at the moment?
(the real app uses string resources of course)
Unfortunately it looks like it's not possible to provide a custom implementation of ModelBindingMessageProvider in ASP.NET Core 3.1.
The constructor of MvcOptions sets the ModelBindingMessageProvider property to an instance of DefaultModelBindingMessageProvider.
ModelBindingMessageProvider = new DefaultModelBindingMessageProvider();
And the property itself has no setter and is not of type ModelBindingMessageProvider.
public DefaultModelBindingMessageProvider ModelBindingMessageProvider { get; }
tl;dr Even though the option of having an own implementation would make sense here, you have to configure the messages in the Startup class.

Problem localizing Display(Name=."..") data annotation attribute using Portable Objects (Orchard Core .po) in Asp.Net Core [duplicate]

I have used Orchard Localization in my asp.net core application.
Startup.cs
services.AddPortableObjectLocalization(options => options.ResourcesPath = "Resources");
services
.AddMvc()
.AddViewLocalization(LanguageViewLocationExpanderFormat.Suffix)
.AddDataAnnotationsLocalization();
model.cs
[Display(Name = "First Name")]
[Required(ErrorMessage = "Customer first name required")]
public string CustomerFirstName { get; set; }
en.po (contains English translations)
msgid "Customer first name required"
msgstr "Customer first name required"
no.po (contains Norwegian translation)
msgid "Customer first name required"
msgstr "Fornavn mangler"
Request url for english
Home/HomeRequest/?cid=40&culture=en
Request url for norwegian
Home/HomeRequest/?cid=11&culture=no
I first entered english url and checked the required validation, validation for english localization fired perfectly. Then I changed value of cid and culture to norwegian and checked the validation validation fires with english localization not in norwegian.
How to get the data annotation validation according to the locale?
The problem was a bug in OrchardCore.Localization.Core nuget package and it's fixed now. update your package to version 1.0.0-beta3-71025 or above to fix this problem.
you can find the issue thread here

Swashbuckle/Swagger on .NET Core 2.1 has stopped working since upgrade

I have a .NET Core 2.0 application, using Swashbuckle/Swagger to generate API documentation. When we were on 2.1.0-preview, Swagger was working fine. Then we did the big upgrade to 2.1.0 release and SDK 2.1.300. We didn't notice exactly when things broke, but now our Swagger docs won't load. Here's what we see:
Project has a reference to Swashbuckle.AspNetCore version 2.5.0. The relevant code in Startup.cs is below. In ConfigureServices():
services.AddSwaggerGen(swaggerOptions =>
{
// Register a swagger doc
swaggerOptions.SwaggerDoc("v1", new Info
{
// Optional descriptive info that will be included in the Swagger output
Contact = new Contact
{
Name = "LightSail",
Url = "https://myurl.com/"
},
Description = "A description of the API can go here",
Title = "My API",
Version = "v1"
});
// Xml file to get comment information from
swaggerOptions.IncludeXmlComments("App_Data/Api.xml");
});
And in Configure():
app.UseSwagger();
app.UseSwaggerUI(swaggerUiOptions => swaggerUiOptions.SwaggerEndpoint("/swagger/v1/swagger.json", "My API v1"));
I found lots of other similar questions, one of which suggested that there might be duplicate endpoints; I tried adding a call to .ResolveConflictingEndpoints() but that made no difference. I have searched through my project folders and there is no file called swagger.json, so I'm guessing that's the problem.
Any ideas why this is not working, or how to fix?
This is usually indicative of controllers/actions that Swashbuckle doesn't support for one reason or another.
It's expected that you don't have a swagger.json file in your project. Swashbuckle creates and serves that dynamically using ASP.NET Core's ApiExplorer APIs. What's probably happening here is that Swashbuckle is unable to generate Swagger.json and, therefore, the UI is failing to display.
As HelderSepu said, it's hard to know exactly what caused the failure, so the best way to debug is probably just to remove half your controllers (just move the files to a temporary location) and check whether the issues persists. Then you'll know which half of your controllers contains the troublesome action. You can 'binary search' removing controllers (and then actions) until you figure out which action method is causing Swashbuckle to not be able to generate Swagger.json. Once you know that, it should be obvious whether this is some issue in your code or an issue that should be filed in the Swashbuckle repo.
For example, Swashbuckle appears to not support open generics, so having a response type attribute like [ResponseType(typeof(IEnumerable<>))] could cause this sort of behavior. It could also be an issue with ambiguous routes or something like that tripping Swashbuckle up. Once you've narrowed down the cause of failure to something more specific like that, it can either be fixed or filed, as appropriate.
Today I found out that I could just go to the json url in the browser and get some error information
for example
myapiurl/api/vi/swagger.json
I was able to solve this error by explicitly adding the http verb attribute to my asp.net core 2.x controller method. The convention of prefixing the method name with the http verb is not enough for Swashbuckle apparently.
[HttpPost]
public async Task<IActionResult> AddNewData([FromBody] MyType myType) { … }
In my case I can reproduce your error by omitting "." from the end point as you have done.
I don't get the error if I include "." at the start of the path.
Here is more of my code in case it is relevant.
In ConfigureServices I have
services.AddSwaggerGen(c =>
{
c.OperationFilter<AuthorizationHeaderParameterOperationFilter>();
c.SwaggerDoc("v1", new Info
{
Version = "v1",
Title = "My API",
Description = "ASP.NET Core Web API",
TermsOfService = "None",
Contact = new Contact
{
Name = "my name",
Email = "me#myemail.com"
}
});
});
In configure
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseAuthentication();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseDatabaseErrorPage();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseCookiePolicy();
app.UseRewriter(new RewriteOptions()
.AddRedirectToHttpsPermanent());
app.UseSwagger(c =>
{
c.RouteTemplate =
"api-docs/{documentName}/swagger.json";
});
app.UseSwaggerUI(c =>
{
//Include virtual directory if site is configured so
c.RoutePrefix = "api-docs";
c.SwaggerEndpoint("./v1/swagger.json", "Api v1");
});
app.UseMvc(routes =>
{
routes.MapRoute(
"default",
"{controller=Home}/{action=Index}/{id?}");
});
Also there is
public class AuthorizationHeaderParameterOperationFilter : IOperationFilter
{
public void Apply(Operation operation, OperationFilterContext context)
{
var filterPipeline = context.ApiDescription.ActionDescriptor.FilterDescriptors;
var isAuthorized = filterPipeline.Select(filterInfo => filterInfo.Filter).Any(filter => filter is AuthorizeFilter);
var allowAnonymous = filterPipeline.Select(filterInfo => filterInfo.Filter).Any(filter => filter is IAllowAnonymousFilter);
if (isAuthorized && !allowAnonymous)
{
if (operation.Parameters == null)
operation.Parameters = new List<IParameter>();
operation.Parameters.Add(new NonBodyParameter
{
Name = "Authorization",
In = "header",
Description = "access token",
Required = true,
Type = "string"
});
}
}
My dependencies are
Microsoft.AspNetCore.App (2.1.0)
Swashbuckle.AspNetCore (2.5.0)
Microsoft.NETCore.App (2.1.0)
Personally I was a bit quick and forgot to add this line to the method ConfigureServices in Startup.cs.
services.AddSwaggerDocument();
In my case, I missed the 'HttpAttribute':
public async Task<IEnumerable<ClientesListDto>> GetAll()
{
return await _service.GetAllAsync();
}
Then I put it and swagger likes it:
[HttpGet]
public async Task<IEnumerable<ClientesListDto>> GetAll()
{
return await _service.GetAllAsync();
}
In my case, I had this:
[HttpGet("CleanUpSnoozedLeads")]
public async Task<ActionResult<bool>> CleanUpSnoozedLeads()
[HttpGet("CleanUpSnoozedLeads")]
public async Task<ActionResult<bool>> DoSomethingElse()
Notice the HttpGet() had the same name. That causes the undefined error as well.
A very common case is ambiguity. Just use the same signature for two PUT or POST operations for example and you will get the error.
Others answers did not worked for me.
I was able to fix and understand my issue when I tried to go to the swagger.json URL location:
https://localhost:XXXXX/swagger/v1/swagger.json
The page will show the error and reason why it is not found.
In my case, I saw that there was a misconfigured XML definition of one of my methods based on the error it returned:
NotSupportedException: HTTP method "GET" & path "api/Values/{id}" overloaded by actions - ...
...
...
In my case, i just forgot to add the HttpPostAttribute annotation to the method.
[HttpPost]
public ActionResult Post()
{
return Ok();
}
In my case there was a conflict in the schemaId. Apparently every class in the swagger JSON must have a unique schemaId. If you have two classes in different namespaces with the same name this will not work. We have to configure "UseFullTypeNameInSchemaIds" in the startup class.
Add "options.CustomSchemaIds(x => x.FullName);" in "services.AddSwaggerGen"
I found the trace by enabling Output window in VS, selecting the main project from Show output from dropdown list then visit http://{yourapiendpoint}/swagger/v1/swagger.json
If your api have same two or more [HttpGet] its not working swagger.
You should be specify [HttpGet] , [HttpGet ("{id}")]
simple solution

Is it possible to customize the ServiceStack /metadata page?

I run my site behind a loadbalancer on a non-standard port. When loading up the /metadata page it has my public domain name yet the local port that the app is hosted on, which causes the links to the different formats to break as well.
Example:
in the browser: http://mydomain.com/api/metadata
in metadata page: http://mydomain.com:10000/api/metadata
Is there a way to customize these links in the output? Further, is it possible to customize the other text/css/etc of the page, such that it can be modified to fit into the template I use for the rest of my site?
v4 Update
v4 ServiceStack provides a number of new ways to customize the built-in Metadata pages:
Using the Virtual File System
ServiceStack's Virtual FileSystem by default falls back (i.e when no physical file exists) to looking for Embedded Resource Files inside dlls.
You can specify the number and precedence of which Assemblies it looks at with Config.EmbeddedResourceSources which by default looks at:
The assembly that contains your AppHost
ServiceStack.dll
The VFS now lets you completely replace built-in ServiceStack metadata pages and templates with your own by simply copying the metadata or HtmlFormat Template files you want to customize and placing them in your folder at:
/Templates/HtmlFormat.html // The auto HtmlFormat template
/Templates/IndexOperations.html // The /metadata template
/Templates/OperationControl.html // Individual operation template
Registering links on the Metadata pages
You can add links to your own Plugins in the metadata pages with:
appHost.GetPlugin<MetadataFeature>()
.AddPluginLink("swagger-ui/", "Swagger UI");
appHost.GetPlugin<MetadataFeature>()
.AddDebugLink("?debug=requestinfo", "Request Info");
AddPluginLink adds links under the Plugin Links section whilst AddDebugLink can be used by plugins only available during debugging or development.
Using Metadata Attributes
Many of the same attributes used in Swagger are also used in the metadata pages, e.g:
[Api("Service Description")]
[ApiResponse(HttpStatusCode.BadRequest, "Your request was not understood")]
[ApiResponse(HttpStatusCode.InternalServerError, "Oops, something broke")]
[Route("/swagger/{Name}", "GET", Summary = #"GET Summary", Notes = "GET Notes")]
[Route("/swagger/{Name}", "POST", Summary = #"POST Summary", Notes = "Notes")]
public class MyRequestDto
{
[ApiMember(Name="Name", Description = "Name Description",
ParameterType = "path", DataType = "string", IsRequired = true)]
[ApiAllowableValues("Name", typeof(Color))] //Enum
public string Name { get; set; }
}
Older v3 Notes
ServiceStack's Metadata page allows limited customization via the EndpointHostConfig config settings (where all of ServiceStack configuration lives). E.g. you can change the Home page Body HTML and the Operations Page HTML in your AppHost with:
SetConfig(new EndpointHostConfig {
MetadataPageBodyHtml = "<p>HTML you want on the home page</p>",
MetadataOperationPageBodyHtml = "<p>HTML you want on each operation page</p>"
});
You can also add further metadata documentation for each Web Service by using the attributing the Request DTO with the [Description] attribute as done in the MoviesRest Example project:
[Description("GET or DELETE a single movie by Id. POST to create new Movies")]
[RestService("/movies", "POST,PUT,PATCH,DELETE")]
[RestService("/movies/{Id}")]
public class Movie
{
public int Id { get; set; }
public string ImdbId { get; set; }
public string Title { get; set; }
}
And what it looks like on the MoviesRest /metadata page.
The Service Stack Metadata page is fully customizable. You can use attributes to annotate specific properties or services whilst retaining the auto-generated content.
The content is delivered using embedded HTML templates and this can also be replaced for detailed customization.
The metadata pages can be completely turned off, which is what I typically do, using the following snippet.
SetConfig(new HostConfig {
EnableFeatures = Feature.All.Remove(Feature.Metadata) });
Further details can be found from Service Stack Wiki.
Look here for insights. It is not how to customize /metadata page, but more how to use XHTML to do the same through Hypermedia APIs.