TempData is null after redirection - asp.net-core-mvc

I want use TempData to pass messages between Test1 and Test2 actions but when I read tempdata's key on Test2's action the value on TempData key is null. I read the documentation about TempData on MDSN this is what it say:
Represents a set of data that persists only from one request to the
next.
My code is that:
public class TestController : Controller
{
public const string TEMP_DATA_KEY = "TEST";
public IActionResult Test1()
{
TempData[TEMP_DATA_KEY] = "ciao";
return RedirectToAction(nameof(Test2));
}
public IActionResult Test2()
{
TempData.TryGetValue(TEMP_DATA_KEY, out object saluto);
return View();
}
}
What is wrong, Test1 is first request Test2 is next request why TempData is null?
Thank you regards

In your controller update the Test2 Action as noted below:
public const string TEMP_DATA_KEY = "TEST";
public IActionResult Test1()
{
TempData[TEMP_DATA_KEY] = "ciao";
return RedirectToAction(nameof(Test2));
}
public IActionResult Test2()
{
TempData.TryGetValue(TEMP_DATA_KEY, out object saluto);
return View(saluto);
}
Update your View (Test2.cshtml) as shown below.
#model string
#{
ViewData["Title"] = "Test2";
}
<h1>#Model</h1>
This wil show the value you've added in Test1 into your TempData in the h1 tag on the Test2 View.

Related

spring CRUD DELETE action that return viewmodel or empty body

