Override WebApiConfig formatting - asp.net-web-api2

In the Register method of my Web API 2 project I've added this bit of code so that the returned JSON is automatically camel cased:
public static void Register(HttpConfiguration config) {
var settings = config.Formatters.JsonFormatter.SerializerSettings;
settings.ContractResolver = new CamelCasePropertyNamesContractResolver();
settings.Formatting = Formatting.Indented;
I have one or two methods, though, where I do not want it to do that, and really want the casing to be left alone. From an individual route's method is there a way to override that?
I have hundreds of methods that want it, and just a couple that don't.

You can try something like below to bypass Global Formatters settings,
public HttpResponseMessage Get()
{
Person content = new Person() { PersonID = 1, PersonName = "name" };
HttpResponseMessage resposne = new HttpResponseMessage();
resposne.Content = new ObjectContent(content.GetType(), content, new JsonMediaTypeFormatter());
return resposne;
}

Related

How to use configured JSON options when using a custom InputFormatter?

I would like to replace the default SystemTextJsonInputFormatter with a custom input formatter, either as a sub-class of TextInputFormatter, or (preferably) SystemTextJsonInputFormatter; preferably the latter, to be as close as possible to the built-in ASp.NET Core behaviour without having to duplicate code into my class, which would need to be periodically updated as new ASP.NET Core releases make changes to SystemTextJsonInputFormatter.
There is some relevant info here:
Custom formatters in ASP.NET Core Web API
Which provide this example of how to register a custom input formatter:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers(options =>
{
options.InputFormatters.Insert(0, new VcardInputFormatter());
});
However, I currently also have this customisation of the JSON options, like so:
internal static IServiceCollection ConfigureJsonSerialization(this IServiceCollection services)
{
services.AddControllers().AddJsonOptions(opts => {
// Note. some of these settings are defaults, but we ensure they are set as required.
opts.JsonSerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull;
opts.JsonSerializerOptions.PropertyNameCaseInsensitive = false;
opts.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
// Add our custom converters.
opts.JsonSerializerOptions.Converters.Add(new DateOnlyJsonConverter());
});
return services;
}
opts there is a Microsoft.AspNetCore.Mvc.JsonOptions object.
When using the built-in SystemTextJsonInputFormatter, these options make it into that formatter instance via its constructor (the relevant code appears to be in MvcCoreMvcOptionsSetup).
I would like to use those same JSON options in my custom input formatter, but cannot see how to obtain a JsonOptions at construction time (the options.InputFormatters.Insert(0, new VcardInputFormatter()); line from above.
I will continue to look through ASP.NET Core internals to try and figure out a way, but thought I would ask here in case anyone can offer some guidance.
Thanks.
A somewhat hacky approach is to simply copy the JsonSerializerOptions properties from the existing SystemTextJsonInputFormatter instance provided by ASP.NET Core, like so:
public static class ServiceCollectionExtensions
{
internal static IServiceCollection ConfigureJsonSerialization(this IServiceCollection services)
{
services.AddControllers(
opts => {
// Search for an existing instance of SystemTextJsonInputFormatter in the InputFormatters list;
// this is the instance provided as standard by ASP.NET Core.
int idx = FindSystemTextJsonInputFormatter(
opts.InputFormatters,
out SystemTextJsonInputFormatter? sysTextJsonInputFormatter);
// We expect to find the SystemTextJsonInputFormatter.
if (sysTextJsonInputFormatter is null)
throw new InvalidOperationException("No SystemTextJsonInputFormatter is defined.");
// We will now create our own input formatter, and swap it in to substitute the
// SystemTextJsonInputFormatter instance. First we have some objects to setup.
// Copy the json serializer settings from the existing SystemTextJsonInputFormatter.
var jsonOptions = new JsonOptions();
CopyJsonSerializerOptions(
jsonOptions.JsonSerializerOptions,
sysTextJsonInputFormatter.SerializerOptions);
// TODO/FIXME: Research how best to get a logger for our input formatter!
var loggerFactory = LoggerFactory.Create(builder => {
builder.AddConsole();
});
var logger = loggerFactory.CreateLogger<SystemTextJsonInputFormatter>();
// Perform the substitution.
opts.InputFormatters[idx] = new JsonMergePatchInputFormatter(jsonOptions, logger);
})
.AddJsonOptions(opts => {
// Note. some of these settings are defaults, but we ensure they are set as required.
opts.JsonSerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull;
opts.JsonSerializerOptions.PropertyNameCaseInsensitive = false;
opts.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
// Add our custom converters.
opts.JsonSerializerOptions.Converters.Add(new DateOnlyJsonConverter());
});
return services;
}
private static int FindSystemTextJsonInputFormatter(
FormatterCollection<IInputFormatter> inputFormatters,
out SystemTextJsonInputFormatter? sysTextJsonInputFormatter)
{
// Search for an instance of SystemTextJsonInputFormatter, and store it's index in the InputFormatters list.
// Note. We expect only one instance at most, but if there is more then one then we simply take the first instance.
for (int i = 0; i < inputFormatters.Count; i++)
{
if (inputFormatters[i] is SystemTextJsonInputFormatter formatter)
{
sysTextJsonInputFormatter = formatter;
return i;
}
}
// Not found.
sysTextJsonInputFormatter = null;
return -1;
}
private static void CopyJsonSerializerOptions(JsonSerializerOptions targetOptions, JsonSerializerOptions sourceOptions)
{
// Notes. A hacky but necessary approach, because we are given a pre-built JsonSerializerOptions object
// that can't be replaced, therefore we must copy the various setting into it.
targetOptions.AllowTrailingCommas = sourceOptions.AllowTrailingCommas;
targetOptions.DefaultBufferSize = sourceOptions.DefaultBufferSize;
targetOptions.DefaultIgnoreCondition = sourceOptions.DefaultIgnoreCondition;
targetOptions.DictionaryKeyPolicy = sourceOptions.DictionaryKeyPolicy;
targetOptions.Encoder = sourceOptions.Encoder;
targetOptions.IgnoreNullValues = sourceOptions.IgnoreNullValues;
targetOptions.IgnoreReadOnlyFields = sourceOptions.IgnoreReadOnlyFields;
targetOptions.IncludeFields = sourceOptions.IncludeFields;
targetOptions.MaxDepth = sourceOptions.MaxDepth;
targetOptions.NumberHandling = sourceOptions.NumberHandling;
targetOptions.PropertyNameCaseInsensitive = sourceOptions.PropertyNameCaseInsensitive;
targetOptions.PropertyNamingPolicy = sourceOptions.PropertyNamingPolicy;
targetOptions.ReadCommentHandling = sourceOptions.ReadCommentHandling;
targetOptions.ReferenceHandler = sourceOptions.ReferenceHandler;
targetOptions.UnknownTypeHandling = sourceOptions.UnknownTypeHandling;
targetOptions.WriteIndented = sourceOptions.WriteIndented;
targetOptions.DefaultIgnoreCondition = sourceOptions.DefaultIgnoreCondition;
targetOptions.DefaultIgnoreCondition = sourceOptions.DefaultIgnoreCondition;
// Re-use the same converter instances; this is OK because their current parent
// SystemTextJsonInputFormatter is about to be discarded.
targetOptions.Converters.Clear();
foreach (var jsonConverter in sourceOptions.Converters)
{
targetOptions.Converters.Add(jsonConverter);
}
}
}
I spent some time wading through ASP.NET Core source code and call stacks in an attempt to find a better/cleaner/official way, but ultimately had to give up and go with the above. Please do let me know if there are better solutions.

Can I change my response data in OutputFormatter in ASP.NET Core 3.1

I'm trying to create a simple feature to make the first action act like the second one.
public IActionResult GetMessage()
{
return "message";
}
public IActionResult GetMessageDataModel()
{
return new MessageDataModel("message");
}
First idea came to my mind was to extend SystemTextJsonOutputFormater, and wrap context.Object with my data model in WriteResponseBodyAsync, but the action is marked sealed.
Then I tried to override WriteAsync but context.Object doesn't have protected setter, either.
Is there anyway I can achieve this by manipulating OutputFormatter?
Or I have another option instead of a custom OutputFormatter?
for some reason they prefer every response in a same format like {"return":"some message I write.","code":1}, hence I want this feature to achieve this instead of creating MessageDataModel every time.
Based on your description and requirement, it seems that you'd like to generate unified-format data globally instead of achieving it in each action's code logic. To achieve it, you can try to implement it in action filter, like below.
public class MyCustomFilter : Attribute, IActionFilter
{
public void OnActionExecuted(ActionExecutedContext context)
{
// implement code logic here
// based on your actual scenario
// get original message
// generate new instance of MessageDataModel
//example:
var mes = context.Result as JsonResult;
var model = new MessageDataModel
{
Code = 1,
Return = mes.Value.ToString()
};
context.Result = new JsonResult(model);
}
Apply it on specific action(s)
[MyCustomFilter]
public IActionResult GetMessage()
{
return Json("message");
}

Naming conventions for view pages and setting controller action for view

I am unsure on how I should be naming my View pages, they are all CamelCase.cshtml, that when viewed in the browser look like "http://www.website.com/Home/CamelCase".
When I am building outside of .NET my pages are named like "this-is-not-camel-case.html". How would I go about doing this in my MVC4 project?
If I did go with this then how would I tell the view to look at the relevant controller?
Views/Home/camel-case.cshtml
Fake edit: Sorry if this has been asked before, I can't find anything via search or Google. Thanks.
There are a few ways you can do this:
Name all of your views in the style you would like them to show up in the url
This is pretty simple, you just add the ActionName attribute to all of your actions and specify them in the style you would like your url to look like, then rename your CamelCase.cshtml files to camel-case.cshtml files.
Use attribute routing
Along the same lines as above, there is a plugin on nuget to enable attribute routing which lets you specify the full url for each action as an attribute on the action. It has convention attributes to help you out with controller names and such as well. I generally prefer this approach because I like to be very explicit with the routes in my application.
A more framework-y approach
It's probably possible to do something convention based by extending the MVC framework, but it would be a decent amount of work. In order to select the correct action on a controller, you'd need to map the action name on its way in to MVC to its CamelCase equivalent before the framework uses it to locate the action on the controller. The easiest place to do this is in the Route, which is the last thing to happen before the MVC framework takes over the request. You'll also need to convert the other way on the way out so the urls generated look like you want them to.
Since you don't really want to alter the existing method to register routes, it's probably best write a function in application init that loops over all routes after they have been registered and wraps them with your new functionality.
Here is an example route and modifications to application start that achieve what you are trying to do. I'd still go with the route attribute approach however.
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
WrapRoutesWithNamingConvention(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
AuthConfig.RegisterAuth();
}
private void WrapRoutesWithNamingConvention(RouteCollection routes)
{
var wrappedRoutes = routes.Select(m => new ConventionRoute(m)).ToList();
routes.Clear();
wrappedRoutes.ForEach(routes.Add);
}
private class ConventionRoute : Route
{
private readonly RouteBase baseRoute;
public ConventionRoute(RouteBase baseRoute)
: base(null, null)
{
this.baseRoute = baseRoute;
}
public override RouteData GetRouteData(HttpContextBase httpContext)
{
var baseRouteData = baseRoute.GetRouteData(httpContext);
if (baseRouteData == null) return null;
var actionName = baseRouteData.Values["action"] as string;
var convertedActionName = ConvertHyphensToPascalCase(actionName);
baseRouteData.Values["action"] = convertedActionName;
return baseRouteData;
}
private string ConvertHyphensToPascalCase(string hyphens)
{
var capitalParts = hyphens.Split('-').Select(m => m.Substring(0, 1).ToUpper() + m.Substring(1));
var pascalCase = String.Join("", capitalParts);
return pascalCase;
}
public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
{
var valuesClone = new RouteValueDictionary(values);
var pascalAction = valuesClone["action"] as string;
var hyphens = ConvertPascalCaseToHyphens(pascalAction);
valuesClone["action"] = hyphens;
var baseRouteVirtualPath = baseRoute.GetVirtualPath(requestContext, valuesClone);
return baseRouteVirtualPath;
}
private string ConvertPascalCaseToHyphens(string pascal)
{
var pascalParts = new List<string>();
var currentPart = new StringBuilder();
foreach (var character in pascal)
{
if (char.IsUpper(character) && currentPart.Length > 0)
{
pascalParts.Add(currentPart.ToString());
currentPart.Clear();
}
currentPart.Append(character);
}
if (currentPart.Length > 0)
{
pascalParts.Add(currentPart.ToString());
}
var lowers = pascalParts.Select(m => m.ToLower());
var hyphens = String.Join("-", lowers);
return hyphens;
}
}
}

Mono.CSharp: how do I inject a value/entity *into* a script?

Just came across the latest build of Mono.CSharp and love the promise it offers.
Was able to get the following all worked out:
namespace XAct.Spikes.Duo
{
class Program
{
static void Main(string[] args)
{
CompilerSettings compilerSettings = new CompilerSettings();
compilerSettings.LoadDefaultReferences = true;
Report report = new Report(new Mono.CSharp.ConsoleReportPrinter());
Mono.CSharp.Evaluator e;
e= new Evaluator(compilerSettings, report);
//IMPORTANT:This has to be put before you include references to any assemblies
//our you;ll get a stream of errors:
e.Run("using System;");
//IMPORTANT:You have to reference the assemblies your code references...
//...including this one:
e.Run("using XAct.Spikes.Duo;");
//Go crazy -- although that takes time:
//foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
//{
// e.ReferenceAssembly(assembly);
//}
//More appropriate in most cases:
e.ReferenceAssembly((typeof(A).Assembly));
//Exception due to no semicolon
//e.Run("var a = 1+3");
//Doesn't set anything:
//e.Run("a = 1+3;");
//Works:
//e.ReferenceAssembly(typeof(A).Assembly);
e.Run("var a = 1+3;");
e.Run("A x = new A{Name=\"Joe\"};");
var a = e.Evaluate("a;");
var x = e.Evaluate("x;");
//Not extremely useful:
string check = e.GetVars();
//Note that you have to type it:
Console.WriteLine(((A) x).Name);
e = new Evaluator(compilerSettings, report);
var b = e.Evaluate("a;");
}
}
public class A
{
public string Name { get; set; }
}
}
And that was fun...can create a variable in the script's scope, and export the value.
There's just one last thing to figure out... how can I get a value in (eg, a domain entity that I want to apply a Rule script on), without using a static (am thinking of using this in a web app)?
I've seen the use compiled delegates -- but that was for the previous version of Mono.CSharp, and it doesn't seem to work any longer.
Anybody have a suggestion on how to do this with the current version?
Thanks very much.
References:
* Injecting a variable into the Mono.CSharp.Evaluator (runtime compiling a LINQ query from string)
* http://naveensrinivasan.com/tag/mono/
I know it's almost 9 years later, but I think I found a viable solution to inject local variables. It is using a static variable but can still be used by multiple evaluators without collision.
You can use a static Dictionary<string, object> which holds the reference to be injected. Let's say we are doing all this from within our class CsharpConsole:
public class CsharpConsole {
public static Dictionary<string, object> InjectionRepository {get; set; } = new Dictionary<string, object>();
}
The idea is to temporarily place the value in there with a GUID as key so there won't be any conflict between multiple evaluator instances. To inject do this:
public void InjectLocal(string name, object value, string type=null) {
var id = Guid.NewGuid().ToString();
InjectionRepository[id] = value;
type = type ?? value.GetType().FullName;
// note for generic or nested types value.GetType().FullName won't return a compilable type string, so you have to set the type parameter manually
var success = _evaluator.Run($"var {name} = ({type})MyNamespace.CsharpConsole.InjectionRepository[\"{id}\"];");
// clean it up to avoid memory leak
InjectionRepository.Remove(id);
}
Also for accessing local variables there is a workaround using Reflection so you can have a nice [] accessor with get and set:
public object this[string variable]
{
get
{
FieldInfo fieldInfo = typeof(Evaluator).GetField("fields", BindingFlags.NonPublic | BindingFlags.Instance);
if (fieldInfo != null)
{
var fields = fieldInfo.GetValue(_evaluator) as Dictionary<string, Tuple<FieldSpec, FieldInfo>>;
if (fields != null)
{
if (fields.TryGetValue(variable, out var tuple) && tuple != null)
{
var value = tuple.Item2.GetValue(_evaluator);
return value;
}
}
}
return null;
}
set
{
InjectLocal(variable, value);
}
}
Using this trick, you can even inject delegates and functions that your evaluated code can call from within the script. For instance, I inject a print function which my code can call to ouput something to the gui console window:
public delegate void PrintFunc(params object[] o);
public void puts(params object[] o)
{
// call the OnPrint event to redirect the output to gui console
if (OnPrint!=null)
OnPrint(string.Join("", o.Select(x => (x ?? "null").ToString() + "\n").ToArray()));
}
This puts function can now be easily injected like this:
InjectLocal("puts", (PrintFunc)puts, "CsInterpreter2.PrintFunc");
And just be called from within your scripts:
puts(new object[] { "hello", "world!" });
Note, there is also a native function print but it directly writes to STDOUT and redirecting individual output from multiple console windows is not possible.

composing MEF parts in C# like a simple Funq container

In Funq and probably most other IoC containers I can simply do this to configure a type:
container.Register<ISomeThing>(c => new SomeThing());
How could I quickly extend MEF (or use existing MEF functionality) to do the same without using attributes.
Here is how I thought I could do it:
var container = new CompositionContainer();
var batch = new CompositionBatch();
batch.AddExport<ISomeThing>(() => new SomeThing());
batch.AddExportedValue(batch);
container.Compose(batch);
With this extension method for CompositionBatch:
public static ComposablePart AddExport<TKey>(this CompositionBatch batch, Func<object> func)
{
var typeString = typeof(TKey).ToString();
return batch.AddExport(
new Export(
new ExportDefinition(
typeString,
new Dictionary<string, object>() { { "ExportTypeIdentity", typeString } }),
func));
}
If I later do:
var a = container.GetExport<ISomeThing>().Value;
var b = container.GetExport<ISomeThing>().Value;
Both instance are the same. How can I force (configure) them to be different instances?
If this is not the way to go, how would I do this in MEF?
I would imagine the key is to add the delegate to the container, e.g.:
container.AddExportedValue<Func<ISomething>>(() => new Something());
That way you can grab the delegate and execute it:
var factory = container.GetExport<Func<ISomething>>();
ISomething something = factory();
Of course, MEF (Silverlight) does provide a native ExportFactory<T> (and ExportFactory<T,TMetadata> type that supports the creation of new instances for each call to import. You can add support for this by downloading Glen Block's ExportFactory for .NET 4.0 (Desktop) library.
If you don't want to use attributes, you can use this trick (based on Mark Seemann's blogpost).
First, create a generic class like this:
[PartCreationPolicy(CreationPolicy.NonShared)]
public class MefAdapter<T> where T : new()
{
private readonly T export;
public MefAdapter()
{
this.export = new T();
}
[Export]
public virtual T Export
{
get { return this.export; }
}
}
Now you can register any class you want in the container, like this:
var registeredTypesCatalog = new TypeCatalog(
typeof(MefAdapter<Foo>),
typeof(MefAdapter<Bar>),
...);
var container = new CompositionContainer(catalog);
Alternatively, you could implement your own export provider derived from ExportProvider, which allows you to pretty much duplicate Funq's way of working:
var provider = new FunqyExportProvider();
provider.Register<IFoo>(context => new Foo());
var container = new CompositionContainer(provider);
Both instance are the same. How can I force (configure) them to be different instances?
Simply mark the SomeThing class like this:
[Export(typeof(ISomeThing)]
[PartCreationPolicy(CreationPolicy.NonShared]
public class SomeThing : ISomeThing
{
...
}
And then you will get different instances wherever you import ISomeThing.
Alternatively, you can also set a required creation policy on an import:
[Export(typeof(IFoo))]
public class Foo : IFoo
{
[Import(typeof(ISomeThing),
RequiredCreationPolicy = CreationPolicy.NonShared)]
public ISomething SomeThing { private get; set; }
}
In Glen Block's Skydrive directory linked to in Matthew Abbott's answer I found something that seems simple and lightweight: A FuncCatalog. Download it here: FuncCatalogExtension.
Using the few little classes from that project I could now do this:
var funcCatalog = new FuncCatalog();
funcCatalog.AddPart<ISomeThing>(ep => new SomeThing());
var container = new CompositionContainer(funcCatalog);
var batch = new CompositionBatch();
batch.AddExportedObject<ExportProvider>(container);
container.Compose(batch);
var a = container.GetExportedObject<ISomeThing>();
var b = container.GetExportedObject<ISomeThing>();