Custom JsonConverter not working on WebAPI object deserialization - c#

I have a model object that I send to the browser and gets sent back to me. I want that ID value in that object to be encrypted. I created a custom JsonConverter to encrypt the string and then decrypt it.
public class SecretItem
{
[JsonConverter(typeof(EncryptedIdConverter))]
public string Id { get; set; }
public string Name { get; set; }
}
This is my EncryptedIdConverter class
class EncryptedIdConverter : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
string encryptedValue = (string)value;
if (!string.IsNullOrWhiteSpace(encryptedValue))
encryptedValue = Encryption.EncryptString(encryptedValue);
serializer.Serialize(writer, encryptedValue);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
string decryptedString = (string)reader.Value;
if (!string.IsNullOrWhiteSpace(decryptedString))
decryptedString = Encryption.DecryptString(decryptedString);
return decryptedString;
}
public override bool CanConvert(Type objectType)
{
return typeof(string).IsAssignableFrom(objectType);
}
}
If I try calling the JsonConvert.Serialization functions, everything works correctly.
JsonConvert.SerializeObject(secretItem)
JsonConvert.DeserializeObject<SecretItem>([JSON secretItem]);
When I return the HttpActionResult Ok(secretItem)... the browser also gets the encrypted Id string.
However, when I POST the data back to my controller, my webapi method is not getting a decrypted property. It skips the JsonConverter.
public async Task<IHttpActionResult> Post(SecretItem secretItem)
{
// Not decrypted
var decryptedId = secretItem.Id;
}
Why would the deserialize logic not be working the same as the serialize logic in my WebAPI? I don't even know where to start debugging that.
We are using Newtonsoft.Json 10.0.0.0, MVC5, .NET Framework 4.6.1.

It turns out the code is working correctly. The problem was that on the POST that was being tested, the content-type wasn't set to "application/json". So, it didn't use the JsonNetFormatter and therefore skipped the converter.
Once I set the contentType, everything works!

How is your Global.asax
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
GlobalConfiguration.Configure(WebApiConfig.Register);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
it should work
maybe you need a TypeConverter
Model binding
When Web API calls a method on a controller, it must set values for the parameters, a process called binding
This is called Model binding
Post(SecretItem secretItem)
Model binding use a TypeConverter
JSON Serialization
This is called JSON Serialization
HttpActionResult Ok(secretItem)
JSON Serialization use a JsonConverter
Documentation
Parameter Binding in ASP.NET Web API | Microsoft Docs
JSON and XML Serialization in ASP.NET Web API | Microsoft Docs
more
asp.net mvc - C# WebAPI: Set default JSON serializer to NewtonSoft JSON - Stack Overflow
c# - Setting the default JSON serializer in ASP.NET MVC - Stack Overflow
c# - How to use Json.NET for JSON modelbinding in an MVC5 project? - Stack Overflow

Related

Is there way to choose between Newtonsoft.json and System.Text.Json in Actions?

I use ASP.NET Core 5, I don't want to migrate from Newtonsoft.Json to System.Text.Json, but in some cases, I want to use System.Text.Json to increase performance in controller actions.
For example, in ActionA, I want to use default behavior of Newtonsoft.Json serializer and in ActionB, I want to change behaviour to System.Text.Json serializer.
As far as I know, there is no build-in way to specific the Jsonconvert for specific controller.
If you want to modify the generated json result jsonconvert, I suggest you could try to use this way.
I suggest you could try to use actionfilter to achieve your requirement.
By using actionfilter, you could modift the input formatter to use other jsonconvert to convert the data.
public class CaseActionAttribute : ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext ctx)
{
if (ctx.Result is ObjectResult objectResult)
{
objectResult.Formatters.Add(new SystemTextJsonOutputFormatter(new JsonSerializerOptions
{
IgnoreNullValues = true
}));
}
}
}
Usage:
[HttpPost]
[CaseAction]
public ActionResult Index([FromForm]Member member) {
if (ModelState.IsValid)
{
RedirectToAction("Index");
}
return Ok();
}
If you want to set the convert for the model binder, the only way is create a cusotmer modle binder and modify the json formatter according to each model type. There is no way to achieve it according to asp.net core has modified the iresoucefilter to not support change is formater.

Return Entity Framework objects as JSON with NancyFx

