I have problem using redirect to another action. here is what I send from my View:
#using(Html.BeginForm("redirect","Home",FormMethod.Get,new { id="Indexform",name="Myform" }))
{<input type="hidden" name="id" value="1" />}
And here is controller:
public ActionResult redirect(string id = ""){
return RedirectToAction("MainSearch","Home",new
{
My_Categories = id,
Area = "New England"
});
}
and the target action is MainSearch in Home Controller:
public ActionResult MainSearch(string My_Categories = "", string Area="Wales"){
ViewBag.Area=Area;
ViewBag.Category=My_Categories;
return View();
}
My Routing Configuration is as following:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Priosa",action = "Huvudmeny",id = UrlParameter.Optional }
);
}
Why do I get this error:
No route in the route table matches the supplied values.
Related
In my Angular app I am trying to save image and text inside a table. Everything is working fine. I can add the the data, I can get the data. But the problem is if I click on save, data shows inside the table but only texts can be seen without refresh, image is showing the alt image value.
But if I refresh the page the page it works perfectly.
and in the table I can see something like this,
Here is my service file:
#Injectable({
providedIn: 'root'
})
export class CategoriesService {
private categories:Category[] = [];
private categoryUpdated = new Subject<Category[]>();
constructor(private http : HttpClient, private router: Router) { }
getUpdateListener(){
return this.categoryUpdated.asObservable();
}
/* posting request */
addCategory(name: string, image: File){
const categoryData = new FormData();
categoryData.append('name', name);
categoryData.append('image',image, name);
this.http.post<{message : string, category: Category}>(
'http://localhost:3000/api/v1.0/categories',categoryData
).subscribe(responseData=>{
const category : Category = {
id: responseData.category.id,
name : name,
image : responseData.category.image
}
this.categories.push(category);
this.categoryUpdated.next([...this.categories]);
})
}
/* getting categories, data must be as backend i.e message and object */
getCategories(){
this.http.get<{message: string; categories: any}>(
"http://localhost:3000/api/v1.0/categories"
)
.pipe(map((cateData)=>{
return cateData.categories.map(category=>{
return {
id: category._id,
name : category.name,
image: category.image
}
})
}))
.subscribe(transformedCate =>{
this.categories = transformedCate;
this.categoryUpdated.next([...this.categories])
})
}
}
And my main component.ts file:
export class CategoriesComponent implements OnInit,OnDestroy{
togglePanel: any = {};
categoryPanel: any = {};
categories : Category[] = [];
private categorySub : Subscription;
constructor(private _categoriesService : CategoriesService, private dialog : MatDialog){}
ngOnInit(){
this._categoriesService.getCategories();
this.categorySub = this._categoriesService.getUpdateListener().subscribe((cate: Category[])=>{
this.categories = cate;
})
}
OnFormOpen(){
this.dialog.open(CategoryFormComponent)
}
ngOnDestroy(){
this.categorySub.unsubscribe();
}
}
And my form component:
export class CategoryFormComponent implements OnInit {
form : FormGroup;
imagePreview : string;
constructor(private dialogRef : MatDialogRef<CategoryFormComponent>,
#Inject (MAT_DIALOG_DATA) private data : any,
private _categoriesService : CategoriesService) {}
onCancel(){
this.dialogRef.close();
}
ngOnInit(): void {
this.form = new FormGroup({
name : new FormControl(null,{validators:[Validators.required, Validators.minLength(3)]}),
image : new FormControl(null,{validators: [Validators.required], asyncValidators : [mimeType]})
})
}
/*event for checking the image after load */
onImgPicked(event : Event){
const file = (event.target as HTMLInputElement).files[0];
this.form.patchValue({image: file});
this.form.get('image').updateValueAndValidity();
// console.log(file);
// console.log(this.form)
const reader = new FileReader();
reader.onload = () =>{
this.imagePreview = reader.result as string;
};
reader.readAsDataURL(file);
}
/*On category added */
OnCategoryAdded(){
//is loading
this._categoriesService.addCategory(this.form.value.name, this.form.value.image);
this.form.reset();
this.dialogRef.close();
}
}
Setting a timeout on ngOnInit works but I want to make it without settiimeout
setTimeout(() => {
this.OnInit();
},10000)
}
It looks you are misunderstanding the properties of the http response in addCategory. Try modifying as below.
addCategory(name: string, image: File){
const categoryData = new FormData();
categoryData.append('name', name);
categoryData.append('image',image, name);
this.http.post<{message : string, category: Category}>(
'http://localhost:3000/api/v1.0/categories',categoryData
).subscribe(responseData=>{
const category : Category = {
id: responseData._doc.id, // here
name : name,
image : responseData._doc.image // here
}
this.categories.push(category);
this.categoryUpdated.next([...this.categories]);
})
}
https://learn.microsoft.com/en-us/azure/azure-signalr/signalr-tutorial-build-blazor-server-chat-app
How do I get this to work with Azure AD activated? It works perfect when I run in locally in visual studio, but when deployed it will not work with Azure AD, only if I remove Azure AD it works.
This is the error message when deployed and after clicking the button "Chat!" next to username textbox:
"ERROR: Failed to start chat client: Response status code does not indicate success: 403 (Forbidden)."
(I have found other threads like this Blazor Server SignalR Chat works on Local, not on Azure but no solution)
//Program.cs
using BlazorApp6ADChat;
using BlazorApp6ADChat.Data;
using BlazorChat;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.Identity.Web;
using Microsoft.Identity.Web.UI;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd"));
builder.Services.AddControllersWithViews()
.AddMicrosoftIdentityUI();
builder.Services.AddAuthorization(options =>
{
// By default, all incoming requests will be authorized according to the default policy
options.FallbackPolicy = options.DefaultPolicy;
});
builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor()
.AddMicrosoftIdentityConsentHandler();
builder.Services.AddSingleton<WeatherForecastService>();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.MapControllers();
app.MapBlazorHub();
app.MapFallbackToPage("/_Host");
app.MapHub<BlazorChatSampleHub>(BlazorChatSampleHub.HubUrl);
app.UseAuthentication();
app.UseAuthorization();
app.Run();
//appsettings.json
{
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"Domain": "xxx.onmicrosoft.com",
"TenantId": "xxx",
"ClientId": "xxx",
"CallbackPath": "/signin-oidc"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}
Not sure if this will help. This is how I wire up a WebAssembly Host (server) with a SignalR Hub.
services.TryAddEnumerable(
ServiceDescriptor.Singleton<IPostConfigureOptions<JwtBearerOptions>,
ConfigureJwtBearerOptions>());
public class ConfigureJwtBearerOptions : IPostConfigureOptions<JwtBearerOptions>
{
public void PostConfigure(string name, JwtBearerOptions options)
{
var originalOnMessageReceived = options.Events.OnMessageReceived;
options.Events.OnMessageReceived = async context =>
{
await originalOnMessageReceived(context);
if (string.IsNullOrEmpty(context.Token))
{
var accessToken = context.Request.Query["access_token"];
var requestPath = context.HttpContext.Request.Path;
var endPoint = $"/chathub";
if (!string.IsNullOrEmpty(accessToken) &&
requestPath.StartsWithSegments(endPoint))
{
context.Token = accessToken;
}
}
};
}
}
I posted a solution here: Microsoft Learn
Basically:
Manually getting the cookies in the host.cshtml page
Passing the Cookie collection to the app.razor so I can create a cascading parameter
Retrieving the parameter and manually populating the cookie container when instantiating the SignalR client (you may have seen this code on the web, but without steps 1 and 2 it will not work on Azure only in IIS Express)
Program.cs
public class Program
{
public static void Main(string[] args)
{
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddAutoMapper(typeof(Program).Assembly);
builder.Services.AddHttpClient();
builder.Services.AddHttpContextAccessor();
builder.Services.AddScoped<HttpContextAccessor>();
builder.Services.AddResponseCompression(opts =>
{
opts.MimeTypes = ResponseCompressionDefaults.MimeTypes.Concat(
new[] { "application/octet-stream" });
});
var app = builder.Build();
app.UseResponseCompression();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.MapBlazorHub();
app.MapHub<ChatHub>("/chathub");
app.MapFallbackToPage("/_Host");
app.Run();
}
}
_Host.cshtml:
<body>
#{
var CookieCollection = HttpContext.Request.Cookies;
Dictionary<string, string> Cookies = new Dictionary<string, string>();
foreach (var cookie in CookieCollection)
{
Cookies.Add(cookie.Key, cookie.Value);
}
}
<component type="typeof(App)" render-mode="ServerPrerendered" param-Cookies="Cookies" />
app.razor:
<CascadingValue Name="Cookies" Value="Cookies">
<CascadingAuthenticationState>
<Router AppAssembly="#typeof(App).Assembly">
<Found Context="routeData">
<AuthorizeRouteView RouteData="#routeData" DefaultLayout="#typeof(MainLayout)" />
<FocusOnNavigate RouteData="#routeData" Selector="h1" />
</Found>
<NotFound>
<PageTitle>Not found</PageTitle>
<LayoutView Layout="#typeof(MainLayout)">
<p role="alert">Sorry, there's nothing at this address.</p>
</LayoutView>
</NotFound>
</Router>
</CascadingAuthenticationState>
</CascadingValue>
#code {
[Parameter] public Dictionary<string, string> Cookies { get; set; }
}
Index.razor:
#code {
#nullable disable
[CascadingParameter(Name = "Cookies")] public Dictionary<string, string> Cookies { get; set; }
System.Security.Claims.ClaimsPrincipal CurrentUser;
private HubConnection hubConnection;
private List<string> messages = new List<string>();
private string userInput;
private string messageInput;
private string strError = "";
protected override async Task OnInitializedAsync()
{
try
{
var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync();
CurrentUser = authState.User;
// ** SignalR Chat
try
{
hubConnection = new HubConnectionBuilder()
.WithUrl(Navigation.ToAbsoluteUri("/chathub"), options =>
{
options.UseDefaultCredentials = true;
var cookieCount = Cookies.Count();
var cookieContainer = new CookieContainer(cookieCount);
foreach (var cookie in Cookies)
cookieContainer.Add(new Cookie(
cookie.Key,
WebUtility.UrlEncode(cookie.Value),
path: "/",
domain: Navigation.ToAbsoluteUri("/").Host));
options.Cookies = cookieContainer;
foreach (var header in Cookies)
options.Headers.Add(header.Key, header.Value);
options.HttpMessageHandlerFactory = (input) =>
{
var clientHandler = new HttpClientHandler
{
PreAuthenticate = true,
CookieContainer = cookieContainer,
UseCookies = true,
UseDefaultCredentials = true,
};
return clientHandler;
};
})
.WithAutomaticReconnect()
.Build();
hubConnection.On<string, string>("ReceiveMessage", (user, message) =>
{
var encodedMsg = $"{user}: {message}";
messages.Add(encodedMsg);
InvokeAsync(StateHasChanged);
});
await hubConnection.StartAsync();
}
catch(Exception ex)
{
strError = ex.Message;
}
}
catch
{
// do nothing if this fails
}
}
// ** SignalR Chat
private async Task Send()
{
if (hubConnection is not null)
{
await hubConnection.SendAsync("SendMessage", messageInput);
}
}
public bool IsConnected =>
hubConnection?.State == HubConnectionState.Connected;
public async ValueTask DisposeAsync()
{
if (hubConnection is not null)
{
await hubConnection.DisposeAsync();
}
}
}
ChatHub.cs:
[Authorize]
public class ChatHub : Hub
{
public async override Task OnConnectedAsync()
{
if (this.Context.User?.Identity?.Name != null)
{
await Clients.All.SendAsync(
"broadcastMessage",
"_SYSTEM_",
$"{Context.User.Identity.Name} JOINED");
}
}
public async Task SendMessage(string message)
{
if (this.Context.User?.Identity?.Name != null)
{
await Clients.All.SendAsync(
"ReceiveMessage",
this.Context.User.Identity.Name,
message);
}
}
}
after login in IdentityServer4 i can't see permisions checkbox and redirection after clicking allow button doesn't work.
So i can see Personal Information title and Application Access title and no checkbox to them.
I'm using asp.net core 2.0 and IdentityServer4
My configuration looks like that:
public class InMemoryConfiguration
{
public static IEnumerable<ApiResource> ApiResources()
{
return new[]
{
new ApiResource("checklist")
};
}
public static IEnumerable<IdentityResource> IdentityResources()
{
return new IdentityResource[]
{
new IdentityResources.OpenId(),
new IdentityResources.Profile(),
new IdentityResources.Email()
};
}
public static IEnumerable<Client> Clients()
{
return new[]
{
new Client
{
ClientId = "checklist",
ClientSecrets = new [] {new Secret("secret".Sha256())},
AllowedGrantTypes = GrantTypes.ResourceOwnerPasswordAndClientCredentials,
AllowedScopes = new [] {
"checklist"
}
},
new Client
{
ClientId = "checklist_implicit",
ClientSecrets = new [] {new Secret("secret".Sha256())},
AllowedGrantTypes = GrantTypes.Implicit,
AllowAccessTokensViaBrowser = true,
AllowedScopes = new [] {
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
IdentityServerConstants.StandardScopes.Email,
"checklist"
},
RedirectUris = new [] {
"http://localhost:4200/callback"
},
PostLogoutRedirectUris = {"http://localhost:4200/home"},
//RequireConsent=false
}
};
}
public static IEnumerable<TestUser> Users()
{
return new[]
{
new TestUser
{
SubjectId = "1",
Username = "kuba#wp.pl",
Password = "password",
Claims = new [] {new Claim("email", "kuba#wp.pl")}
}
};
}
}
And startup.cs:
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(options => {
// this defines a CORS policy called "default"
options.AddPolicy("default", policy => {
policy
.SetIsOriginAllowedToAllowWildcardSubdomains()
.WithOrigins("http://localhost:4200")
.AllowAnyHeader()
.AllowAnyMethod()
.AllowCredentials();
});
});
services.AddIdentityServer()
//.AddSigningCredential(new X509Certificate2(#"D:\kuba\git\checklist\certs\IdentityServer4Auth.pfx", "changeit"))
.AddDeveloperSigningCredential()
.AddTestUsers(InMemoryConfiguration.Users().ToList())
.AddInMemoryClients(InMemoryConfiguration.Clients())
.AddInMemoryIdentityResources(InMemoryConfiguration.IdentityResources())
.AddInMemoryApiResources(InMemoryConfiguration.ApiResources());
services.AddMvc();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole();
app.UseDeveloperExceptionPage();
app.UseCors("default");
app.UseIdentityServer();
app.UseStaticFiles();
app.UseMvcWithDefaultRoute();
}
On client side i'm using angular
No clue why, but in the Consent view - index.html,
Change this:
<partial name="_ScopeListItem" model="#scope" />
To:
#Html.Partial("_ScopeListItem", scope)
And the consent checkboxes show up as expected.
I have been fiddling around with IDS 4 and I have a small problem. I set my token life times to about 15 seconds and even though they are expired I can still retrieve date from my resource server. If I remove the token from my header on the client call I get a 401 error.
Client
[Authorize]
public async Task<ActionResult> Shouts()
{
var accessToken = await HttpContext.GetTokenAsync("access_token");
var tokenh = new JwtSecurityTokenHandler();
var jwtSecurityToken= tokenh.ReadJwtToken(accessToken);
var val = jwtSecurityToken.ValidTo;
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", accessToken);
var rawResponse = await client.GetAsync("http://localhost:5002/api/Values/Get");
if (rawResponse.StatusCode == System.Net.HttpStatusCode.Unauthorized)
{
var refreshSucc = await this.RefreshTokensAsync(this.HttpContext);
if (!refreshSucc)
{
var authServerInfo = await this.GetAuthenticationServerInfo();
return Redirect(authServerInfo.AuthorizeEndpoint);
}
}
var response = await (rawResponse).Content.ReadAsStringAsync();
var data = JsonConvert.DeserializeObject<List<String>>(response);
return View("Shouts", data);
}
}
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
services.AddAuthentication(options =>
{
options.DefaultScheme = "Cookies";
options.DefaultChallengeScheme = "oidc";
})
.AddCookie("Cookies",o=>o.LogoutPath="/home/logout")
.AddOpenIdConnect("oidc", opt =>
{
opt.SignInScheme = "Cookies";
opt.Authority = "http://localhost:5000";
opt.RequireHttpsMetadata = false;
opt.ClientId = "AuthTest_Code";
opt.ClientSecret = "secret";
opt.ResponseType = "id_token code";
opt.Scope.Add("TestAPI");
opt.Scope.Add("offline_access");
opt.Scope.Add("email");
opt.GetClaimsFromUserInfoEndpoint = true;
opt.SaveTokens = true;
});
services.AddMvc();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseBrowserLink();
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles();
app.UseAuthentication();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
}
Auth Server
public static IEnumerable<Client> GetClients()
{
return new List<Client>
{
new Client
{
ClientId = "AuthTest_Code",
ClientSecrets=new []{new Secret("secret".Sha256()) },
AllowedGrantTypes = GrantTypes.Hybrid,
AllowedScopes = new List<string>
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
IdentityServerConstants.StandardScopes.Email,
"TestAPI"
},
AllowAccessTokensViaBrowser=true,
AllowOfflineAccess=true,
RedirectUris = new [] { "http://localhost:5001/signin-oidc" },
PostLogoutRedirectUris={ "http://localhost:5001/signout-callback-odic"},
RequireConsent=false,
AccessTokenLifetime=15,
AbsoluteRefreshTokenLifetime=15
}
};
}
public class Startup
{
public IHostingEnvironment Environment { get; }
public Startup(IHostingEnvironment environment)
{
Environment = environment;
}
// This method gets called by the runtime. Use this method to add services to the container.
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
var isServer = services.AddIdentityServer()
.AddSigningCredential(new X509Certificate2(#"C:\OpenSSLCerts\Authenticate.pfx", "Password1"))
.AddInMemoryApiResources(TestConfig.GetApis())
.AddInMemoryClients(TestConfig.GetClients())
.AddInMemoryIdentityResources(TestConfig.GetIdentityResources())
.AddTestUsers(TestConfig.GetUsers());
services.AddMvc();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseIdentityServer();
app.UseStaticFiles();
app.UseMvcWithDefaultRoute();
}
}
Resource Server
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
namespace ResourceAPI
{
public class Startup
{
// This method gets called by the runtime. Use this method to add services to the container.
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
services.AddMvcCore()
.AddAuthorization()
.AddJsonFormatters();
services.AddAuthentication("Bearer")
.AddIdentityServerAuthentication(options =>
{
options.Authority = "http://localhost:5000";
options.RequireHttpsMetadata = false;
options.ApiName = "TestAPI";
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseAuthentication();
app.UseMvc();
}
}
}
[Route("api/[controller]")]
public class ValuesController:ControllerBase
{
[HttpGet]
[Route("Get")]
[Authorize]
public async Task<IActionResult> Get()
{
var username = User.Claims.FirstOrDefault(t => t.Type == "email")?.Value;
return Ok(new string[] { "value1", "value2" });
}
}
Access Token:
eyJhbGciOiJSUzI1NiIsImtpZCI6IjQ0Q0Q4NjFEQjA0MzdEMUM4NUY2REU0MTIyMkFDOEIwMTE3M0Q2MTAiLCJ0eXAiOiJKV1QiLCJ4NXQiOiJSTTJHSGJCRGZSeUY5dDVCSWlySXNCRnoxaEEifQ.eyJuYmYiOjE1NDIxMjc4OTQsImV4cCI6MTU0MjEyNzkwOSwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo1MDAwIiwiYXVkIjpbImh0dHA6Ly9sb2NhbGhvc3Q6NTAwMC9yZXNvdXJjZXMiLCJUZXN0QVBJIl0sImNsaWVudF9pZCI6IkF1dGhUZXN0X0NvZGUiLCJzdWIiOiIxIiwiYXV0aF90aW1lIjoxNTQyMTI3ODkzLCJpZHAiOiJsb2NhbCIsImVtYWlsIjoiRGF2aWRAQUJldHRlckxpZmUuY28uemEiLCJzY29wZSI6WyJvcGVuaWQiLCJwcm9maWxlIiwiZW1haWwiLCJUZXN0QVBJIiwib2ZmbGluZV9hY2Nlc3MiXSwiYW1yIjpbInB3ZCJdfQ.hNskjZz3IBqPg_5T0xVwYEP5RukcMt3l019PRp74vNBkiEr6FLvBADa_eylhNGA8qDd7SwyDkq6APaKxaNt0YybAChZvFW6pzLlfknVVHM1vuN7PDOX9PNGhFK9kSONBypXo6JqV3epactsmJvhr3FZxBSufUDRyV4j_YY3O8TCjJf_5UORc_3ox9clNdjt-Vx-BlcDjVbpBw2P76F3pq2IPPDM139H4qYcyaTzSlEbFd3EdVwO6O85bWM1G8yQ9zQAUk23It29oHxTtwsi5Wg4Zpmt7K7AlvKjKxKoxw_Y32QdBkGY9xU_KXOn4h0WJV3LG-InZc7BveLGHq6ncaQ
Try setting options.JwtValidationClockSkew property to zero in your web api(resource server)
services.AddAuthentication("Bearer")
.AddIdentityServerAuthentication(options =>
{
options.JwtValidationClockSkew = TimeSpan.Zero;
options.Authority = "https://localhost:44323";
options.RequireHttpsMetadata = false;
options.ApiName = "api1";
});
There is a clock skew in the Microsoft JWT validation middleware. It is set by default to 5 mins. And it's suggest to leave it as default (300 seconds/5 minutes).
This is most likely due to the default 5 minute (if memory serves) clock skew allowance. It is however possible to override this setting in IDS4 and the bearer token middleware.
I am making an angular application which uses a MongoDB database and NodeJS server.
The idea is that I make an application which for now only has a list of posts and beside that the detailed-post. The components are nicely standing next to eachother and working but I have one problem. When I try to retrieve a single post I can see via console.dir(post) that all is good and the object has been transmitted to the angular app. The problem is that when I try to use post.content I get an undefined message.
I have searched for hours but can not seem to find the cause of this. I would greatly appreciate any help you can give me. Beneath here is all the information, if you need to see something else, please tell me.
Thanks in advance.
This is the post-detail.component.html where I want to display the data.
<div class="row">
<div class="col-xs-12">
<p>Content:</p>
<h1>{{ post.content }}</h1>
</div>
</div>
The detail.ts file (I left out the imports)
#Component({
selector: 'app-post-detail',
templateUrl: './post-detail.component.html',
styleUrls: ['./post-detail.component.css']
})
export class PostDetailComponent implements OnInit {
post: Post = new Post();
id: string;
constructor(private postService: PostService,
private route: ActivatedRoute,
private router: Router) {
}
ngOnInit() {
this.route.params
.subscribe(
(params: Params) => {
this.id = params['id'];
this.postService.getPost(this.id).then(res => {
console.dir(res);
console.dir(res.content);
this.post = res;
});
}
);
}
}
The post.service.ts which I am using to retrieve the actual data:
#Injectable()
export class PostService {
postChanged = new Subject<Post[]>();
private headers = new Headers({'Content-Type': 'application/json'});
private serverUrl = environment.serverUrl + '/blogPosts/'; // URL to web api
private posts: Post[];
constructor(private http: Http) {
}
//this one DOES work
getPosts() {
console.log('Fetching BlogPosts from database.')
return this.http.get(this.serverUrl, {headers: this.headers})
.toPromise()
.then(response => {
this.posts = response.json() as Post[];
return response.json() as Post[];
})
.catch(error => {
return error;
});
}
getPost(index: string) {
console.log('Fetching individual BlogPost from database.');
console.log('index' + index);
if (index == null) {
console.log('null');
return null;
}
return this.http.get(this.serverUrl + index, {headers: this.headers})
.toPromise()
.then(response => {
console.dir(response.json().content);
console.dir(response.json());
return response.json() as Post;
})
.catch(error => {
return this.handleError(error);
});
}
}
The Post model:
export class Post {
private id: string;
private _content: string;
constructor(values: Object = {}) {
Object.assign(this, values);
}
public get _id(): string {
return this.id;
}
public set _id(n: string) {
this.id = n;
}
public get content(): string {
return this._content;
}
public set content(n: string) {
this._content = n;
}
}
And I added in the Postman GET /blogPost/id and the console log as images.
Thanks!
Console log
Postman GET route
I might be wrong but can you please change the _content to content everywhere in the service ?
Edit: are you sure the this.id is correct when you call the service method ? cause if it is null or undefined then return null will be executed.
Another note is that in Postman i see the response is an array of objects (one object) at this example. Can you try this.post = res[0]; in the component ?
return response.json() as Post;
In post.service.ts should be:
return response.json()[0] as Post;
I did not see that the object was wrapped in an array, by accessing it I was able to get it out and use it.