Redirecting from RedirectToLogin.razor gives error - redirect

I have the following code:
App.razor
#using DorpshuisManager.Areas.Identity.Components
<CascadingAuthenticationState>
<Router AppAssembly="#typeof(App).Assembly">
<Found Context="routeData">
<AuthorizeRouteView RouteData="#routeData" DefaultLayout="#typeof(MainLayout)">
<NotAuthorized>
#if (!context.User.Identity.IsAuthenticated)
{
<RedirectToLogin ReturnUrl="#NavigationManager.ToBaseRelativePath(NavigationManager.Uri)" />
}
else
{
<p role="alert">Sorry, you're not authorized to view this page.</p>
}
</NotAuthorized>
</AuthorizeRouteView>
<FocusOnNavigate RouteData="#routeData" Selector="h1" />
</Found>
<NotFound>
<PageTitle>Not found</PageTitle>
<LayoutView Layout="#typeof(MainLayout)">
<div class="d-flex vw-100 vh-100 align-items-center justify-content-center text-center">
<div>
<iconify-icon icon="tabler:face-id-error" style="color: black;" width="100" height="100"></iconify-icon>
<h1>Pagina niet gevonden ...</h1>
<a href="/">
<button class="btn btn-primary mt-3 btn-rounded py-3 px-4">Ga naar home pagina</button>
</a>
</div>
</div>
</LayoutView>
</NotFound>
</Router>
</CascadingAuthenticationState>
RedirectToLogin.razor
#code {
[Parameter]
public string ReturnUrl { get; set; }
protected override async Task OnInitializedAsync()
{
ReturnUrl = "~/" + ReturnUrl;
NavigationManager.NavigateTo("Identity/Account/Login?returnUrl=" + ReturnUrl, true);
await base.OnInitializedAsync();
}
}
But I get the following error:
I searched on Google and Stack Overflow, but all given anwsers are not working...
I also tried aspnetcore blazor navigation from razor component to razor page but this also didn't work

Try this:
Redirect component:
#inject NavigationManager NavManager
#if(IsAuthenticated)
{
<div class="alert alert-danger">Sorry, you're not authorized to view this page.</div>
}
else
{
<div class="alert alert-info">Redirecting to the Log In Page.</div>
}
#code {
[Parameter] public bool IsAuthenticated { get; set; }
[Parameter] public string ReturnUrl { get; set; } = string.Empty;
protected async override Task OnAfterRenderAsync(bool firstRender)
{
// added to show the messsge
await Task.Delay(2000);
ReturnUrl = "/" + ReturnUrl;
if (!IsAuthenticated)
this.NavManager.NavigateTo("Identity/Account/Login?returnUrl=" + ReturnUrl, true);
}
}
And App:
<AuthorizeRouteView RouteData="#routeData" DefaultLayout="#typeof(MainLayout)">
<NotAuthorized>
<ReturnToLogin IsAuthenticated="context.User.Identity?.IsAuthenticated ?? false" />
</NotAuthorized>
</AuthorizeRouteView>

Related

Returning object details in the View based on the object ID ASP.net core MVC

