Using the Json.NET serializer in an MVC4 project - asp.net-mvc

I'm starting to learn Json.NET, but I'm having trouble using its serializer. I have a new MVC4 project with a Web.API service:
public class PTE_TestsController : ApiController {
PTE_TestsRepository _repository = new PTE_TestsRepository();
// GET /api/PTE_Tests/5
public HttpResponseMessage<string> Get(int id) {
try {
PTE_Test test = _repository.GetTest(id);
return new HttpResponseMessage<string>(JsonConvert.SerializeObject(test));
} catch {
return new HttpResponseMessage<string>(HttpStatusCode.NotFound);
}
}
}
JsonConvert.SerializeObject() works as expected and returns a string. My Web.API controller returns that as part of an HttpResponseMessage. The end result, when viewed in Fiddler, is not JSON data, but JSON data being serialized again (I think):
"{\"ID\":1,\"Name\":\"Talar Tilt\",\"TagID\":1,\"PracticeID\":1,
\"SpecificAreaID\":1,\"TypeID\":1,\"VideoID\":1,\"PicID\":1}"
Does someone know how to turn off the default serializer so I can use Json.NET directly? By the way, I'm not using the default serializer because I can't figure out how to make it work with complex objects (PTE_Test will eventually contain members of type List).
Alternately, it will also solve my problem if someone can explain how to use the default serializer with complex objects. MSDN's explanation didn't help me.

Rick Strahl has a blog on that here with a code that works.

As others have pointed out, you need to create a formatter and replace the DataContractSerializer with the JSON.NET serializer. Although, if you're not in a rush for JSON.NET specifically, rumor has it that next beta/rc is going to have support for JSON.NET built in.
Conceptually, however, you're missing part of the magic of WebAPI. With WebAPI you return your object in it's native state (or IQueryable if you want OData support). After your function call finishes the Formatter's take over and convert it into the proper shape based on the client request.
So in your original code, you converted PTE_Test into a JSON string and returned it, at which point the JSON Formatter kicked in and serialized the string. I modified your code as follows:
public class PTE_TestsController : ApiController {
PTE_TestsRepository _repository = new PTE_TestsRepository();
public HttpResponseMessage<PTE_Test> Get(int id) {
try {
PTE_Test test = _repository.GetTest(id);
return new HttpResponseMessage(test);
} catch {
return new HttpResponseMessage<string>(HttpStatusCode.NotFound);
}
}
}
Notice how your function returns PTE_Test instead of string. Assuming the request came in with a request header of Accept = application/json then the JSON formatter will be invoked. If the request had a header of : Accept = text/xml the XML formatter is invoked.
There's a decent article on the topic here. If you're a visual learner, Scott Gu shows some examples using fiddler in this video, starting around 37 minutes. Pedro Reys digs a little deeper into content negotiation here.

The way to do this is to use formatters.
Check out: https://github.com/WebApiContrib/WebAPIContrib/tree/master/src/WebApiContrib.Formatting.JsonNet.
Json.NET support will be in the RC release of Web API.

Related

How to handle API callbacks in ASP.NET MVC (Helloworks API in my case)

As per their documentation from link https://docs.helloworks.com/v3/reference#callbacks
"With the HelloWorks API you can use callbacks to be notified about certain events. Currently we support a callback to be registered when a step is started, the cancellation of a workflow, and the completion of a workflow.
The callback URL will be called according to the following:
curl -X POST https://your.domain.com/some/path
-H "X-HelloWorks-Signature: {signature}"
-H "Content-Type: application/json"
-d "{payload}
I am not able to figure out how can I handle the callback in ASP.NET MVC 4.0. The callback returns data on JSON format. Once I receive the data, I can format it as per my need and can save to database. But how can I get the data in my controller? Guidance from experts on APIs are highly appreciated. Thanks in advance.
I am not able to figure out how can I handle the callback in ASP.NET MVC 4.0.
You need to have an api controller that accepts POST requests. That api endpoint is then called by the HelloWorks api. The fancy word to describe this mechanism is a Webhook. A nice introduction can be found here.
The very basic would be a controller like
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
namespace MyWebAPI.Controllers
{
public class WebHookController : ApiController
{
// POST: api/webhook
public void Post([FromBody]string value)
{
}
}
}
You will need to register the url https://yourwebsite.domain/api/webhook at the HelloWorks api so it knows where to send the data to.
You probably want to secure this endpoint so others cannot abuse this api. See the docs for some guidance.
For example, in your case you should check that a header named "X-HelloWorks-Signature" is send in the request to the endpoint. The value of that header is a hash that should match the value of a hash of the content you received. To calculate the hash code to match create a hash using the SHA-256 algorithm and base16-encode the result.
There is also documentation from Microsoft on how to create web apis
Peter your guidance worked. I appreciate that. It was straight forward, only the technical jargon are making it intimidating :). Below are the code that worked. I am still to secure it using signature.
[HttpPost]
public ActionResult Callback()
{
string rawBody = GetDocumentContents(Request);
dynamic eventObj = JsonConvert.DeserializeObject(rawBody);
Test newTest = new Test();
newTest.Response = "Bikram-1" + (string)eventObj.type;
var test = db.Tests.Add(newTest);
db.SaveChanges();
return Content("Success!");
}
private string GetDocumentContents(HttpRequestBase Request)
{
string documentContents;
using (Stream receiveStream = Request.InputStream)
{
using (StreamReader readStream = new StreamReader(receiveStream, Encoding.UTF8))
{
documentContents = readStream.ReadToEnd();
}
}
return documentContents;
}

