How to return view along with model in Async task in Asp.Net MVC - asp.net-mvc

I am testing asynch task in MVC and creating asynchronous task following code. When I return model value along with view name return View("Index", EmpResponse), I am getting error. but if I simply return view return view(). it is working well.
public class AsynchController : Controller
{
string Baseurl = "http://dummy.restapiexample.com/api/v1/";
// GET: Asynch
public async Task<ActionResult> Index()
{
using (var client = new HttpClient())
{
//Passing service base url
client.BaseAddress = new Uri(Baseurl);
client.DefaultRequestHeaders.Clear();
//Define request data format
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
//Sending request to find web api REST service resource GetAllEmployees using HttpClient
HttpResponseMessage Res = await client.GetAsync("employees");
var EmpResponse = "";
//Checking the response is successful or not which is sent using HttpClient
if (Res.IsSuccessStatusCode)
{
//Storing the response details recieved from web api
EmpResponse = Res.Content.ReadAsStringAsync().Result;
//Deserializing the response recieved from web api and storing into the Employee list
}
//returning the employee list to view
return View("Index", EmpResponse);
}
}
In simply ActionResult, I know we can return view name and model both together. is it issue with Async task?

Your intention is to pass EmpResponse as the view-model for your Index view, but since you have already deserialized EmpResponse as a string, it matches the wrong overload of the View() helper method (the one which accepts both viewName and masterName).
Try to pass it as an object to match the correct overload:
return View("Index", EmpResponse as object);
A better approach would be to store the received data as a strongly-typed collection of objects:
var empResponse = await Res.Content.ReadAsAsync<IEnumerable<Employee>>();
Then pass it as a view-model:
return View("Index", empResponse);

This isn't really an async issue, but a model type issue. (Though there is an async issue waiting to become a problem... Don't call .Result directly, but instead use await to get the result.)
Your model is a string. But the overload for View() which takes a second string uses it to find a named view. Which is why it's looking for a view called your long JSON string. (Well, a "master view" in this case since you're sending it two strings.)
Don't use a string as a model. Use a model. Instead of sending one big JSON string to your view, deserialize it into a model of some sort. The type is up to you, but the deserialization might look something like:
var response = await client.GetAsync("employees");
YourModelTypeHere model = null;
if (response.IsSuccessStatusCode)
{
var responseString = await result.Content.ReadAsStringAsync();
model = JsonConvert.DeserializeObject<YourModelTypeHere>(responseString);
}
return View(model);
There may even be an option in result to read/deserialize as your model directly, saving you a line of code above. But the overall principle is the same. Use strongly typed models instead of complex serialized strings.
*In this case, YourModelTypeHere looks like it would in fact be an IEnumerable<YourModel> or perhaps an IList<YourModel>, based on the serialized JSON we're seeing.
*Note also that this uses your current logic of sending an empty model to the same view if nothing was successfully retrieved. For an empty string that may be okay, for null it may become problematic depending on what your view is doing. (Either way your view is going to have to change if it currently expects a string as a model.) Perhaps redirect or return an error in the case of no available model? The logic of how your system should behave is up to you.

Related

Change returned object value after action execution in Web API