I'm building online catalog for phones, I have two controller one for phone Catalog and second for phone's details.With catalog everything is fine, now my goal is to see phone details after clicking on the phone's name or photo in the catalog.I think that with phone's id, its easier to solve this task, also I use repository pattern.
This Catalog' s Controller:
public class PhonesCatalog : Controller
{
private readonly IPhoneRepository _repository;
public int PageSize = 6;
public PhonesCatalog(IPhoneRepository repository)
{
_repository = repository;
}
[HttpGet]
public IActionResult Catalog(int productPage = 1)
=> View(new ProductsListViewModel
{
Phones = _repository.Phones
.OrderBy(x => x.PhoneId)
.Skip((productPage -1) * PageSize)
.Take(PageSize),
PagingInfo = new PagingInfo
{
CurrentPage = productPage,
ItemsPrePage = PageSize,
TotalItems = _repository.Phones.Count()
}
});
}
The repository pattern:
public interface IPhoneRepository
{
IQueryable<Phone> Phones { get; }
Phone GetPhoneById(int id);
}
public class EfMobileStoreRepository : IPhoneRepository
{
private readonly MobileStoreCatalogContext _context;
public EfMobileStoreRepository(MobileStoreCatalogContext context)
{
_context = context;
}
public IQueryable<Phone> Phones => _context.Phones;
public Phone GetPhoneById(int id) => _context.Phones
.FirstOrDefault(p => p.PhoneId == id);
}
and here is Phone detail controller and view:
public class PhonesDetails : Controller
{
private readonly IPhoneRepository _repository;
public PhonesDetails(IPhoneRepository repository)
{
_repository = repository;
}
[HttpGet]
public IActionResult Details(int id)
{
return View(_repository.GetPhoneById(id));
}
}
#model Mobile_Store_Catalog_wandio.Models.Phone
<h4>Phone Details</h4>
<div class="row">
<p>#Model.PhoneName</p>
<p>#Model.Manufactor</p>
<p>#Model.OperationSystem</p>
<p>#Model.Processor</p>
<p>#Model.Memory</p>
<p>#Model.ScreenResolution</p>
<p>#Model.Size</p>
<p>#Model.Wight</p>
</div>
Here Catalog View:
#model ProductsListViewModel
<h1>Phone Catalog</h1>
<div class="container-fluid">
<div class="container-fluid">
<div class="row">
<div class="col-md-3 btn-group-vertical text-center">
Filter
Search
</div>
<div class=" row col-md-9">
#foreach (var p in Model.Phones)
{
<div class=" col-md-4 border border-dark">
<a href="#Url.Action("Details", "PhonesDetails")">
<img class="img-fluid" src="/Images/#p.ImageName"/>
</a>
<p class="text-center container">#p.PhoneName</p>
<p class="text-white text-center bg-success">#p.Price.ToString("C2")</p>
</div>
}
</div>
</div>
</div>
</div>
<div class="row">
<div page-model="#Model.PagingInfo" page-action="Catalog" page-classes-enabled="true"
page-class="btn" page-class-normal="btn-outline-dark"
page-class-selected="btn-primary" class="btn-group-toggle m-1 al">
</div>
</div>
and here is the ProductViewList code:
public class ProductsListViewModel
{
public IEnumerable<Phone> Phones { get; set; }
public PagingInfo PagingInfo { get; set; }
}
Problem is that when I'm clicking on the phone image,but it takes only first id of phone and returns its detail,it doesn't matter which image is clicked, but I need to return details by id, not only first id Phone detail.
What I'm doing wrong here, can anyone help me?
You can change your code like this:
<a asp-controller="PhonesDetails" asp-action="Details" asp-route-id="#p.Id">
<img class="img-fluid" src="/Images/#p.ImageName"/></a>

How to update a MVC Form before Submit

So i have 2 Viewbags, they each have a list of values from a database table, the first Viewbag have all possible values of a database column, while the other have only the values corresponding to the selected value in the first Viewbag.
I have the logic for the search.
but i need to have the form update after selecting one value, since they both need to be in the same form, it is not searching for the second value.
OBS:i am only using the controllers, and cshtml views, not razor pages.
Here is a simple demo how to create cascading selectlist in asp.net core mvc:
Model:
public class Category
{
public int Id { get; set; }
public string Name { get; set; }
}
public class SubCategory
{
public int Id { get; set; }
public int CategoryId { get; set; }
public string SubCategoryName { get; set; }
}
View(Index.cshtml):
<div>
<div style="float:left;width:40%">
<form id="form">
<div class="form-group row">
<label>Category Name</label>
<div class="col-12">
<select id="CategoryId" class="custom-select mr-sm-2"
asp-items="#(new SelectList( #ViewBag.Category,"Id","Name"))">
<option value="">Please Select</option>
</select>
</div>
</div>
<div class="form-group row">
<label>SubCategory Name</label>
<div class="col-12">
<select id="SubCategoryId" class="custom-select mr-sm-2"
asp-items="#(new SelectList(string.Empty,"Id","SubCategoryName"))">
<option value="">Please Select</option>
</select>
</div>
</div>
<div>
<input type="button" value="Search" />
</div>
</form>
</div>
</div>
#section Scripts
{
<script>
$(function () {
$('#CategoryId').change(function () {
var data = $("#CategoryId").val();
$.ajax({
url: '/Home/GetSubCategory?CategoryId=' + data,
type: 'Get',
success: function (data) {
var items = "";
$.each(data, function (i, item) {
items += "<option value='" + item.value + "'>" + item.text + "</option>";
});
$('#SubCategoryId').html(items);
}
})
});
})
</script>
}
Controller:
public class HomeController : Controller
{
private readonly MvcProj3Context _context;
public HomeController(MvcProj3Context context)
{
_context = context;
}
public IActionResult Index()
{
ViewBag.Category = _context.Category.ToList();
return View();
}
public JsonResult GetSubCategory(int CategoryId)
{
ViewBag.SubCategory = (from m in _context.SubCategory
where m.CategoryId == CategoryId
select m).ToList();
return Json(new SelectList(ViewBag.SubCategory, "Id", "SubCategoryName"));
}
}
Result:
The problem is, that the .cshtml file is completely rendered before its send to your browser. Therefore you cannot change it with C# code after its sent to the browser.
You could use blazor if you want to do it with c#, or you could do it with javascript.

