what's the best practises for MVC web APi pure JSON? - asp.net-mvc

As per topic, i have created two samples of MVC web api with both JSON as input and output.
sample put
[HttpPut]
public string Put(int id, [FromBody] dynamic value)
{
return id.ToString() + value.name + value.time;
}
sample get
[HttpGet]
public string Get()
{
Portal.BusinessLogic.User.FAQHelper faqHELPER = new Portal.BusinessLogic.User.FAQHelper();
return JsonConvert.SerializeObject(faqHELPER.getFAQs()); ;
}
However, i am not sure whether this is the correct way to do a web controller which accepts and returns json string. Can anyone guide me on how to do it the efficient and safe way ?

One of the approaches is pretty much a default way, is to have your parameters and return type as Model of the data you save and retrieve. In this case your controller will look like this:
// [HttpPut] <-- This is not needed if method called "Put...."
public HttpResponseMessage Put (MyDataPOCOModel model)
{
// update your data
}
// [HttpGet] <-- This is not needed if method called "Get...."
public HttpResponseMessage Get(int id)
{
// Get the data, pack HttpResponseMessage and return
// Optionally, return can be just MyDataPOCOModel
}
//[HttpGet] <-- This is not neededif method called "Get...."
public HttpResponseMessage Get()
{
// Get the data, pack HttpResponseMessage and return
// Optionally, return can be just IEnumerable<MyDataPOCOModel>
}
If your code is like the one above, by default, web api will automatically serialize/deserialize your Model object into and from JSON

Related

MVC 5 - RedirectToAction - Cannot pass parameter correctly

