NewtonsoftJsonInputFormatter is not available as InputFormatter in .NET5 - asp.net-core

I have NET5 application and in statup I have configured the application to use Newtonsoft instead of System.Text.Json. For posting CSP report I want to add application/csp-report as a supported media type.
Even though I have configured to use Newtonsoft using AddNewtonsoftJson, the NewtonsoftJsonInputFormatter is still not available as inputformatter. The following code returns null
when try to find in InputFormatters collection.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews(config =>
{
var jsonInputFormatter = options.InputFormatters
.OfType<NewtonsoftJsonInputFormatter>()
.First();
//jsonInputFormatter is null here
jsonInputFormatter.SupportedMediaTypes.Add("application/csp-report")
})
// Use Newtonsoft’s Json.NET instead of System.Text.Json.
.AddNewtonsoftJson((options)=>
{
options.SerializerSettings.ContractResolver = new DefaultContractResolver();
})
}

based on the post here . The accepted answer in that post did not work for me. However the other suggested work around by #Vincent Rutten did work
services.AddOptions<MvcOptions>()
.PostConfigure<IOptions<JsonOptions>, IOptions<MvcNewtonsoftJsonOptions>, ArrayPool<char>, ObjectPoolProvider, ILoggerFactory>(
(mvcOptions, jsonOpts, newtonJsonOpts, charPool, objectPoolProvider, loggerFactory) =>
{
var formatter = mvcOptions.InputFormatters.OfType<NewtonsoftJsonInputFormatter>().First(i => i.SupportedMediaTypes.Contains("application/json"));
formatter.SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("application/csp-report"));
mvcOptions.InputFormatters.RemoveType<NewtonsoftJsonInputFormatter>();
mvcOptions.InputFormatters.Add(formatter);
});

Related

Blazor Server Side Localization without cookie

So Im trying to add Localization without a cookie to my Blazor Server Side project.
In the documentaition https://learn.microsoft.com/en-us/aspnet/core/blazor/globalization-localization?view=aspnetcore-5.0#provide-ui-to-choose-the-culture it says:
Use of a cookie ensures that the WebSocket connection can correctly propagate the culture. If localization schemes are based on the URL path or query string, the scheme might not be able to work with WebSockets, thus fail to persist the culture. Therefore, use of a localization culture cookie is the recommended approach.
So I know its not recommended, but I would like it to be path-based anyway if possible. Can it be done with the standard services.AddLocalization(); and IStringLocalizer, or do I have to build a custom one?
For Localization, I prefer a direct, clear, and readable link that contains the language's first 2 letters (like: mysite.com/en/mypage ), which allows having the correct link using exactly the language needed.
The only solution I found that works perfectly with Blazor Server without the need for Cookies was using the package "BlazorServerUrlRequestCultureProvider" created by "Pier-Luc Bonneville"; it is effortless to use and gives excellent results.
Usage:
Install the package BlazorServerUrlRequestCultureProvider (worked perfectly for me with .Net 5.0):
Install-Package BlazorServerUrlRequestCultureProvider -Version 1.0.0
Startup.cs:
using BlazorServerUrlRequestCultureProvider;
// ...
//------------------------------
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
// ...
// Set the Resources folder name:
services.AddLocalization(options => options.ResourcesPath = "Resources");
// ...
}
}
//------------------------------
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
}
app.UseHttpsRedirection();
app.UseStaticFiles();
#region Localization
var supportedCultures = new[]
{
new CultureInfo("en"),
new CultureInfo("ar"),
};
var options = new RequestLocalizationOptions
{
// Select a default language:
DefaultRequestCulture = new RequestCulture("en"),
// For Numbers, Dates, etc:
SupportedCultures = supportedCultures,
// For strings that we have localized in .resx files:
SupportedUICultures = supportedCultures
};
app.UseUrlRequestLocalization(options);
#endregion
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapBlazorHub();
endpoints.MapFallbackToPage("/_Host");
});
app.UseRequestLocalization();
}
That was just all you need! No Cookies nor Controllers!
Now use StringLocalizer as usual (in my case, the resource files names were App.resx and App.ar.resx inside the Resource folder):
MyPage.razor:
#page "/ar/MyPage"
#page "/en/MyPage"
#inject IStringLocalizer<App> localizer
<h1>#localizer["My Translated Text Here !"]</h1>
All Thanks & Credits goes to "Pier-Luc Bonneville" for his wonderful work. Project Website , NuGet .
--
Bonus:
To make your life much easier, use the tool ResXManager to easily manage and translate resource files, it's a free and great tool, and it will save you a lot of time.