.net MVC Odata endpoint works on POST, but 404s on GET

My question should have a simple answer, but after hours of experimentation and googling, I have nothing so now I'm here.
I am working with .net MVC 5 odata 2.0. I am trying to create a new odata endpoint that works on a get. I've successfully created a few endpoints that work with POST, but I can't seem to get one that works as a GET.
relevant code
WebApiConfig.cs
ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
builder.EntitySet<Review>("Reviews");
builder.EntitySet<Strategy>("Strategies");
ActionConfiguration ReviewsInStrategy = builder.Entity<Strategy>().Action("ReviewsInStrategy");
ReviewsInStrategy.ReturnsCollectionFromEntitySet<Review>("Reviews");
config.Routes.MapODataServiceRoute("odata", "odata", builder.GetEdmModel());
StrategiesController.cs
[EnableQuery]
public IQueryable<Review> ReviewsInStrategy([FromODataUri] Guid key){
Strategy strategy = db.Strategies.Find(key);
return strategy.Reviews
}
Now I go to fiddler and try a GET to
[myurl]/odata/Strategies(guid'[myguid]')/ReviewsInStrategy
I get a 404 result. But when I change fiddler to a POST (no other change - I don't add accept headers or content types or anything) it works just fine.
How do I make this work with GET?
You need to add a Get-function to your controller:
[EnableQuery]
public SingleResult<Strategies> Get([FromODataUri] Guid key)
{
IQueryable<Strategies> result = db.Strategies.Where(p => p.Id == key);
return SingleResult.Create(result);
}
That way you can use a GET request on the Strategies-resource. If you still want to use a custom action you must add the parameter to your config:
ActionConfiguration ReviewsInStrategy = builder.Entity<Strategy>().Action("ReviewsInStrategy").Returns<Review>().Parameter<Guid>("Key");
[HttpGet]
[ODataRoute("ReviewsInStrategy(Key={key})")]
public IHttpActionResult<Review> ReviewsInStrategy([FromODataUri] Guid key){
Strategy strategy = db.Strategies.Find(key);
return Ok(strategy.Reviews);
}
Not sure if all this works with the complex type Guid. Maybe you need to change it to string and parse it inside the function. Here is a good msdn article about this.

Breeze Todo Example - Why does the TodosController return a string on purge and reset?

I'm going over the TODOs example in Breeze for Angular and have a question which I am unable to find an answer for.
Why does the TodosController return a string on both purge and reset? It doesn't seem to matter if I return any string, null, or nothing at all...
Code snippet:
// ~/breeze/todos/purge
[HttpPost]
public string Purge()
{
TodoDatabaseInitializer.PurgeDatabase(_contextProvider.Context);
return "purged";
}
// ~/breeze/todos/reset
[HttpPost]
public string Reset()
{
Purge();
TodoDatabaseInitializer.SeedDatabase(_contextProvider.Context);
return "reset";
}
Thanks!
AJ
You are correct, it does not matter, in fact thi call has more to do with Entity Framework than breeze. The string is just a message for clarity and can be nice if you are using a tool like fiddler, which shows your http requests and the information about them. In this case, you would see purged in you http response body in fiddler, so you know the call succeeded. Doesn't look like it's used on the client though.

ASP.NET 4.0 JSON from WebService - Removing Escape Slashes

Hi I'm hitting my head against the wall here...
I'm using DataContractJsonSerializer to encode data that I'm retrieving from the database which I'm sending back to an AJAX call...
I have this extension:
public static string ToJSON<T>(this T obj) where T : class
{
DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(T));
using (MemoryStream stream = new MemoryStream())
{
serializer.WriteObject(stream, obj);
return Encoding.Default.GetString(stream.ToArray());
}
}
and I'm calling it from the ASMX:
return _webServiceService.GetSponsors().ToJSON();
Passing in the resultset from a Stored Procedure using Entity Framework
It's all work kinda working, except this is part of what's being returned:
{"d":"[{\"FileID\":296,\"IconUrl\":\"\\/Files\\/Banners\\/20119\\/00B56BAC.jpg\",\"ImageUrl\":....
How on earth to I get rid of the slashes so that it returns the following:
{"d":"[{"FileID":296,"IconUrl":"/Files/Banners/20119/00B56BAC.jpg","ImageUrl":....
I'm trying to implement the JQuery UI Autocomplete. Well I'm guessing this will sort it, not sure if I need the FileID value within quotes?
You don't need to JSON serialize the return value by hand, because ASP.NET will handle it for you automatically in that scenario. What you're seeing there is ASP.NET applying a second level of JSON serialization to your manually generated string, which requires escaping double quotes with backslashes.
Change your ASMX method to return something like List<Sponsor> and return _webServiceService.GetSponsors() directly, without the ToJSON() extension.

Best way to structure the code for an ASP.NET MVC REST API that is decoupled from the data formats?

I am creating a REST API in ASP.NET MVC. I want the format of the request and response to be JSON or XML, however I also want to make it easy to add another data format and easy to create just XML first and add JSON later.
Basically I want to specify all of the inner workings of my API GET/POST/PUT/DELETE requests without having to think about what format the data came in as or what it will leave as and I could easily specify the format later or change it per client. So one guy could use JSON, one guy could use XML, one guy could use XHTML. Then later I could add another format too without having to rewrite a ton of code.
I do NOT want to have to add a bunch of if/then statements to the end of all my Actions and have that determine the data format, I'm guessing there is some way I can do this using interfaces or inheritance or the like, just not sure the best approach.
Serialization
The ASP.NET pipeline is designed for this. Your controller actions don't return the result to the client, but rather a result object (ActionResult) which is then processed in further steps in the ASP.NET pipeline. You can override the ActionResult class. Note that FileResult, JsonResult, ContentResult and FileContentResult are built-in as of MVC3.
In your case, it's probably best to return something like a RestResult object. That object is now responsible to format the data according to the user request (or whatever additional rules you may have):
public class RestResult<T> : ActionResult
{
public override void ExecuteResult(ControllerContext context)
{
string resultString = string.Empty;
string resultContentType = string.Empty;
var acceptTypes = context.RequestContext.HttpContext.Request.AcceptTypes;
if (acceptTypes == null)
{
resultString = SerializeToJsonFormatted();
resultContentType = "application/json";
}
else if (acceptTypes.Contains("application/xml") || acceptTypes.Contains("text/xml"))
{
resultString = SerializeToXml();
resultContentType = "text/xml";
}
context.RequestContext.HttpContext.Response.Write(resultString);
context.RequestContext.HttpContext.Response.ContentType = resultContentType;
}
}
Deserialization
This is a bit more tricky. We're using a Deserialize<T> method on the base controller class. Please note that this code is not production ready, because reading the entire response can overflow your server:
protected T Deserialize<T>()
{
Request.InputStream.Seek(0, SeekOrigin.Begin);
StreamReader sr = new StreamReader(Request.InputStream);
var rawData = sr.ReadToEnd(); // DON'T DO THIS IN PROD!
string contentType = Request.ContentType;
// Content-Type can have the format: application/json; charset=utf-8
// Hence, we need to do some substringing:
int index = contentType.IndexOf(';');
if(index > 0)
contentType = contentType.Substring(0, index);
contentType = contentType.Trim();
// Now you can call your custom deserializers.
if (contentType == "application/json")
{
T result = ServiceStack.Text.JsonSerializer.DeserializeFromString<T>(rawData);
return result;
}
else if (contentType == "text/xml" || contentType == "application/xml")
{
throw new HttpException(501, "XML is not yet implemented!");
}
}
Just wanted to put this on here for the sake of reference, but I have discovered that using ASP.NET MVC may not be the best way to do this:
Windows Communication Foundation (WCF)
provides a unified programming model
for rapidly building service-oriented
applications that communicate across
the web and the enterprise
Web application developers today are
facing new challenges around how to
expose data and services. The cloud,
move to devices, and shift toward
browser-based frameworks such as
jQuery are all placing increasing
demands on surfacing such
functionality in a web-friendly way.
WCF's Web API offering is focused on
providing developers the tools to
compose simple yet powerful
applications that play in this new
world. For developers that want to go
further than just exposing over HTTP,
our API will allow you to access all
the richness of HTTP and to apply
RESTful constraints in your
application development. This work is
an evolution of the HTTP/ASP.NET AJAX
features already shipped in .Net 4.0.
http://wcf.codeplex.com/
However I will not select this as the answer because it doesn't actually answer the question despite the fact that this is the route I am going to take. I just wanted to put it here to be helpful for future researchers.

Resources