I have a method that looks as follows:
[Authorize]
public ActionResult Create(int? birdRowId, Entities.BirdSighting sighting)
{
...
...
}
I want to call the above method from another method in the same controller as follows:
[Authorize]
[HttpPost]
public ActionResult Create(Entities.BirdSighting birdSighting, FormCollection collection)
{
...
...
return RedirectToAction("Create", new {birdRowId = 10, sighting = birdSighting});
}
The RedirectToAction method calls the method correctly. And, the first parameter of the method being called (birdRowId) does equal 10. However, the second parameter, sighting, is always null, even though I'm passing an instantiated object with values. What am I doing wrong?
Remember, HTTP is stateless !
RedirectToAction method returns a 302 response to the client browser and thus the browser will make a new GET request to the specified URL.
If you are trying to follow the PRG pattern, I think you should not try to pass complex objects. You should only pass the ID of the resource so that the GET action can build the resource( the model) again using that ID.
return RedirectToAction("Created", "YourControllerName", new { #id=10} );
and in the Created action, read the id and build the object there.
public ActionResult Created(int id)
{
BirdSighting sighting=GetSightingFromIDFromSomeWhere(id);
// to do :Return something back here (View /JSON etc..)
}
If you really want to pass some data across (Stateless) HTTP Requests, you may use some temporary storage mechanism like TempData
Set your object to TempData in your HttpPost action method.
[HttpPost]
public ActionResult Create(BirdSighting birdSighting, FormCollection collection)
{
// do something useful here
TempData["BirdSighting"] =birdSighting;
return RedirectToAction("Created", "YourControllerName");
}
And in your GET action method,
public ActionResult Created()
{
var model=TempData["BirdSighting"] as BirdSighting;
if(model!=null)
{
//return something
}
return View("NotFound");
}
TempData uses Session object behind the scene to store the data. But once the data is read, the data is terminated.

How should I design MVC to conditionally return JSON or pretty HTML?

I have data that will either be consumed by a human or a web service. The REST url is:
http://host.com/movies/detail/1
http://host.com/movies/detail/The Shawshank Redemption (1994)
What convention should I follow to conditionally return JSON or HTML? Should I add a parameter such as "?json" or should I look at the client headers,.. some variation of both?
If I do a variation of both, if a conflict is found which takes precedent?
Check whether the Request is Ajax. You may use the Request.IsAjaxRequest() method which returns true/false.
public ActionResult details(string id)
{
var movieViewModel=movieService.GetMovieDetails(id);
If(Request.IsAjaxRequest())
{
// return Json now
return Json(movieViewModel,JsonRequestBehavior.AllowGet);
}
// Not an ajax request, Let's return Normal View (HTML)
return View(movieViewModel);
}
UNIT TESTING ASPECT : Request.IsAjaxRequest() is not unit test friendly! So if you are worried about unit tests, You can write your IsAjaxRequest property and put in your basecontroller class and use it.
public class BaseController : Controller
{
protected bool IsAjaxRequest()
{
//Using this method instead of Request.IsAjaxRequest() because
//IsAjaxRequest is static and not mockable ! (not unit test friendly)
var isAjax = Request.Headers["X-Requested-With"];
if (isAjax != null && isAjax.ToUpper() == "XMLHTTPREQUEST")
return true;
return false;
}
}
Now inherit your controller from this BaseController.
public class HomeController : BaseController
{
public ActionResult details(string id)
{
var movieViewModel=movieService.GetMovieDetails(id);
If(IsAjaxRequest)
{
// return Json now
return Json(movieViewModel,JsonRequestBehavior.AllowGet);
}
// Not an ajax request, Let's return Normal View (HTML)
return View(movieViewModel);
}
}
You could also use:
[HttpGet]
public ActionResult() {
// Return HTML
}
[HttpPost]
public ActionResult() {
// Assuming Ajax is of the type post
}
Just another solution if all your Ajax is using post.
I prefer using a parameter explicit in the URL because that way building REST petitions is easy for developers, self explanatory and with no surprises (do you have to guess default format? or see "difficult" to see HTTP headers). You decide:
if you have many options for formats you can use format=json
you can go with json parameter, but it is not pretty because you have to pair it with a value json=true, json=1. Besides you can set json=1&xml=1&html=1, harder to handle.
the twitter way is to emulate an extension such as call.json or call.xml (e.g. https://dev.twitter.com/docs/api/1.1/get/statuses/user_timeline)
I recommend don't tie together a kind of petition and a format. Let your API clients decide, ajax-json is commonly used, but not all develepers use it that way. Maybe I am writing a terminal application with no ajax, maybe I want to do a wget to your API and still get json.

How to get a response "stream" from an action in MVC3/Razor?

I am using MVC3, .NET4, C#.
I need to create some XHTML using a Razor View. I do this via an Action.
public ActionResult RenderDoc(int ReportId)
{
//A new document is created.
return View();
}
I then need to take the output from this and convert it to a Word Doc. I am using a 3rd party component to do this and it expects a "stream" or a "file" for the XHTML source that is read in for conversion to a DOC, like the following:
document.Open(MyXhtmlStream,FormatType.Html,XHTMLValidationType.Transitional);
My Question:
What would be a good way to call the "RenderDoc" Action and obtain the result as a stream to feed into "MyXhtmlStream".
Many thanks.
EDIT: I have had another idea !!!
1) Render the View within the action to create a String(XHTMLString). I have seen a method to do this on SO.
2) Create a MemoryStream and put this string into it.
Stream MyStream = New MemoryStream("XHTMLString and encoding method");
EDIT2: Based on Darin's answer
I need to clasyify a little further, and I hope to do this via tweaking Darin's code for my purpose.
public class XmlDocumentResult : ActionResult
{
private readonly string strXhtmlDocument;
public XmlDocumentResult(string strXhtmlDocument)
{
this.strXhtmlDocument = strXhtmlDocument;
}
public override void ExecuteResult(ControllerContext context)
{
WordDocument myWordDocument = new WordDocument();
var response = context.HttpContext.Response;
response.ContentType = "text/xml";
myWordDocument.Open(response.OutputStream, FormatType.Html, XHTMLValidationType.Transitional);
}
}
The above is closer to what I need. Note the 3rd Party WordDocument type. So there is still the issue of how I get the "strXhtmlDocument" into the "Response.OutputStream?
I would just write a custom ActionResult to handle that:
public class XmlDocumentResult : ActionResult
{
private readonly Document document;
public XmlDocumentResult(Document document)
{
this.document = document;
}
public override void ExecuteResult(ControllerContext context)
{
var response = context.HttpContext.Response;
response.ContentType = "text/xml";
document.Open(response.OutputStream, FormatType.Html, XHTMLValidationType.Transitional);
}
}
You could of course adjust the response Content-Type if necessary and also append a Content-Disposition header if you want.
And then simply have my controller action return this custom action result:
public ActionResult RenderDoc(int reportId)
{
Document document = repository.GetDocument(reportId);
return new XmlDocumentResult(document);
}
Now the controller action doesn't need to handle plumbing code anymore. The controller action does what a typical controller action is supposed to do:
Query the Model
Pass this model to an ActionResult
In your case the model is this Document class or whatever it is called.

How do I include a model with a RedirectToAction?

In the RedirectToAction below, I'd like to pass a viewmodel. How do I pass the model to the redirect?
I set a breakpoint to check the values of model to verify the model is created correctly. It is correct but the resulting view does not contain the values found in the model properties.
//
// model created up here...
//
return RedirectToAction("actionName", "controllerName", model);
ASP.NET MVC 4 RC
RedirectToAction returns a 302 response to the client browser and thus the browser will make a new GET request to the url in the location header value of the response came to the browser.
If you are trying to pass a simple lean-flat view model to the second action method, you can use this overload of the RedirectToAction method.
protected internal RedirectToRouteResult RedirectToAction(
string actionName,
string controllerName,
object routeValues
)
The RedirectToAction will convert the object passed(routeValues) to a query string and append that to the url(generated from the first 2 parameters we passed) and will embed the resulting url in the location header of the response.
Let's assume your view model is like this
public class StoreVm
{
public int StoreId { get; set; }
public string Name { get; set; }
public string Code { set; get; }
}
And you in your first action method, you can pass an object of this to the RedirectToAction method like this
var m = new Store { StoreId =101, Name = "Kroger", Code = "KRO"};
return RedirectToAction("Details","Store", m);
This code will send a 302 response to the browser with location header value as
Store/Details?StoreId=101&Name=Kroger&Code=KRO
Assuming your Details action method's parameter is of type StoreVm, the querystring param values will be properly mapped to the properties of the parameter.
public ActionResult Details(StoreVm model)
{
// model.Name & model.Id will have values mapped from the request querystring
// to do : Return something.
}
The above will work for passing small flat-lean view model. But if you want to pass a complex object, you should try to follow the PRG pattern.
PRG Pattern
PRG stands for POST - REDIRECT - GET. With this approach, you will issue a redirect response with a unique id in the querystring, using which the second GET action method can query the resource again and return something to the view.
int newStoreId=101;
return RedirectToAction("Details", "Store", new { storeId=newStoreId} );
This will create the url Store/Details?storeId=101
and in your Details GET action, using the storeId passed in, you will get/build the StoreVm object from somewhere (from a service or querying the database etc)
public ActionResult Details(string storeId)
{
// from the storeId value, get the entity/object/resource
var store = yourRepo.GetStore(storeId);
if(store!=null)
{
// Map the the view model
var storeVm = new StoreVm { Id=storeId, Name=store.Name,Code=store.Code};
return View(storeVm);
}
return View("StoreNotFound"); // view to render when we get invalid store id
}
TempData
Following the PRG pattern is a better solution to handle this use case. But if you don't want to do that and really want to pass some complex data across Stateless HTTP requests, you may use some temporary storage mechanism like TempData
TempData["NewCustomer"] = model;
return RedirectToAction("Index", "Users");
And read it in your GET Action method again.
public ActionResult Index()
{
var model=TempData["NewCustomer"] as Customer
return View(model);
}
TempData uses Session object behind the scene to store the data. But once the data is read the data is terminated.
Rachel has written a nice blog post explaining when to use TempData /ViewData. Worth to read.
Using TempData to pass model data to a redirect request in Asp.Net Core
In Asp.Net core, you cannot pass complex types in TempData. You can pass simple types like string, int, Guid etc.
If you absolutely want to pass a complex type object via TempData, you have 2 options.
1) Serialize your object to a string and pass that.
Here is a sample using Json.NET to serialize the object to a string
var s = Newtonsoft.Json.JsonConvert.SerializeObject(createUserVm);
TempData["newuser"] = s;
return RedirectToAction("Index", "Users");
Now in your Index action method, read this value from the TempData and deserialize it to your CreateUserViewModel class object.
public IActionResult Index()
{
if (TempData["newuser"] is string s)
{
var newUser = JsonConvert.DeserializeObject<CreateUserViewModel>(s);
// use newUser object now as needed
}
// to do : return something
}
2) Set a dictionary of simple types to TempData
var d = new Dictionary<string, string>
{
["FullName"] = rvm.FullName,
["Email"] = rvm.Email;
};
TempData["MyModelDict"] = d;
return RedirectToAction("Index", "Users");
and read it later
public IActionResult Index()
{
if (TempData["MyModelDict"] is Dictionary<string,string> dict)
{
var name = dict["Name"];
var email = dict["Email"];
}
// to do : return something
}
Another way to do it is to store it in the session.
var s = JsonConvert.SerializeObject(myView);
HttpContext.Session.SetString("myView", s);
and to get it back
string s = HttpContext.Session.GetString("myView");
myView = JsonConvert.DeserializeObject<MyView>(s);