Getting 'TypeError: Failed to fetch' error when trying to call AspNetCore Restful API from Blazor Wasm

I'm getting a 'TypeError: Failed to fetch' error when trying to call my AspNetCore Restful API from Blazor Wasm. I can call it from Postman, and it works fine.
My Environment:
Microsoft Visual Studio Community 2019 Preview Version 16.6.0 Preview 3.0
Client: Blazor Wasm Service (dotnetstandard 2.1)
AspNet.WebApi.Client 5.2.7
AspNetCore..WebAssembly 3.2 preview 4.2
System.Net.Http.Json 3.2 preview 5.2
Important Usings:
using Microsoft.AspNetCore.JsonPatch;
using Newtonsoft.Json;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Net.Http.Json;
Parent namespace and class omitted
_httpClient is injected into parent class
public async Task<MyDto> UpdatePartialAsync(Guid primaryId, ObjectForUpdateDto objectForUpdateDto)
{
MyDto dtoFromApi = null;
var patchDoc = new JsonPatchDocument<ObjectForUpdateDto>()
.Replace(o => o.Name, objectForUpdateDto.Name)
.Replace(o => o.Description, objectForUpdateDto.Description)
var uri = $"MyUri/myResources/{primaryId}";
try
{
_httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var serializedPatchDoc = JsonConvert.SerializeObject(patchDoc);
var json = new StringContent(serializedPatchDoc, Encoding.UTF8, "application/json-patch+json");
var response = await _httpClient.PatchAsync(uri, json);
return await response.Content.ReadAsAsync<MyDto>();
}
catch (Exception)
{
throw; //throws 'TypeError: Failed to fetch'
}
return dtoFromApi;
}
My API (.Net 5.0, also tried .Net Core 3.1):
[HttpPatch]
[Route("{primaryId}")]
public ActionResult UpsertPartial([FromRoute]Guid primaryId, [FromBody] JsonPatchDocument<ObjectForUpdateDto> objectForUpdateDto)
{
//client call never makes it here
return NoContent();
}
What a misleading error message. It was a CORS issue.
The fix was adding "PATCH" to my CORS policy in my API's startup.cs ConfigureServices method (which previously was "GET, DELETE, PUT, POST, OPTIONS").
services.AddCors(options =>
{
options.AddPolicy(CorsAllowAll,
builder =>
{
builder.WithOrigins(Constants.ApiClientCors).AllowAnyHeader().WithMethods("GET, PATCH, DELETE, PUT, POST, OPTIONS");
});
});
#inliner49er, I wish that I could add a comment to clarify what you responded, since your answer is correct, but I don't have enough reputation points. Therefore, I'll post my tweaks to your answer as a separate answer.
You nailed it, the CORS issue fixed my program also. The only part of your code that didn't make sense was the reference that you have to a class called Constants. I am in the process of trying to complete the PluralSight tutorial, and because I'm working entirely internally, I can safely replace your code with the following:
services.AddCors(options =>
{
options.AddPolicy("PolicyName", builder => builder.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod());
});
I am super new to all of this, literally hours into the process of learning about it, so there is a buttload that I don't understand. I just thought I'd post this to help anyone who might have similar questions to what I had.
You can also try adding these lines directly to the Configure method of the Startup class:
//ENABLE CORS
app.UseCors(x => x
.AllowAnyMethod()
.AllowAnyHeader()
.SetIsOriginAllowed(origin => true) // allow any origin
.AllowCredentials()); // allow credentials

GetRequiredService from within Configure