Use of View Components with forms

I am trying to learn the correct usage of View Components in ASP.NET Core MVC, so I have the following example: The idea is to render a view with Movie details, and inside of it, the ReviewViewComponent holding a 10-star movie rating widget.
Inside of the View Component's View is a form with radio buttons. The form action name is passed to the View Component (either Create or Edit, depending on if the user already gave a rating). Depending on the received action name, the form inside the View Component will either call the Create or the Edit method inside of the ReviewsController.
This all works until I reach the return call inside of ReviewsController. I would like to be able to return the View Component there and simply render the return result inside the div with id="now-showing-details-rating-div" in Details using AJAX. This works with Partial Views (code commented out in the ReviewsController's Edit method), but it seems it doesn't work with View Components (it just renders the View Component's View as a completely new View, even though I call the AJAX on the form the same way I would if it were in a Partial View).
Am I actually misusing the View Component concept here? In the sense of rendering a portion of a View after form submit, is it actually better to just use Partial Views?
Ajax snippet
$.ajax({
type: "POST",
url: requestUrl,
data: form.serialize(),
success: function (data) {
$("#" + divZaRezultat).html(data);
}
});
ViewModels:
public class MovieDetailsVM
{
public string Title { get; set; }
public int Id { get; set; }
public int Year { get; set; }
public string Actors { get; set; }
public string Country { get; set; }
public string Directors { get; set; }
public int Duration { get; set; }
public string VideoLink { get; set; }
public string AverageRating { get; set; }
public string NumberOfReviews { get; set; }
public ReviewIndexVM CurrentUserReview { get; set; }
}
public class ReviewIndexVM
{
public int ReviewId { get; set; }
public int Rating { get; set; }
public MasterModel User { get; set; }
public MasterModel Movie { get; set; }
}
ViewComponent
public class ReviewViewComponent : ViewComponent
{
public async Task<IViewComponentResult> InvokeAsync(string methodName, ReviewIndexVM review)
{
ViewBag.Method = methodName;
return View(review);
}
}
ViewComponent Default View
#model Cinema.DTO.ViewModels.Reviews.ReviewIndexVM
#{
ViewData["Title"] = "Default";
}
<form asp-controller="Reviews" asp-action="#ViewBag.Method">
<input asp-for="ReviewId" hidden />
<input asp-for="Movie.Id" hidden />
<input asp-for="User.Id" hidden />
<div class="rating form-group">
#for (int i = 10; i > 0; i--)
{
<input asp-for="Rating" type="radio" value="#i" id="#($"rating-star-{i}")" onclick="this.form.submit();" class="form-control rating-star"><label class="rating-star-label" for="#($"rating-star-{i}")"></label>
}
</div>
</form>
Views:
#model Cinema.DTO.ViewModels.Movies.MovieDetailsVM
#using Microsoft.AspNetCore.Identity
#using Cinema.Domain.Entities.Identity
#inject SignInManager<ApplicationUser> SignInManager
#inject UserManager<ApplicationUser> UserManager
#{
ViewData["Title"] = "Details";
Layout = "~/Views/Shared/_Layout.cshtml";
bool first = true;
DateTime currentDate = DateTime.Now;
}
<section>
<div class="container">
<div class="content-wrap">
<div class="row">
<h1 class="h2">#Html.DisplayFor(model => model.Title)</h1>
</div>
<div class="row">
<div class="col-md-4">
<img id="movie-poster" class="pull-left" src="~/img/movie-poster.png" />
</div>
<div class="col-md-8">
<ul class="list-unstyled movie-info">
<li>
<span>#Html.DisplayNameFor(model => model.Title)</span>
#Html.DisplayFor(model => model.Title)
</li>
<li>
<span>#Html.DisplayNameFor(model => model.Year)</span>
#Html.DisplayFor(model => model.Year)
</li>
<li>
<span>#Html.DisplayNameFor(model => model.Actors)</span>
#Html.DisplayFor(model => model.Actors)
</li>
<li>
<span>#Html.DisplayNameFor(model => model.Country)</span>
#Html.DisplayFor(model => model.Country)
</li>
<li>
<span>#Html.DisplayNameFor(model => model.Directors)</span>
#Html.DisplayFor(model => model.Directors)
</li>
<li>
<span>#Html.DisplayNameFor(model => model.Duration)</span>
#Html.DisplayFor(model => model.Duration)
</li>
#*<li>
<span>#Html.DisplayNameFor(model => model.GenreMovies)</span>
#Html.DisplayFor(model => model.GenreMovies)
</li>*#
<li>
<span>#Html.DisplayNameFor(model => model.VideoLink)</span>
#Html.DisplayFor(model => model.VideoLink)
</li>
</ul>
Average rating <span class="badge">#Model.AverageRating</span>
<hr />
<div asp-authorize asp-roles="#Roles.User">
Your rating:
<div id="now-showing-details-rating-div">
#if (#Model.CurrentUserReview.ReviewId == 0)
{
#await Component.InvokeAsync("Review", new { methodName = "Create", review = #Model.CurrentUserReview })
}
else
{
#await Component.InvokeAsync("Review", new { methodName = "Edit", review = #Model.CurrentUserReview })
}
</div>
</div>
</div>
</div>
</div>
</section>
#section Scripts {
$(document).ready(function () {
$('.rating-star-label').mouseover(function () {
$('.rating-star').prop('checked', false);
});
});
</script>
}
ReviewsController
[HttpGet]
[Authorize(Roles = Roles.User)]
public async Task<IActionResult> Edit(int reviewId)
{
Review review = await _unit.Reviews.GetAsync(reviewId);
var authorizationResult = await _authorizationService.AuthorizeAsync(User, review, OperationRequirements.Update);
if (authorizationResult.Succeeded)
{
ReviewUpdateVM model = review.ToUpdateVM();
return PartialView(model);
}
else if (User.Identity.IsAuthenticated)
{
return new ForbidResult();
}
else
{
return new ChallengeResult();
}
}
[Authorize(Roles = Roles.User)]
public async Task<IActionResult> Edit(ReviewIndexVM model)
{
Review review = model.Create();
var authorizationResult = await _authorizationService.AuthorizeAsync(User, review, OperationRequirements.Update);
if (authorizationResult.Succeeded)
{
await _unit.Reviews.UpdateAsync(review, model.ReviewId);
await _unit.SaveAsync();
return ViewComponent("Review");
//return Redirect("/Reviews/Details?reviewId=" + review.Id);
}
else if (User.Identity.IsAuthenticated)
{
return new ForbidResult();
}
else
{
return new ChallengeResult();
}
}
}
else if (User.Identity.IsAuthenticated)
{
return new ForbidResult();
}
else
{
return new ChallengeResult();
}
}
Here is a working demo:
Details.cshtml:
#model MovieDetailsVM
#{
ViewData["Title"] = "Details";
Layout = "~/Views/Shared/_Layout.cshtml";
bool first = true;
DateTime currentDate = DateTime.Now;
}
<section>
<div class="container">
<div class="content-wrap">
<div class="row">
<h1 class="h2">#Html.DisplayFor(model => model.Title)</h1>
</div>
<div class="row">
<div class="col-md-8">
<ul class="list-unstyled movie-info">
<li>
<span>#Html.DisplayNameFor(model => model.Title)</span>
#Html.DisplayFor(model => model.Title)
</li>
<li>
<span>#Html.DisplayNameFor(model => model.Year)</span>
#Html.DisplayFor(model => model.Year)
</li>
<li>
<span>#Html.DisplayNameFor(model => model.Actors)</span>
#Html.DisplayFor(model => model.Actors)
</li>
<li>
<span>#Html.DisplayNameFor(model => model.Country)</span>
#Html.DisplayFor(model => model.Country)
</li>
<li>
<span>#Html.DisplayNameFor(model => model.Directors)</span>
#Html.DisplayFor(model => model.Directors)
</li>
<li>
<span>#Html.DisplayNameFor(model => model.Duration)</span>
#Html.DisplayFor(model => model.Duration)
</li>
#*<li>
<span>#Html.DisplayNameFor(model => model.GenreMovies)</span>
#Html.DisplayFor(model => model.GenreMovies)
</li>*#
<li>
<span>#Html.DisplayNameFor(model => model.VideoLink)</span>
#Html.DisplayFor(model => model.VideoLink)
</li>
</ul>
Average rating <span class="badge">#Model.AverageRating</span>
<hr />
<div>
Your rating:
<div id="now-showing-details-rating-div">
#if (#Model.CurrentUserReview == null)
{
#await Component.InvokeAsync("Review", new { methodName = "Create", review = #Model.CurrentUserReview })
}
else
{
#await Component.InvokeAsync("Review", new { methodName = "Edit", review = #Model.CurrentUserReview })
}
</div>
</div>
</div>
</div>
</div>
</div>
</section>
Ajax in Details.cshtml:
#section Scripts {
<script>
$(document).ready(function () {
$('.rating-star-label').mouseover(function () {
$('.rating-star').prop('checked', false);
});
});
function Update() {
$.ajax({
type: "POST",
url: "/Reviews/Edit/#Model.CurrentUserReview.ReviewId",
data: $("form").serialize(),
success: function (data) {
$("#now-showing-details-rating-div").html(data);
}
});
}
</script>
}
Components/Review/Default.cshtml:
#model ReviewIndexVM
#{
ViewData["Title"] = "Default";
}
<form asp-controller="Reviews" asp-action="#ViewBag.Method">
<input asp-for="ReviewId" hidden />
<input asp-for="Movie.Id" hidden />
<input asp-for="User.Id" hidden />
<div class="rating form-group">
#for (int i = 10; i > 0; i--)
{
<input asp-for="Rating" type="radio" value="#i" id="#($"rating-star-{i}")" onclick="Update();" class="form-control rating-star"><label class="rating-star-label" for="#($"rating-star-{i}")"></label>
}
</div>
</form>
Controller:
public class ReviewsController : Controller
{
private readonly Component2_2Context _context;
public ReviewsController(Component2_2Context context)
{
_context = context;
}
// GET: Reviews/Details/5
public async Task<IActionResult> Details(int? id)
{
var reviewIndexVM = await _context.MovieDetailsVM
.Include(m => m.CurrentUserReview)
.FirstOrDefaultAsync(m => m.Id == id);
return View(reviewIndexVM);
}
// GET: Reviews/Edit/5
public async Task<IActionResult> Edit(int? id)
{
if (id == null)
{
return NotFound();
}
var reviewIndexVM = await _context.ReviewIndexVM.FindAsync(id);
if (reviewIndexVM == null)
{
return NotFound();
}
return View(reviewIndexVM);
}
// POST: Reviews/Edit/5
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(int id, [Bind("ReviewId,Rating")] ReviewIndexVM reviewIndexVM)
{
if (id != reviewIndexVM.ReviewId)
{
return NotFound();
}
if (ModelState.IsValid)
{
_context.Update(reviewIndexVM);
await _context.SaveChangesAsync();
return ViewComponent("Review");
}
return new ChallengeResult();
}
ViewComponent:
public class ReviewViewComponent : ViewComponent
{
public async Task<IViewComponentResult> InvokeAsync(string methodName, ReviewIndexVM review)
{
ViewBag.Method = methodName;
return View(review);
}
}
Result:

Asp Net Core - Entity Framework - ViewBag - Related Data - CRUD

I am creating a simple Location and Country relationship and i have been able to link the to tables and i am able to pull the related data, the issue is that when i try post the form the database does not update with the ID of the country.
When monitoring the POST action via Firefox Developer tools i can see the ID of the country is being posted.
LocationsController
public class LocationsController : Controller
{
private readonly DataContext _context;
public LocationsController(DataContext context)
{
_context = context;
}
// DropDown: Populate the Dropdown lists
private void PopulateCountryDropDownList(object selectedCountry = null)
{
var countriesQuery = from c in _context.Countries
orderby c.CountryID
select c;
ViewBag.CountryID = new SelectList(countriesQuery.AsNoTracking(), "CountryID", "Title");
}
// GET: Locations/Create
public IActionResult Create()
{
PopulateCountryDropDownList();
return View();
}
// POST: Locations/Create
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create(Location location)
{
if (ModelState.IsValid)
{
_context.Add(location);
await _context.SaveChangesAsync();
return RedirectToAction("Index");
}
return View(location);
}
}
Location
using System.ComponentModel.DataAnnotations;
namespace RaceCaribbean.Models
{
public class Location
{
public int LocationID { get; set; }
public string Title { get; set; }
public Country Country { get; set; }
}
}
Country
using System.ComponentModel.DataAnnotations;
namespace RaceCaribbean.Models
{
public class Country
{
public int CountryID { get; set; }
[Required]
public string Title { get; set; }
[Required]
public string Code { get; set; }
}
}
Create.cshtml
#model RaceCaribbean.Models.Location
#{
ViewData["Title"] = "Create";
}
<h2>Create</h2>
<form asp-action="Create">
<div class="form-horizontal">
<h4>Location</h4>
<hr />
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="Title" class="col-md-2 control-label"></label>
<div class="col-md-10">
<input asp-for="Title" class="form-control" />
<span asp-validation-for="Title" class="text-danger"></span>
</div>
</div>
<div class="form-group">
<label asp-for="Country" class="col-md-2 control-label"></label>
<div class="col-md-10">
<select asp-for="Country" class="form-control" asp-items="ViewBag.CountryID">
<option selected="selected" value="">-- Select Country --</option>
</select>
<span asp-validation-for="Country" class="text-danger" />
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
</form>
<div>
<a asp-action="Index">Back to List</a>
</div>
#section Scripts {
#{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}