I'm building a small API (for read operations - GET) using NancyFX and C# with .NET 4.0
I'm using Entity Framework 6.0 in order to access a SQL Server 2008R2 database.
I have the following route exposed with Nancy (this is just for testing purposes):
public ExampleNancyModule()
{
Get["/v1/address_types"] = parameters =>
{
var address_types = context.address_type.First();
return Response.AsJson(address_types);
};
}
I'm able to access the route with Postman, however I'm receiving an empty response body. How can I return the object and/or a list of objects with Nancy?
I guess there's more configuration that needs to be done first. I'm new with Nancy, I just started using it this morning. It seems promising!
Thanks for the support.
I found a solution for this case:
I've changed the default Json Serializer that comes with Nancy with NewtonSoft.Json
The code in ExampleNancyModule remains the same, however I've added a Boostrap file to overwrite the default behaviour of Nancy. My Bootstrap.cs file looks like this:
namespace MyProject
{
public class Bootstrap : DefaultNancyBootstrapper
{
protected override void ConfigureApplicationContainer(TinyIoCContainer container)
{
base.ConfigureApplicationContainer(container);
container.Register<JsonSerializer, CustomJsonSerializer>();
}
}
public class CustomJsonSerializer : JsonSerializer
{
public CustomJsonSerializer()
{
this.ContractResolver = new CamelCasePropertyNamesContractResolver();
this.Formatting = Formatting.Indented;
}
}
}
With this I can get a JSON response that respects the attributes and the JSON configuration of my Entity Framework Models. Hope this helps someone else.

How can I access request information from within the serialization process

I am working on a (self-hosted) WebApi application using Visual Studio 2012 targetting .Net 4+ and with an eye on moving to VS2013 as soon as possible to take advantage of MVC 5 and WebApi 2.0.
I need to prefix all outgoing Uri's with a string that is sent as a query parameter on the incoming request. The challenge is to do this without any specific code in controllers, models or views/viewmodels. And if at all possible, I would also like to stay away from using an action filter that would need to use reflection/recursion to work through all properties of the outgoing response, though it would be fine to use a result action filter to make the url prefix available to the serializer.
What I have come with so far is a DelegatingHandler to get the url prefix to use from the request's query parameters so it can be added as a property to some request/controller context object; and with a JsonConverter to add the desired prefix to all Uri type properties in responses.
What I am left with is getting the Url prefix specified in the request's parameters to the convertor. The serializer that is passed to the JsonConvertor does have a Context property, but I can't see if and how that is related to the request context.
I can think of a number of approaches to solve this, but keep run into "lack of knowledge" walls on how MVC/WebApi carries request (context) information around the pipeline.
Delegating Handler:
class UrlPrefixHandler : DelegatingHandler
{
private string GetUrlPrefixValue(HttpRequestMessage request)
{
var queryStrings = request.GetQueryNameValuePairs();
if (queryStrings == null)
return null;
var match = queryStrings.FirstOrDefault(kv => string.Compare(kv.Key, "url_prefix", true) == 0);
if (string.IsNullOrEmpty(match.Value))
return null;
return match.Value;
}
async protected override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken)
{
string urlPrefix = GetUrlPrefixValue(request);
// TODO : How do I get this to the serializer on a per request basis?
// and do this without placing requirements on controllers/models/views?
HttpResponseMessage response = await base.SendAsync(request, cancellationToken);
return response;
}
}
Json Converter:
class UriPrefixConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return typeof(Uri).IsAssignableFrom(objectType);
}
public override bool CanRead
{
get { return false; }
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException("Unnecessary because CanRead is false. The type will skip the converter.");
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
JObject result = new JObject();
//serializer.
Uri u = (Uri)value as Uri;
// TODO : Getting the prefix from the request's context somehow
Uri prefixed = new Uri("/uriPrefix" + u.OriginalString, UriKind.Relative);
writer.WriteValue(prefixed.OriginalString);
}

Customizing ASP Web API Json serialization by which Action is invoked