I'm trying to access one of my services from within the Configure call within Startup.cs in aspnet core. I'm doing the following however I get the following error "No service for type 'UserService' has been registered." Now I know it is registered because I can use it in a controller so I'm just doing something wrong when it comes to using it here. Please can someone point me in the right direction. I'm happy with taking a different approach to setting up Tus if there's a better way of achieving what I want.
var userService = app.ApplicationServices.GetRequiredService<UserService>();
userService.UpdateProfileImage(file.Id);
The below is where I'm wanting to use
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
... Other stuff here...
app.InitializeSimpleInjector(container, Configuration);
container.Verify();
app.UseTus(httpContext =>
{
var restaurantEndpoint = "/restaurant/images";
var userEndpoint = "/account/images";
var endPoint = "/blank/images";
if (httpContext.Request.Path.StartsWithSegments(new PathString(restaurantEndpoint)))
{
endPoint = restaurantEndpoint;
}
if (httpContext.Request.Path.StartsWithSegments(new PathString(userEndpoint)))
{
endPoint = userEndpoint;
}
return new BranchTusConfiguration
{
Store = new TusDiskStore(#"C:\tusfiles\"),
UrlPath = endPoint,
Events = new Events
{
OnBeforeCreateAsync = ctx =>
{
return Task.CompletedTask;
},
OnCreateCompleteAsync = ctx =>
{
return Task.CompletedTask;
},
OnFileCompleteAsync = async ctx =>
{
var file = await ( (ITusReadableStore)ctx.Store ).GetFileAsync(ctx.FileId, ctx.CancellationToken);
var userService = app.ApplicationServices.GetRequiredService<UserService>();
userService.UpdateProfileImage(file.Id);
}
}
};
});
... More stuff here...
};
My end goal is to move this to an IApplicationBuilder extension to clean up my startup.cs but that shouldn't affect anything if it's working from within startup.cs
Edit: Add to show the registration of the userService. There is a whole lot of other stuff being registered and cross wired in the InitializeSimpleInjector method which I've left out. can add it all if need be..
public static void InitializeSimpleInjector(this IApplicationBuilder app, Container container, IConfigurationRoot configuration)
{
// Add application presentation components:
container.RegisterMvcControllers(app);
container.RegisterMvcViewComponents(app);
container.Register<UserService>(Lifestyle.Scoped);
container.CrossWire<IServiceProvider>(app);
container.Register<IServiceCollection, ServiceCollection>(Lifestyle.Scoped);
}
Please read the Simple Injector integration page for ASP.NET Core very closely, as Simple Injector integrates very differently with ASP.NET Core as Microsoft documented how DI Containers should integrate. The Simple Injector documentation states:
Please note that when integrating Simple Injector in ASP.NET Core, you do not replace ASP.NET’s built-in container, as advised by the Microsoft documentation. The practice with Simple Injector is to use Simple Injector to build up object graphs of your application components and let the built-in container build framework and third-party components
What this means is that, since the built-in container is still in place, resolving components using app.ApplicationServices.GetRequiredService<T>()—while they are registered in Simple Injector—will not work. In that case you are asking the built-in container and it doesn't know about the existence of those registrations.
Instead, you should resolve your type(s) using Simple Injector:
container.GetInstance<UserService>()

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

Swagger .Net Core

I am trying to configure swagger for my .Netcore App (1.1) and couldnt generate the docs.
Here is my configuration
public void ConfigureServices(IServiceCollection services) {
services.AddMvcCore().AddVersionedApiExplorer(o => o.GroupNameFormat = "1.0");
services.AddMvc();
services.AddApiVersioning(opt =>
{
opt.ApiVersionReader = new HeaderApiVersionReader("api-version");
opt.DefaultApiVersion = new ApiVersion(1, 0);
opt.ReportApiVersions = true;
opt.AssumeDefaultVersionWhenUnspecified = true;
});
services.AddSwaggerGen(
options =>
{
options.SwaggerDoc("1.0",new Info {Contact = new Contact() {Name="Admin" } });
// add a custom operation filter which sets default values
options.OperationFilter<SwaggerDefaultValues>();
});
}
In the Configure Method
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=User}/{action=Get}/{requestString?}");
});
app.UseSwagger(o=>
{
o.RouteTemplate = "docs/{documentName}/swagger.json";
});
app.UseSwaggerUI(
options =>
{
options.SwaggerEndpoint("/docs/1.0/swagger.json", "1.0");
});
When I run the application,
http://localhost:5000/docs/1.0/swagger.json
I am getting the below methods, None of my API's are discovered.
{"swagger":"2.0","info":{"contact":{"name":"Admin"}},"basePath":"/","paths":{},"definitions":{},"securityDefinitions":{}}
OK, I have reproduced your problem and found that the reason is the value in GroupNameFormat option.
If quickly, instead of const version string you should specify version format. As you want to have version in url as 1.0 you may use:
services.AddMvcCore().AddVersionedApiExplorer( o => o.GroupNameFormat = "VVVV" );
From Version Format section in Documentation:
Format Specifier: VVVV
Description: Major, minor version, and status
Examples: 1-RC -> 1.0-RC, 1.1 -> 1.1, 1 -> 1.0
Regarding AddMvcCore() vs AddMvc():
From the swagger docs at https://github.com/domaindrivendev/Swashbuckle.AspNetCore
Swashbuckle relies heavily on ApiExplorer, the API metadata layer that ships with ASP.NET Core. If you're using the AddMvc helper to bootstrap the MVC stack, then ApiExplorer will be automatically registered and SB will work without issue. However, if you're using AddMvcCore for a more paired-down MVC stack, you'll need to explicitly add the Api Explorer service:
services.AddMvcCore().AddApiExplorer();
If you also want AddVersionedApiExplorer(), chain that after AddApiExplorer()