While returning Json Response from .NET Core 6 Web APIs, I want to encode all strings with WebUtility.HtmlEncode. As of now, I am writing this line for each string and I just came across this link: https://stackoverflow.com/a/44117929/3234665.
Is there anyway we can declare this at some central/ global level (i.e. Attribute, Program.cs etc?) to reduce repetitive lines everywhere?
For globally add the custom ContractResolver,you need add the configuration in your Program.cs like below :
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllersWithViews().AddNewtonsoftJson();
//add this service to container...
JsonConvert.DefaultSettings = () => new JsonSerializerSettings
{
ContractResolver = new CustomResolver(),
Formatting= Formatting.Indented,
DefaultValueHandling= DefaultValueHandling.Ignore, //add this
};
Related
I have a .NET Core 3.0 Console application (although I plan to change this to a Blazor app later on).
In my Program class's public static void Main method, I am configuring dependency injection using the pattern described by Microsoft here.
In particular, I want to register a handful of HttpClient instances using the "named clients" pattern described here.
It's all fine except for this one problem: My second and third HttpClient instances should have a DefaultRequestHeader that passes a session Id. But I won't know the value of the session Id until I execute an API command to log in to a remote server using the first HttpClient instance. What should I do?
My code so far is below.
Can I somehow get a reference to the first HttpClient instance and call my Login method from inside this block of code? Or can I call the Login method later from a different block of code and then add the appropriate DefaultRequestHeader to the other HttpClient instances long after they have been instantiated?
public class Program
{
public static void Main()
{
var builder = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddUserSecrets<Program>()
.AddEnvironmentVariables(); ;
IConfigurationRoot configuration = builder.Build();
var settings = new ApplicationOptions();
configuration.GetSection("AppSettings").Bind(settings);
var services = new ServiceCollection();
// This is the client I'll use to log in and get a session token
services.AddHttpClient("Authentication", c =>
{
c.BaseAddress = new Uri(settings.AuthenticationApi);
c.DefaultRequestHeaders.Accept.Clear();
c.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
c.DefaultRequestHeaders.Add("X-Application", settings.AppKey);
c.DefaultRequestHeaders.Add("User-Agent", "My API Client v0.0.1");
}).ConfigurePrimaryHttpMessageHandler(() =>
{
return GetMessageHandlerWithSecurityCertificate(settings);
});
// and this is the next of several clients where I'll need to send the session token with my requests
services.AddHttpClient("AnotherApi", c =>
{
c.BaseAddress = new Uri(settings.AnotherApi);
c.DefaultRequestHeaders.Accept.Clear();
c.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
c.DefaultRequestHeaders.Add("X-Application", settings.AppKey);
c.DefaultRequestHeaders.Add("User-Agent", "My API Client v0.0.1");
//c.DefaultRequestHeaders.Add("ssoid", sessionToken);
// What should I do here? I want to add a sessionToken string but I can only get the token's value by using the other HttpClient (above) to log in!
});
// Removed one more HttpClient for brevity
}
private static HttpMessageHandler GetMessageHandlerWithSecurityCertificate(ApplicationOptions settings)
{
var handler = new HttpClientHandler();
handler.ClientCertificates.Add(GetSecurityCertificate(settings.SslCertificateFilename, settings.SslCertificatePassword));
return handler;
}
private static X509Certificate2 GetSecurityCertificate(string certFilename, string certPassword)
{
return new X509Certificate2(certFilename, certPassword);
}
}
Can you not use the IHttpClientFactory directly and generate an HttpClient at the time you need it using your credentials? In other words, instead of trying to register all of the individual HttpClient(s), register the IHttpClientFactory as normal. Inject that into the class thats going to need the HttpClient, and just create it with your credentials there? Thats what I would do. After all, the IHttpClientFactory is simply a wrapper class which generates your HttpClients using the factory pattern.
Additionally: At a stretch you could use an extension method to configure your HttpClient with preset values to save duplicating code everywhere for consistent stuff.
ASP.NET Core 2.2, ASP.Net Boilerplate version 4.1, Modul Zero Core version 4.4.0 is used.
The API accepts JSON objects as input and returns JSON objects. I was faced with the task of ensuring the possibility of using XML in conjunction with JSON.
I tried it like this:
// file RootNs.Web.Mvc Startup.cs
public IServiceProvider ConfigureServices(IServiceCollection services) {
// ...
services.AddMvc(options => {
options.Filters.Add(new AutoValidateAntiforgeryTokenAttribute());
options.ReturnHttpNotAcceptable = true;
options.OutputFormatters.RemoveType<HttpNoContentOutputFormatter>();
options.RespectBrowserAcceptHeader = true;
options.OutputFormatters.Add(new XmlSerializerOutputFormatter());
// so also tried
// options.OutputFormatters.Add(new XmlDataContractSerializerOutputFormatter());
options.FormatterMappings
.SetMediaTypeMappingForFormat("xml", MediaTypeHeaderValue.Parse("application/xml"));
})
}
After that, I decorated the method in the API with the FormatFilter attribute. But it did not help.
How to add support for XML output formater? In which project (*.Web.Mvc or *.Web.Host) should the file Startup.cs be edited?
I'm trying to manually define some json settings for my responses in an ASP.NET MVC Core web app. Given the following code, it does not work:
services.AddMvcCore()
... <snip a few services>
.AddJsonFormatters(options => options = JsonHelpers.JsonSerializerSettings);
and
public static JsonSerializerSettings JsonSerializerSettings
{
get
{
var settings = new JsonSerializerSettings
{
ContractResolver = new CamelCasePropertyNamesContractResolver(),
Formatting = Formatting.None,
NullValueHandling = NullValueHandling.Include,
DateFormatHandling = DateFormatHandling.IsoDateFormat
};
settings.Converters.Add(new StringEnumConverter());
return settings;
}
}
I'm assuming that I need to manually specify each setting, during setup. Not define the new instance of the settings instead. (I'll test this in a sec).
e.g.
services.AddMvcCore()
.AddJsonFormatters(o =>
{
o.ContractResolver = new CamelCasePropertyNamesContractResolver();
o.NullValueHandling = NullValueHandling.Ignore;
});
So, why can't I use my instance instead of manually setting each property? My initial reasoning was that I uses these json settings in OTHER parts of my system, so I wanted to have one place to define the settings and any other places use that same instance. This way, I don't need to manually update multiple locations in the code.
I have following code inside Startup.cs and expecting it to override default serialization options. I want it to override every single serialization throughout my asp net core 2.0 project, but action return value that is not correct, I think this global property is not working in core 2.0
I have it written inside Configure exactly before app.UseMvc();
JsonConvert.DefaultSettings = () => new JsonSerializerSettings
{
Formatting = Formatting.Indented,
TypeNameHandling = TypeNameHandling.Objects,
ContractResolver = new CamelCasePropertyNamesContractResolver(),
Converters = new List<JsonConverter> { new StringEnumConverter() }
};
In ASP.NET Core, this is configured when wiring up the services on the application in Startup.ConfigureServices. There is an fluent AddJsonOptions(Action<MvcJsonOptions>) extension to the IMvcBuilder returned by the AddMvc() extension. MvcJsonOptions exposes a SerializerSettings property which you can configure in your action code.
So instead of configuring once before registering MVC, it's done as part of the MVC registration.
Example incorporating your setup:
services.AddMvc()
.AddJsonOptions( options =>
{
options.SerializerSettings.Formatting = Formatting.Indented;
options.SerializerSettings.TypeNameHandling = TypeNameHandling.Objects;
options.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
options.SerializerSettings.Converters.Add(new StringEnumConverter());
});
I have a custom route that reads URLs from a no-SQL database (MongoDB) and add them to the route pipeline at moment that the application starts, which is "pretty standard"
something like this (in my startup.cs file):
app.UseMvc(routes =>
{
routes.MapRoute(
"default",
"{controller=Home}/{action=Index}/{id?}");
routes.Routes.Add(
new LandingPageRouter(routes, webrequest, memoryCache, Configuration));
// this custom routes adds URLs from database
}
the issue is that if I add another route to the database after the application has started I basically get a 404 since the routing system isn't aware of this new route, I think that what I need is add the missing routes at runtime or (less convenient) restart the application pool from another web application (which has been developed on framework 4.5 and obviously it runs on a different pool)
Any other thoughts on this?
thanks.
The first question is what does database mean when you say: I add another route to the database and wether you can keep your routes in a JSON, XML or INI file.
If you can, for example, keep the routes in a JSON file, then there is possible for the routes to be dynamically available on runtime (as you can read in the ASP.NET Core Documentation)
You can find a full blog post about this here.
Assuming that routes.json is a JSON file with structure similar to the following, and is in the same folder as Startup:
{
"route": "This is a route",
"another-route": "This is another route",
"default": "This is default!"
}
You can configure the Startup class in the following way:
Note that this example does not use MVC but the separate routing package, although I assume you can transpose it to work with MVC.
public class Startup
{
public IConfiguration Configuration {get;set;}
public Startup(IHostingEnvironment env)
{
var configurationBuilder = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("routes.json", optional: false, reloadOnChange: true);
Configuration = configurationBuilder.Build();
}
public void ConfigureServices(IServiceCollection services)
{
services.AddRouting();
}
public void Configure(IApplicationBuilder app)
{
var routeBuilder = new RouteBuilder(app);
routeBuilder.MapGet("{route}", context =>
{
var routeMessage = Configuration.AsEnumerable()
.FirstOrDefault(r => r.Key == context.GetRouteValue("route")
.ToString())
.Value;
var defaultMessage = Configuration.AsEnumerable()
.FirstOrDefault(r => r.Key == "default")
.Value;
var response = (routeMessage != null) ? routeMessage : defaultMessage;
return context.Response.WriteAsync(response);
});
app.UseRouter(routeBuilder.Build());
}
}
At this point, while the application is running, you can modify the JSON file, save it, then because of the reloadOnChange: true parameter, the framework will reinject the new configuration into the Configuration property.
The implementation of the reload is based on a file watcher, so if you want to use a database for this - a SQL Server, then I think you have to implement this yourself.
A (not pretty at all and reminded here just for completeness) solution could be to create a console application that adds database changes in a file, then use that file in the application configuration.
Best regards!
In ASP.NET Core 3 you can use Dynamic Routing. In Startup.cs add:
app.UseEndpoints(endpoints =>
{
endpoints.MapDynamicControllerRoute<SearchValueTransformer>("{**url}");
});
And create new class SearchValueTransformer
class SearchValueTransformer : DynamicRouteValueTransformer
{
public override async ValueTask<RouteValueDictionary> TransformAsync(HttpContext httpContext, RouteValueDictionary values)
{
var url = values["url"] as string;
if (String.IsNullOrEmpty(url))
return values;
values["controller"] = "Controller";
values["action"] = "Action";
values["name"] = url;
return values;
}
}
Also in method TransformAsync you can search in your MongoDB for proper Controller, Action and Name values. More info: https://weblogs.asp.net/ricardoperes/dynamic-routing-in-asp-net-core-3