Accessing resources via Uri in Asp.net mvc

I am working on an ASP.NET MVC web application in which I have an object with a Uri property. The Uri contains a restful link to a resource in the following form:
/Repository/Dataset/5
The Dataset action of the Repository controller returns the contents of dataset 5 as Json.
How do I call this method from the Uri and interpret the response as Json from within the object?
Many thanks.
In server side action return JsonResult.
public ActionResult Dataset(int id)
{
// reository code
return Json(model);
}
client side call $.getJSON.
My opinion is that you should not call your controller from anywhere in code.In ASP.NET MVC Controller is there to accept request, take data and choose proper view to be returned back.
Maybe you should add method on repository that is returning already JSONized data, or introduce "Middle man" that can serialize data returned from repository so controller can call middle man to do the job. Then repository (or "Middle man") can be called from anywhere in code.
e.g.(used Json.NET for json serialization):
public class MiddleMan
{
IRepository repository
public MiddleMan(IRepository repository)
{
this.repository = repository;
}
public string GetJsonObjects(int id)
{
return JsonConvert.SerializeObject(repository.GetObject(id));
}
}
then controller (or anywhere in the code) can call this middle class:
public string Dataset(int id)
{
return new MiddleMan(repository).GetJsonObjects(id);
}
For the time being I'm going to implement a uri extension method something along these lines, creating a WebRequest object for the Uri.
public static string GetContent(this Uri uri)
{
var myRequest = (HttpWebRequest) WebRequest.Create(uri);
myRequest.Method = "GET";
WebResponse myResponse = myRequest.GetResponse();
var sr = new StreamReader(myResponse.GetResponseStream(), System.Text.Encoding.UTF8);
string result = sr.ReadToEnd();
sr.Close();
myResponse.Close();
return result;
}

Resources