I want to write a DELETE action that return a no content body if no id error exist. If id not exist I want to redirect to the coresponding GET view.
Controller code:
#RequestMapping(value = "/todo/delete/{id}", method = RequestMethod.GET)
public String getDeleteTodo(Model model, #PathVariable("id") String id)
{
Optional<Todo> todo = todoRepository.findById(Long.decode(id));
if (todo.isEmpty()) {
model.addAttribute("msginfo", "ctl-todo.delete.msginfo.id-not-exist");
model.addAttribute("requestedId", id);
}
else {
model.addAttribute("todo", todo.get());
}
return "v-todo-delete";
}
#RequestMapping(value = "/todo/delete/{id}", method = RequestMethod.DELETE)
public String deleteTodo(#PathVariable String id, RedirectAttributes redirAttrs)
{
boolean exists = todoRepository.existsById(Long.decode(id));
if (exists) {
todoRepository.deleteById(Long.decode(id));
return ""; //here I want to return a no-content body response
}
else {
redirAttrs.addFlashAttribute("msginfo", "ctl-todo.delete.msginfo.id-not-exist");
redirAttrs.addFlashAttribute("requestedId", id);
return "redirect:/todo/delete" + id;
}
}
More informations about the view:
The GET view is juste a view that display the todo entity corresponding to the id. The deletion is make with a button using ajax to call the DELETE method. Then response is return as 204 with no content into the body, i redirect the user with javascript to the main page... If an id not exist in the DELETE method, I want to redirect to the GET method to show an error message.
If someone have an idea to do this.
Thanks in advance.
Try using return type as ResponseEntity with whatever response body along with a response status. Please refer below code changes:
#RequestMapping(value = "/todo/delete/{id}", method = RequestMethod.DELETE)
public ResponseEntity deleteTodo(#PathVariable String id, RedirectAttributes redirAttrs)
{
boolean exists = todoRepository.existsById(Long.decode(id));
if (exists) {
todoRepository.deleteById(Long.decode(id));
return new ResponseEntity(HttpStatus.NO_CONTENT); //This will return No Content status
}
else {
redirAttrs.addFlashAttribute("msginfo", "ctl-todo.delete.msginfo.id-not-exist");
redirAttrs.addFlashAttribute("requestedId", id);
return new ResponseEntity( "redirect:/todo/delete" + id, HttpStatus.OK);
}
}
Final anwser for me:
#RequestMapping(value = "/todo/delete/{id}", method = RequestMethod.DELETE)
public ResponseEntity<?> deleteTodo(#PathVariable String id, RedirectAttributes redirAttrs)
{
boolean exists = todoRepository.existsById(Long.decode(id));
if (exists) {
todoRepository.deleteById(Long.decode(id));
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
else {
redirAttrs.addFlashAttribute("msginfo", "ctl-todo.delete.msginfo.id-not-exist");
redirAttrs.addFlashAttribute("requestedId", id);
/* I use CONFLICT here to explain that the entity was possibly deleted
by another user between the moment the user give the view containing
the DELETE ajax link and the moment he click on it. */
return new ResponseEntity<String>( "redirect:/todo/delete" + id, HttpStatus.CONFLICT);
}
}
Thank you Mandar Dharurkar & Jeethesh Kotian for your help ;)

Access TempData in ExecuteResult Asp.Net MVC Core

I wanted to save notification in TempData and shown to user. I create extension methods for this and implement a class which Extends from ActionResult. I need to access TempData in override ExecuteResult method with ActionContext.
Extension Method:
public static IActionResult WithSuccess(this ActionResult result, string message)
{
return new AlertDecoratorResult(result, "alert-success", message);
}
Extends ActionResult class.
public class AlertDecoratorResult : ActionResult
{
public ActionResult InnerResult { get; set; }
public string AlertClass { get; set; }
public string Message { get; set; }
public AlertDecoratorResult(ActionResult innerResult, string alertClass, string message)
{
InnerResult = innerResult;
AlertClass = alertClass;
Message = message;
}
public override void ExecuteResult(ActionContext context)
{
ITempDataDictionary tempData = context.HttpContext.RequestServices.GetService(typeof(ITempDataDictionary)) as ITempDataDictionary;
var alerts = tempData.GetAlert();
alerts.Add(new Alert(AlertClass, Message));
InnerResult.ExecuteResult(context);
}
}
Call extension method from controller
return RedirectToAction("Index").WithSuccess("Category Created!");
I get 'TempData ' null , How can I access 'TempData' in 'ExecuteResult' method.
I was literally trying to do the exact same thing today (have we seen the same Pluralsight course? ;-) ) and your question led me to find how to access the TempData (thanks!).
When debugging I found that my override on ExecuteResult was never called, which led me to try the new async version instead. And that worked!
What you need to do is override ExecuteResultAsync instead:
public override async Task ExecuteResultAsync(ActionContext context)
{
ITempDataDictionaryFactory factory = context.HttpContext.RequestServices.GetService(typeof(ITempDataDictionaryFactory)) as ITempDataDictionaryFactory;
ITempDataDictionary tempData = factory.GetTempData(context.HttpContext);
var alerts = tempData.GetAlert();
alerts.Add(new Alert(AlertClass, Message));
await InnerResult.ExecuteResultAsync(context);
}
However, I have not fully understood why the async method is called as the controller is not async... Need to do some reading on that...
I find out the way to get the TempData. It need to get from ITempDataDictionaryFactory
var factory = context.HttpContext.RequestServices.GetService(typeof(ITempDataDictionaryFactory)) as ITempDataDictionaryFactory;
var tempData = factory.GetTempData(context.HttpContext);

OData routing and action

From example on other site:
In WebApiConfig.cs
EntitySetConfiguration<ContactType> contactType = builder.EntitySet<ContactType>("ContactType");
var actionY = contactType.EntityType.Action("ChangePersonStatus");
actionY.Parameter<string>("Level");
actionY.Returns<bool>();
var changePersonStatusAction = contactType.EntityType.Collection.Action("ChangePersonStatus");
changePersonStatusAction.Parameter<string>("Level");
changePersonStatusAction.Returns<bool>();
In ContactTypeController
[HttpPost]
[ODataRoute("Default.ChangePersonStatus")]
public IHttpActionResult ChangePersonStatus(ODataActionParameters parameters)
{
if (ModelState.IsValid)
{
var level = parameters["Level"];
// SAVE THIS TO THE DATABASE OR WHATEVER....
}
return Ok(true);
}
Now the action can be called:
For the Entity:
http://localhost:51902/odata/ContactType(5)/Default.ChangePersonStatus
For the Entity Collection:
http://localhost:51902/odata/ContactType/Default.ChangePersonStatus
I don't understand how we retrieve Id = 5 of ContactType in the action and save something to database by this ID when use ..odata/ContactType(5)/Default.ChangePersonStatus link
For the Entity Collection:
http://localhost:51902/odata/ContactType/Default.ChangePersonStatus
Your method in controller should be as follows:
[HttpPost]
[ODataRoute("ContactType/Default.ChangePersonStatus")]
public IHttpActionResult ChangePersonStatus(ODataActionParameters parameters)
{
...
}
For the Entity:
http://localhost:51902/odata/ContactType(5)/Default.ChangePersonStatus
Your method in controller should be as follows:
[HttpPost]
[ODataRoute("ContactType({key})/Default.ChangePersonStatus")]
public IHttpActionResult ChangePersonStatus(int key, ODataActionParameters parameters)
{
...
}
key will have value 5;
To reference a single entity, you need to include [FromODataUri] before the key:
[HttpPost]
[ODataRoute("ContactType({key})/Default.ChangePersonStatus")]
public IHttpActionResult ChangePersonStatus([FromODataUri] int key, ODataActionParameters parameters)
{
// Code
}
You should now be able to access your id within the method.

WebAPI 2.0 Multiple Get on single controller

I'm currently trying to implement a simple WebAPI 2.0 controller to get and retrieve users from a central table.
Looking at implementing :
GetUserByName(string userName)
GetUserByID(int userId)
GetUserByEmail(string email)
Using Routing I have been able to get the api to work with GetById and GetByName. I have also added a route prefix to the controller level
The code looks like this so far, There isn't very much being done in the API controller at the moment I just wish to test that the correct methods are being hit.
[RoutePrefix("api/user")]
public class UserController : ApiController
{
[Route("")]
[HttpGet]
public IEnumerable<User> Get()
{
return new List<User>();
}
// GET: api/Users/5
[Route("{id:int}")]
public User Get(int id)
{
return new User();
}
[Route("{id}/PasswordHash")]
[HttpGet]
public string PasswordHash(int id)
{
return "test";
}
[Route(Name = "/{userName:alpha}")]
public User GetByName(string userName)
{
return new User();
}
[Route(Name = "/GetByEmail/{email}")]
[HttpGet]
public User GetByEmail(string email)
{
return new User()
}
// POST api/<controller>
[HttpPost]
[Route("")]
public string Post([FromBody]User value)
{
return "test";
}
}
Once I add in the get by email it doesn't seem to work, I've tried giving the method it's own custom routing but unfortunately it doesn't seem to work. Any suggestions would be great.
You have set the name of the routes not the routes themselves, and since both methods have the same signature with one string variable you get the problem, so change your code to be :
[Route("/{userName:alpha}")]
public User GetByName(string userName)
{
return new User();
}
[Route("/GetByEmail/{email}")]
[HttpGet]
public User GetByEmail(string email)
{
return new User()
}
hope this helps.

Assert.IsInstanceOfType failed in Moq Package in Mvc3

I am Using Moq package for testing a Controller.
HomeController.cs
public class HomeController : Controller
{ readonly IPermitRepository _repository;
public HomeController(IPermitRepository rep)
{ this._repository = rep; }
public ViewResult Index()
{
ViewBag.Message = "Hello World";
PermitModel model = _repository.GetPermitDetails();
return View(model);
}
}
In HomeControllerTest.cs
[TestClass]
Public class HomeControllerTest
{
[TestMethod]
public void Index()
{
var messagingService = new Mock<IPermitRepository>();
var controller = new HomeController(messagingService.Object);
var result = controller.Index() as ViewResult;
Assert.IsInstanceOfType(result.Model, typeof(PermitModel));
}
}
But its giving error.
Assert.IsInstanceOfType failed. Expected type:. Actual type:<(null)>.
Can some one provide solution and also some inf about Moq package in MVC3.
Thanks in advance
Moq returns null by default for each non void method call.
So when in your controller you call _repository.GetPermitDetails(); it return null that's why your test fails.
You need to call Setup on your method to return something:
var messagingService = new Mock<IPermitRepository>();
messagingService.Setup(m => m.GetPermitDetails()).Returns(new PermitModel());
var controller = new HomeController(messagingService.Object);
You can find more info in the Moq quickstart on how to customize the mock behaviour.

Resources