Accessing resources via Uri in Asp.net mvc - 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;
}

Related

ASP.NET Core Middleware and URL Parsing

Trying to add special endpoints to an ASP.NET Core MVC through a middleware.
In a app.UseWhen, I need to parse the request URL. In a Controller context, MVC does a great job extracting userId using the following template:
GET http://contoso.com/users/{userId}/addresses
How could this be cleanly done in a middleware where MVC Controller constructs aren't setup?
Bonus points if the answer helps figuring out if the address conforms to this pattern in the first place.
All I have on hand is a DefaultHttpContext.
Solution based on Mark Vincze's blog
This method used to extract the the user id and work with it...
private static void AddAddressesRoute(IApplicationBuilder app, RouteBuilder builder)
{
builder.MapVerb(
HttpMethod.Get.Method,
"users/{userId}/addresses",
async context =>
{
var routeData = context.GetRouteData();
var userId = routeData.Values["userId"];
// userId available from here
}
);
}
Should be initiated from an application builder extension method.
public static IApplicationBuilder UseAddresses(
this IApplicationBuilder app
)
{
RouteBuilder builder = new RouteBuilder(app);
AddAddressesRoute(app, builder);
app.UseRouter(builder.Build());
return app;
}
Becomes a middleware that can be added to the Startup.Configure method just like this:
app.UseAddresses()
It doesn't even interfere with MVC that still gets triggered if the route doesn't match.
URL parsing comes to play in MVC pipeline, not in ASP.NET Core one.
You might want to consider MVC filters instead, which have access to routing context.
You can access the HttpContext from middleware and parse out key-value pairs from the query string but you can not access the path parameters via key-value.
For example:
You make a GET to the following controller via http://contoso.com/api/users/5?zip=90210:
// GET api/users/5
[HttpGet("{id}")]
public ActionResult<string> Get(int id)
{
return "value";
}
Custom Middleware:
public class MyCustomMiddleware
{
public Task InvokeAsync(HttpContext context)
{
// get full path from context request path
var queryPath = context.Request.Path().ToString();
// will return /api/users/5
// get id from query string
var queryStringId = context.Request.Query["zip"].ToString();
// will return 90210
}
}
There isn't any mapping from your Controller parameters to the HttpContext.

what's the best practises for MVC web APi pure JSON?

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

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.

Returning Json from an MVC controller that extends Apicontroller

I'm trying to return a Json string from an MVC controller in a WebAPI application, but am unable to use return Json(... because the class being used extends ApiController and not Controller (I believe).
Is there an alternative method to do what I'm trying to do (e.g. return a different type)? Or a workaround?
This is my controller code:
public class SocialController : ApiController
{
public ActionResult Get(SocialRequest request) // or JsonResult?
{
JavaScriptSerializer js = new JavaScriptSerializer();
string jsontest = js.Serialize(request); // just serializing back and forth for testing
return Json(jsontest, JsonRequestBehavior.AllowGet);
}
}
The error I'm receiving is "System.Web.Helpers.Json is a type but is used like a variable."
I've found the following related SO question but it hasn't solved it for me, if anyone can elaborate I'd really appreciate it (and dish out the rep points):
Why Json() Function is unknown
In Asp.net Web Api you don't have ActionResults anymore. You simply return the object you need. The framework converts the object to the proper response (json, xml or other types)
[HttpGet]
public IEnumerable<string> GetUsers(SocialRequest request)
{
return _applicationsService.GetUserss(request);
}

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);

Resources