In ASP.Net Web API, the action returned object will be converted to XML or JSON automatically - is there a way to add additional process to the returned object before it gets converted?
What I want to achieve is to wrap returned object into a generic APIResponse (another class) type which has an object Property called Data that will be assigned with whatever the original returned object is.
for example:
public Books[] FindBooks(string keyword)
{
..
return new Books[] {new Book(){Name="ASP.NET"}};
}
This will return JSON of book array by default, however I want to wrap the result into an object called APIResponse, so the returned object becomes:
new APIResponse(){
Data = //the Book[] array return from the action method
}
By doing this, I will be able to keep the freedom of returning normal business objects in Web API however always return the same object type in JSON format when the front-end Ajax requests.
I believe it can be done in a way however I'm not familiar with the Web API life cycle, can any way give some guide on this?
Thank you.
I fixed it by creating a custom MediaTypeFormatter however simply inheriting from JSON formatter which have already got all what I need, here is the very simple code I added, which resolved all issues I have!!!
public class APIResponseMediaFomatter : JsonMediaTypeFormatter
{
public override Task WriteToStreamAsync(Type type, object value, System.IO.Stream writeStream, System.Net.Http.HttpContent content, System.Net.TransportContext transportContext)
{
ResponseMessage wrappedValue = null;
if (type != typeof(ResponseMessage) || (value != null && value.GetType() != typeof(ResponseMessage)))
wrappedValue = new ResponseMessage(value);
return base.WriteToStreamAsync(typeof(ResponseMessage), wrappedValue, writeStream, content, transportContext);
}
}
Cheers!
Interestingly, Web API already works exactly how you describe. It already has generic request and response classes that can hold your object payload. Just do the following,
public HttpResponseMessage FindBooks(string keyword)
{
...
var books = new Books[] {new Book(){Name="ASP.NET"}};
var content = new ObjectContent<Book[]>(books,new JsonMediaTypeFormatter());
return new HttpResponseMessage { Content = content);
}
There is no need to re-invent your own generic response object that can hold metadata and data, HTTP has already done that for you.
Why dont you return your wrapper object APIResponse from WebAPI
public APIResponse FindBooks(string keyword)
{
var books = new Books[] {new Book(){Name="ASP.NET"}};
return new APIResponse {Data= books };
}
Just use an action filter, and modify the response content inside it:
public class ApiResponseWrapperActionFilter : ActionFilterAttribute
{
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
// Check that the response is an object content
var objectContent = actionExecutedContext.Response.Content as ObjectContent;
if (objectContent != null)
{
// Wrap the returned value in the ApiResponse
objectContent.Value = new ApiResponse() { Data = objectContent.Value };
}
}
Apply the filter to the whole API (in global config) or a whole controller (attribute applied to the controller class) or to the desired methods (attribute in each method).
If you're returning something that it's not an object (a custom response) it will skip the wrapping.

post object to MVC controller using HttpWebRequest, WebClient etc

I have this api controller action that takes an object "ContentList" as parameter.
[HttpPost]
public List<string> SendList(string param, ContentList list)
{
List<string> testReturn = new List<string> { "test1", "test2", "test3", "test4" };
return testReturn ;
}
What I have tried so far is to call a controller action like this:
Uri _uri = new Uri("http://localhost:xxxxx/api/FakeTest/SendList?param=test");
var serializer = new JavaScriptSerializer();
string requestData = serializer.Serialize(new
{
list = ContentList,
});
using (var client = new WebClient())
{
client.Headers[HttpRequestHeader.ContentType] = "application/json";
var result = client.UploadData(_uri, Encoding.UTF8.GetBytes(requestData));
var tempString = Encoding.UTF8.GetString(result);
}
In this example, tempString = ["test1","test2","test3","test4"] as reurned by the controller action..
In the controller action, I can access the properties of the passed in ContentList, and return their values (changing the actions return value accordingly ofcourse).
However, in the controller action, I need to send off the ContentList object for further processing, and this seems to fail. I get a 500 internal server error, and I can't put a breakpoint in the controller to follow the values passed in. The debugger never hits it...
I expect this has something to do with sending json to the controller action.
Anyway, it seems that the ContentList is rejected by the code it is sent to from the controller action, so I figure I need to do some sort of de-serializing, right?
Bottomline, the question is, what is the correct way to call a controller action from code, pass in a C# object, and make it usable from the controller action?
If you are using MVC 3 your controller should be able to reveive and parse json data in a direct way. If you are using MVC 2 you'll need to register a new factory on your application to take care of json parsing on the controller
protected void Application_Start()
{
RegisterRoutes(RouteTable.Routes);
ValueProviderFactories.Factories.Add(new JsonValueProviderFactory());
}
More info on the subject here:
http://haacked.com/archive/2010/04/15/sending-json-to-an-asp-net-mvc-action-method-argument.aspx

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

Passing ArrayList parameter to controller action ASP.NET MVC

I am writing an application wherein I need to send a System.Collections.ArrayList data as a parameter from one controller action to another.
I am using
return RedirectToAction("action1","controller1", new { arrList = arrListInFirstAction});
But since the ArrayList goes out of scope in the first action, the parameter in the redirected to action receives a null parameter.
Can someone please help me find an answer to this problem.
Thanks.
you can not send complex types as route parameters. you can, however, use TempData collection to keep that object for one request and on next request it will be automatically removed from collection
publci ActionResutl action()
{
TempData["arr"] = new int[]{1,2,3};
return RedirectToAction("action1");
}
Public ActionResult action1()
{
int[] arr = TempData["arr"];
return View();
}

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