Servicestack + model binding on json post using AngularJs

I'm calling a REST service developed in serviceStack, I'm using angularJs as a client but my problem is that for some reason I can't bind my json model with my requestDTO.
my angularJs code:
I have a AngularJs Service
angular.module('mseApp.services', ['ngResource']).
factory('loginService',
function ($resource) {
return $resource('/Api/User/login', '',
{
query: {
method: 'POST',
params:{ }
}
});
});
controller:
function loginCtrl($scope, loginService) {
$scope.submit = function () {
var user = loginService.query({loginRequest:$scope.loginRequest});
};
}
View:
<div>
<div ng-model="loginRequest">
<form ng-submit="submit()" ng-controller="loginCtrl">
<input id="username" ng-model="loginRequest.Username" type="text" name="text" />
<input id="password" ng-model="loginRequest.Password" type="text" name="text" />
<input type="submit" id="submit" value="Submit" />
<span ng-model="Username"></span>
</form>
</div>
</div>
on the dot net side
my DTO
public class LoginRequest
{
public string Username { get; set; }
public string Password { get; set; }
}
public UserResponse Post(LoginRequest loginRequest)
{
}
everything if fine the only problem is that LoginRequest is null on my service method.
Any ideas???
Just send
query($scope.loginRequest)
instead of
query({loginRequest:$scope.loginRequest})
that will solve your issue.
You can also utilize the $promise object of your query to defer processing of your binding to $scope
function loginCtrl($scope, loginService) {
$scope.submit = function () {
var user = loginService.query.$promise.then(function (promise) {
$scope.userResponse = promise.d;
});
};
}
then bind to (assuming your Username is at UserResponse.Username) like this
<span ng-model="UserResponse.Username"></span>