REST services should provide content negotiation. This means that clients send an Accept header that contains the desired content type of the response. If the service does not support this media type, it should respond with status code 406 (Not Acceptable).
I try to map this behavior to ASP.NET Core. ASP.NET core returns a JSON document, if it doesn't recognize the media type in the Accept header. In previous versions of the framework the behavior described above could be achieved by adding a special output formatter to the configuration:
public void ConfigureServices(IServiceCollection services) {
services.AddMvc(options => {
options.OutputFormatters.Insert(0, new HttpNotAcceptableOutputFormatter());
});
}
Unfortunately, HttpNotAcceptableOutputFormatter was removed from the ASP.NET Core framework after RC1. Is there any replacement for this class in the current version of the framework?
I had this before:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
}
Then I change it to AddMvcCore() instead of AddMvc()
public void ConfigureServices(IServiceCollection services)
{
services.AddMvcCore();
}
Finally I had that issue with the Response 406 so what I did was to add .AddJsonFormatters() to services.AddMVCCore() and my API worked again.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvcCore()
.AddJsonFormatters();
}
In such cases, it’s a good idea to find the commit that removed the functionality, to see what it likely got replaced with. In this case, HttpNotAcceptableOutputFormatter was removed with this commit to fix issue #4612:
Alter content negotiation algorithm so that it can be configured (via MvcOptions) to always respect an explicit Accept header.
What it was replaced with is MvcOptions.ReturnHttpNotAcceptable, which is a setting on the MvcOptions that you configure when adding MVC with AddMvc.
So your code should become like this:
services.AddMvc(options =>
{
options.ReturnHttpNotAcceptable = true;
});
You add this to the ConfigureService method in the Startup class.
services.AddMvc(options =>
{
options.ReturnHttpNotAcceptable = true;
// If you need to add support for XML
// options.OutputFormatters.Add(new XmlDataContractSerializerOutputFormatter());
});
None of the above answer worked for me , finally this worked
Adding the following line in ConfigureServices of Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddMvcCore().AddJsonFormatters().AddApiExplorer();
}
Related
I'm trying to create a Web-Api based on asp.net core. My requirement is that, it should support xml serialization by default and not json.
I added
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1)
.AddXmlSerializerFormatters();
But still, the default serializer is json.
I need it to return xml without the client having to specify
{"Accept":"application/xml"}
As said here, you must force your application to produce XML instead of JSON:
services.AddMvc(opt =>
{
opt.Filters.Add(new ProducesAttribute("application/xml"));
}).SetCompatibilityVersion(CompatibilityVersion.Version_2_2)
.AddXmlSerializerFormatters();
For changing default serializer, you could try
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc(options => {
options.OutputFormatters.Insert(0, new XmlDataContractSerializerOutputFormatter());
}).SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
}
In my ASP.Net Core 2.2 setup I have a method to create an "allow all" CORS policy
public static IServiceCollection AddAllowAllCors(this IServiceCollection services)
{
services.AddCors(options =>
{
options.AddPolicy("AllowAll", builder =>
{
builder.AllowAnyHeader()
.AllowAnyMethod()
.AllowCredentials()
.SetIsOriginAllowed(_ => true);
});
});
return services;
}
Which is added in ConfigureServices:
public void ConfigureServices(IServiceCollection services)
{
...
services.AddAllowAllCors();
...
}
Which I activate in my Configure method:
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
...
app.UseCors("AllowAll");
...
}
When I try to make a PUT request from my React app, the OPTIONS request looks like this in Chrome:
But the actual PUT request fails with a 405:
This is despite the fact that the Access-Control-Allow-Origin header in the OPTIONS response to be allowed. This worked in 2.1 but doesn't in 2.2. The exact error message is:
Access to fetch at 'MY_REQUEST_URI' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
I've also tried removing AllowCredentials() in the policy but that didn't make a difference.
Looks like your CORS configuration is ok. But there is no actual PUT handler. You should check your API with some kind of rest client like postman or ARC first of all and then think about CORS.
A combination of turning off the new Endpoint Routing feature and disabling the WebDav module in IIS fixed this issue for me. I believe that because of Endpoint Routing, my preflight requests were actually being handled by the WebDav module and not by my app.
To turn off Endpoint Routing, set the flag to false in .AddMvc():
services.AddMvc(options =>
{
...other options
options.EnableEndpointRouting = false;
})
public static IServiceCollection AddAllowAllCors(this IServiceCollection services)
{
services.AddCors(options =>
{
options.AddPolicy("AllowAll", builder =>
{
builder.AllowAnyHeader()
.AllowAnyMethod()
.AllowCredentials()
.AllowAnyOrigin();
});
});
return services;
}
I have Lamar set up in my .NET Core 2 project:
public class Program
{
public static void Main(string[] args)
{
IWebHost webhost = CreateWebHostBuilder(args).Build();
//((Container)webhost.Services).GetInstance<IStart>().Run();
webhost.Run();
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseLamar()
.UseStartup<Startup>();
}
...
public class Startup
{
...
public void ConfigureContainer(ServiceRegistry services)
{
services.Configure<Configuration.Auth>("auth", Configuration);
...
services.Scan(s =>
{
s.TheCallingAssembly();
s.WithDefaultConventions();
});
services.AddCors();
services.AddMvc()
.AddJsonOptions(o =>
{
o.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
o.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
})
.SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
services.AddDbContext<Context>(options => options.UseSqlServer(Configuration.GetConnectionString("defaultConnection")));
}
}
However, when attempting to use Scaffold API Controller with actions, using Entity Framework I run into the following error:
There was an error running the selected code generator: 'No parameterless constructor defined for this object.'
Looking up this https://learn.microsoft.com/en-us/aspnet/core/migration/1x-to-2x/?view=aspnetcore-2.2#update-main-method-in-programcs suggested that this can show up in ASP.NET Core 2 projects that attempt to still use the .NET 1.x structure.
I found a hacky work-around that I'll post below, which suggests that the scaffolding code generation may have an issue with Lamar. However, is there a better solution? Can you set up Lamar to be able to handle Entity Framework Code generation?
Considering EF was failing in the generate code section, I wondered if perhaps the issue was not the parameterless constructor (I'm pretty sure that whatever unnamed object it was referring to actually has one) but the issue with how the WebHost gets built when using Lamar.
The note in the Lamar documentation on integrating with ASP.NET Core states
Note! The Startup.ConfigureServices(ServiceRegistry) convention does not work as of ASP.Net Core 2.1. Use ConfigureContainer(ServiceRegistry) instead.
I was using that Lamar function in my Startup; however, if I changed it back to ConfigureContainer(IServiceCollection services) (and commented out the Lamar-specific functions, such as Scan), I found that I was able to scaffold the EF controller again.
So, at the moment, my workaround is to comment out Lamar before scaffolding, and then uncomment it back once I'm done. I suspect there may be a better solution though...
I'm developing a REST Api using ASP.NET Core. I want to force the application to produce JSON responses which I can achive decorating my controllers with the "Produces" attribute. Example:
[Produces("application/json")]
public class ProductsController : ControllerBase
{
...
}
But according to this article: https://learn.microsoft.com/en-us/aspnet/core/mvc/models/formatting the filter can be applied globally, but I can't really figure out how.
Is there anyone out there that could provide a simple example of how to apply the "Produces" filter globally?
The linked documentation tells it already, you just have to read carefully and follow the link ;)
See Filters to learn more, including how to apply filters globally.
and when you follow the link you find an sample:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc(options =>
{
options.Filters.Add(typeof(SampleActionFilter)); // by type
options.Filters.Add(new SampleGlobalActionFilter()); // an instance
});
services.AddScoped<AddHeaderFilterWithDi>();
}
In your case:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc(options =>
{
options.Filters.Add(new ProducesAttribute("application/json"));
});
}
I am learning ASP.NET Core MVC from a book, the code snippet in question is as follows:
// CHAPTER 4 - ESSENTIAL C# FEATURES
namespace LanguageFeatures {
public class Startup {
public void ConfigureServices(IServiceCollection services) {
services.AddMvc();
}
// etc.
Because the book is about ASP.NET Core MVC rather than ASP.NET MVC, I think I have to use AddMvcCore() rather than AddMvc() as follows:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvcCore(); // as opposed to:
//services.AddMvc();
}
Is what I do here correct?
Have a look at the MvcServiceCollectionExtensions.cs class on the ASP.NET Core GitHub repo:
public static IMvcBuilder AddMvc(this IServiceCollection services)
{
if (services == null)
{
throw new ArgumentNullException(nameof(services));
}
var builder = services.AddMvcCore();
builder.AddApiExplorer();
builder.AddAuthorization();
AddDefaultFrameworkParts(builder.PartManager);
// Order added affects options setup order
// Default framework order
builder.AddFormatterMappings();
builder.AddViews();
builder.AddRazorViewEngine();
builder.AddRazorPages();
builder.AddCacheTagHelper();
// +1 order
builder.AddDataAnnotations(); // +1 order
builder.AddCors();
return new MvcBuilder(builder.Services, builder.PartManager);
}
AddMvcCore() and AddMvc() both return an IMvcBuilder that can be used to further configure the MVC services.
AddMvcCore(), as the name implies, only adds core components of the MVC pipeline, requiring you to add any other middleware (needed for your project) by yourself.
AddMvc() internally calls AddMvcCore() and adds other middleware such as the Razor view engine, Razor pages, CORS, etc.
For now, I would follow what your tutorial suggests and stick to AddMvc().
As of ASP.NET Core 3.0, there are additional methods that give fine-grained control over what portions of the MVC pipeline are available to your application, e.g.:
services.AddControllers()
services.AddControllersWithViews()
Refer to this article and MSDN for more information about what they do and when to use them.