I am looking at converting an existing JSON api from a hacky MVC3 implementation to the latest MVC4 Web Api. The MVC3 implementation uses JSON.NET to do all the serialization which will make the upgrade nice and smooth.
I am stuck on customizing how the results of some action get serialized. For instance I want some actions to return only a few properties of the outputted objects, whilst others may do rather deep serialization. In my current implementation, an action can add a bunch of serialization overrides by setting appropriate settings in the HttpContext. These are later picked up for custom serialization through a class derived from JsonResult. The main use of adding custom JsonConverters is to control and reduce the number of key/values getting serialized, and vary the parameters to serialize depending on the action (certain actions should return more object parameters than others).
Condensed example of a controller and the class controlling json serialization in my current MVC3 implementation:
public class TestController : JsonController {
public JsonResult Persons() {
ControllerContext.HttpContext.Items[typeof(IEnumerable<JsonConverter>)] = new JsonConverter[] {
new InterfaceExtractorJsonConverter<IPersonForList>(),
new StringEnumConverter()
};
ControllerContext.HttpContext.Items[typeof(IContractResolver)] = new SpecialCamelCasePropertyNamesContractResolver();
}
}
public class JsonNetResult : JsonResult {
public override void ExecuteResult(ControllerContext context) {
var response = context.HttpContext.Response;
var additionalConverters = context.HttpContext.Items[typeof(IEnumerable<JsonConverter>)] as IEnumerable<JsonConverter> ?? Enumerable.Empty<JsonConverter>();
var contractResolver = context.HttpContext.Items[typeof(IContractResolver)] as IContractResolver ?? new JsonContractResolver();
var typeNameHandling = TypeNameHandling.None;
if (context.HttpContext.Items.Contains(typeof(TypeNameHandling)))
typeNameHandling = (TypeNameHandling)context.HttpContext.Items[typeof(TypeNameHandling)];
response.Write(JsonConvert.SerializeObject(Data, Formatting.Indented, new JsonSerializerSettings {
ContractResolver = contractResolver,
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
Converters = additionalConverters,
TypeNameHandling = typeNameHandling
}));
}
}
In Web Api I see that I can get the Json formatter from the configuration and alter the serializations globally.
var config = new HttpSelfHostConfiguration("http://localhost:8080");
var jsonFormatter = config.Formatters.OfType<JsonMediaTypeFormatter>().Single();
jsonFormatter.SerializerSettings.ContractResolver = new SpecialCamelCasePropertyNamesContractResolver();
jsonFormatter.SerializerSettings.Converters = new[] { new InterfaceExtractorJsonConverter<ITesting>() };
However, I was planning on controlling the serialization of actions on an individual basis (or per controller) by adding some attributes to specify which JsonConverter's to use. Thus I would like the Json serializer to find the relevant attributes given to the invoked action/controller and alter serialization accordingly. I am not sure where and how to do this. Should I inherit from JsonMediaTypeFormatter and do the work there somehow? What other options do I have?
I've never seen anyone want to control the serialization in that way. But to achieve your goal with the minimal amount of rework, I would return all of that information from your method directly:
class JsonNetResponse {
public IContractResolver ContractResolver { get;set; }
// Other Json.Net bits
public object Value { get; set; }
}
I would then create a custom Formatter than can handle those objects:
class JsonNetFormatter : MediaTypeFormatter {
public override bool CanWriteType(Type t) {
return typeof(JsonNetResponse).IsAssignableFrom(t);
}
// TODO WriteToStreamAsync which is basically a copy of your original JsonNetResult
}

ASP.Net WebAPI Get current controller name from inside MediaTypeFormatter

I am writing a media type formatter for HTML to automatically generate a Razor view based on an html request from the user. I am doing this for use inside a SelfHosted service. I need to detect what controller/action was requested to allow me to pick the view to render into.
public class RazorHtmlMediaTypeFormatter : MediaTypeFormatter
{
public RazorHtmlMediaTypeFormatter()
{
SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/html"));
}
public override bool CanWriteType(Type type)
{
return true;
}
public override bool CanReadType(Type type)
{
return false;
}
public override Task WriteToStreamAsync(Type type, object value, Stream stream, HttpContentHeaders contentHeaders, System.Net.TransportContext transportContext)
{
return Task.Factory.StartNew(() =>
{
var view = Razor.Resolve(String.Format("{0}.{1}.cshtml", something.Controller, something.Action), value);
byte[] buf = System.Text.Encoding.Default.GetBytes(view.Run(new ExecuteContext()));
stream.Write(buf, 0, buf.Length);
stream.Flush();
});
}
}
Why not wrapping your returned objects in Metadata<T>?
I.e. return, instead of MyCustomObject, Metadata<MyCustomObject>. As Metadata properties, you can set controller name and action. Then in the formatter, just decouple the Metadata and your custom object, and serialize just that custom object.
I blogged about this approach here - http://www.strathweb.com/2012/06/extending-your-asp-net-web-api-responses-with-useful-metadata/. While the purpose of the article is a bit different, I am sure you can relate it to your needs.
Edit: or if you are OK with a small hack, use a custom filter and headers:
public override void OnActionExecuting(HttpActionContext actionContext)
{
actionContext.Response.Headers.Add("controller", actionContext.ActionDescriptor.ControllerDescriptor.ControllerName);
actionContext.Response.Headers.Add("action", actionContext.ActionDescriptor.ActionName;);
base.OnActionExecuting(actionContext);
}
then just read it from the headers in the formatter, and remove the header entries so that they don't get sent to the client.
Web API Contrib has a working RazorViewFormatter in here.

Categories