How to implement AntiForgery Token in Angular with NET 6 - c#

I'm working with several but obsolete documentation about implementing antiforgery token with angular. In my case I'm working without MVC Razor, only angular 13.3.4 and NET 6.0
I just make the configuration:
builder.Services.AddAntiforgery(options =>
{
options.HeaderName = "X-XSRF-TOKEN";
});
builder.Services.AddScoped<AntiforgeryMiddleware>();
and then the controller:
public class AntiforgeryMiddleware : IMiddleware
{
private readonly IAntiforgery _antiforgery;
public AntiforgeryMiddleware(IAntiforgery antiforgery)
{
_antiforgery = antiforgery;
}
public async Task InvokeAsync(HttpContext context, RequestDelegate next)
{
var isGetRequest = string.Equals("GET", context.Request.Method, StringComparison.OrdinalIgnoreCase);
if (!isGetRequest)
{
_antiforgery.ValidateRequestAsync(context).GetAwaiter().GetResult();
}
await next(context);
}
}
but still can't get the thing with angular. My post is this one (just dummy to test it):
import { Component, Inject } from '#angular/core';
import { HttpClient, HTTP_INTERCEPTORS } from '#angular/common/http';
import { HttpXSRFInterceptor } from 'src/app/interceptors/tok.interceptor';
providers: [
{ provide: HTTP_INTERCEPTORS, useClass: HttpXSRFInterceptor, multi: true }
]
#Component({
selector: 'app-fetch-data',
templateUrl: './fetch-data.component.html'
})
export class FetchDataComponent {
public lenormandjack: LenormandHand = {} as LenormandHand;
constructor(http: HttpClient, #Inject('BASE_URL') baseUrl: string, ) {
http.post<LenormandHand>(baseUrl + 'api/lenormand', null, { withCredentials: true }).subscribe(result => {
this.lenormandjack = result;
console.dir(result);
console.log("OK");
console.dir(this.lenormandjack);
}, error => console.error(error));
}
}
I'm learning angular and I can't get a code that even compiles as typescript. Totally blocked and the documentation in several (dozens) of searchs returns the same. Or just for WebApi.
I'm trying to get working the antiforgery with this controller:
[HttpPost]
[ValidateAntiForgeryToken]
public LenormandHand Post()
{
return foobar;
}
My interceptor:
import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest, HttpXsrfTokenExtractor } from "#angular/common/http";
import { Injectable } from "#angular/core";
import { Observable } from "rxjs";
#Injectable()
export class XsrfInterceptor implements HttpInterceptor {
constructor(private tokenExtractor: HttpXsrfTokenExtractor) { }
private actions: string[] = ["POST", "PUT", "DELETE"];
private forbiddenActions: string[] = ["HEAD", "OPTIONS"];
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
let token = this.tokenExtractor.getToken();
console.log ("TOKEN: "+token);
let permitted = this.findByActionName(request.method, this.actions);
let forbidden = this.findByActionName(request.method, this.forbiddenActions);;
if (permitted !== undefined && forbidden === undefined && token !== null) {
request = request.clone({ setHeaders: { "X-XSRF-TOKEN": token } });
}
return next.handle(request);
}
private findByActionName(name: string, actions: string[]): string | undefined {
return actions.find(action => action.toLocaleLowerCase() === name.toLocaleLowerCase());
}
}
UPDATE:
Following this: Anti forgery with token API and angular
I could compile but but answer is 400 bad request. In deep, the header that contains X-XSRF-TOKEN is always false

I finally did it. There are a lot of changes (and not) and there are a lot of considerations you have to take into account in order to implement the AntiForgery with security.
I will make a tutorial because I lost a lot of time to resolve this request/issue.
The code from question itself I made is just fine, so you can use it as a base.
DO NOT EVEN try the "controller way" to generate a token. It is a non-sense because everyone can call it.
The invoke should have the following cookie options:
Path = "/",
HttpOnly = false,
Secure = true,
SameSite = SameSiteMode.Strict
The setup only needs this configuration in the Startup.cs:
builder.Services.AddAntiforgery(options => options.HeaderName = "X-XSRF-TOKEN");
And this configuration:
app.UseAntiforgeryToken();
The heavy metal part: the call should be not absolute:
I use "api" here because I only want to secure the controllers that includes "api" in the endpoint to have more flexibility.
constructor(http: HttpClient, #Inject('BASE_URL') baseUrl: string, ) {
http.post<yourclass>(baseUrl + 'api/controller', null, { withCredentials: true }).subscribe(result => {
this.yourinstance = result;
}, error => console.error(error));
}
}
In app.module.ts file add this:
import { XsrfInterceptor } from 'src/app/interceptors/tok.interceptor';
and then INSIDE the #NgModule({ section:
providers: [
{ provide: HTTP_INTERCEPTORS, useClass: XsrfInterceptor, multi: true }
],
See my complete module file:
import { BrowserModule } from '#angular/platform-browser';
import { NgModule } from '#angular/core';
import { FormsModule } from '#angular/forms';
import { HttpClientModule, HTTP_INTERCEPTORS } from '#angular/common/http';
import { RouterModule } from '#angular/router';
import { AppComponent } from './app.component';
import { NavMenuComponent } from './nav-menu/nav-menu.component';
import { HomeComponent } from './home/home.component';
import { CounterComponent } from './counter/counter.component';
import { FetchDataComponent } from './fetch-data/fetch-data.component';
import { XsrfInterceptor } from 'src/app/interceptors/tok.interceptor';
#NgModule({
declarations: [
AppComponent,
NavMenuComponent,
HomeComponent,
CounterComponent,
FetchDataComponent
],
imports: [
BrowserModule.withServerTransition({ appId: 'ng-cli-universal' }),
HttpClientModule,
FormsModule,
RouterModule.forRoot([
{ path: '', component: HomeComponent, pathMatch: 'full' },
{ path: 'counter', component: CounterComponent },
{ path: 'fetch-data', component: FetchDataComponent },
])
],
providers: [
{ provide: HTTP_INTERCEPTORS, useClass: XsrfInterceptor, multi: true }
],
bootstrap: [AppComponent]
})
export class AppModule { }
If you want to use this only for some kind of controllers, I also separate them with "api" prefix:
So my controller has this route:
[ApiController]
[Route("api/[controller]")]
and my method looks like this:
[HttpPost]
[ValidateAntiForgeryToken]
public Method Post()
{
You can make an Invoke and check if the path contains 'api' or not. Several examples of this implementation have it because the example is a web api (totally optional, per gusto e piacere).
If you are in development mode with a reverse proxy, don't forget to add the "api" to the proxy settings if you are using the same method like me.
You add this to proxy.config.js
const PROXY_CONFIG = [
{
context: [
"/imgs",
"/api"
],

Related

Initiate Header Once and Send in Every HTTP Request

I am using a service to get user ids in HTML DropDownList and it works fine. Here am using an Angular service to get the user details and for back-end, ASP.NET Web Api. So far this is what I've done so far:
Web Api - C#:
[Route("api/values/GetUserInfo")]
[Authorize]
[HttpGet]
public List<User> GetUserInfo(string type)
{
List<User> lst = null;
if (type!= null)
{
lst = GetUserInfo().Where(c => c.userType== type).ToList();
}
else
{
lst = GetUserInfo().ToList();
}
return lst;
}
Angular: Service - UserService
GetUserInfo(dept: string) {
debugger;
this.Url = 'http://localhost:53743/api/values/';
var a = this.Url + 'GetUserInfo';
var headers_object = new HttpHeaders().set("Authorization", "Bearer " + localStorage.getItem('Token')); //Set JWT Token
let params = new HttpParams().set("type", type);
return this.http.get<any>(a, { headers: headers_object, params: params }); //Get request to retrieve the user details from database server
}
Finally in the Angular Component:
public empIds: any[];
constructor(private dataservice: UserService, private appComponent: AppComponent, private sanitizer: DomSanitizer, private route: Router, private http: HttpClient) { //UserService injected in the constructor
}
ngOnInit() {
this.LoadUserData('');
}
LoadUserData(dept: string) {
debugger;
this.dataservice.GetUserInfo(dept).subscribe(result => { //Calling the `Angular` service here
this.empIds = result; //Keeping the result set here
}, error => console.error(error));
}
So far, these are my done work and was wondering if I can manage the header initiated once in every HTTP request. Like a class that handles all the post and get request and I did some research on it searching goggle but bit confused how could I make it work accordingly? Below is what I've been studying so far and trying to implement in my code sample. But the issue is when there are parameters how I could handle them with the following code sample.
Angular - httpService:
import { Injectable } from '#angular/core';
import { Http, Headers } from '#angular/http';
import { HttpClient, HttpHeaders, HttpParams } from '#angular/common/http';
#Injectable({
providedIn: 'root'
})
export class HttpClientService {
value: any;
constructor(private http: HttpClient) { }
createAuthorizationHeader(headers: HttpHeaders) {
//headers.append('content-type', 'application/json');
//headers.append("Authorization", "Bearer " + localStorage.getItem('Token'));
}
get(url, value, type) {
debugger;
let params = new HttpParams().set("dept", dept);
var headers_object = new HttpHeaders().set("Authorization", "Bearer " + value);
const httpOptions = {
headers: headers_object
};
return this.http.get<any>(url, {
headers: headers_object, params: params
});
}
post(url, data, value) {
let params = new HttpParams();
let headers_object = new HttpHeaders();
headers_object.append('content-type', 'application/json');
headers_object.append("Authorization", "Bearer " + value);
//this.createAuthorizationHeader(headers);
return this.http.post<any>(url, data, {
headers: headers_object, params: params
});
}
}
As an example below in the component:
this.httpService.get(a, localStorage.getItem('Token'), dept).subscribe(result => {
this.empIds = result;
}, error => console.error(error));
N.B: I am almost novice to Angular and it's working flow, trying to figure out the things in a better way - Thanks.
You should use interceptor.
If you want to add the specific headers in every request you have to do like the following : (Here I add Accept Header in every http request)
#Injectable()
export class ExampleAuth implements HttpInterceptor {
intercept(request: HttpRequest<any>, next: HttpHandler):
Observable<HttpEvent<any>> {
request = request.clone({ headers: request.headers.set('Accept', 'application/json') }); // Here you can add your special headers
return next.handle(request);
}
}
then add the interceptor in the provider section of your app module as below :
#NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
HttpClientModule
],
providers: [
{ provide: HTTP_INTERCEPTORS, useClass: ExampleAuth, multi: true }
],
bootstrap: [AppComponent]
})
export class AppModule { }
Now whenever a new HTTP request will be made, this Interceptor will modify the Headers of request.
More Explanations:
Angular Interceptor is a powerful feature which can be used in many ways for securing and handling many HTTP related phenomena.
What we can do using Interceptors?
Interceptors can be used in a number of ways in an application.
– Set request headers in HTTP calls like Content-Type or send any custom headers.
– Authenticate HTTP calls by setting Security tokens
– Show Spin Loaders/ Progress Bars as HTTPS calls are in progress.
– Handle Errors in HTTP calls at one place
– Show Response messages using Notifications/ Toasts
EDIT:
According to your example,In your HttpClientService , you have to remove the following lines because they will add automatically to the http request headers in the interceptor:
var headers_object = new HttpHeaders().set("Authorization", "Bearer " + value);
headers_object.append('content-type', 'application/json');
Modify your HttpClientService as below:
import { Injectable } from '#angular/core';
import { Http, Headers } from '#angular/http';
import { HttpClient, HttpHeaders, HttpParams } from '#angular/common/http';
#Injectable({
providedIn: 'root'
})
export class HttpClientService {
value: any;
constructor(private http: HttpClient) { }
get(url, value, type) {
debugger;
let params = new HttpParams().set("dept", dept);
const options = {
params: params
};
return this.http.get<any>(url, options);
}
post(url, data, value) {
let params = new HttpParams();
const options = {
params: params
};
return this.http.post<any>(url, data, options);
}
}
Then in the Interceptor we have to add the headers that we have just removed from HttpClientService
#Injectable()
export class ExampleAuth implements HttpInterceptor {
intercept(request: HttpRequest<any>, next: HttpHandler):
Observable<HttpEvent<any>> {
request = request.clone({ headers: request.headers.set('content-type', 'application/json') });
request = request.clone({ headers: request.headers.set("Authorization", "Bearer " + localStorage.getItem('Token') ) });
return next.handle(request);
}
}
Then register the interceptor in app module.
You can do it with HttpInterceptor which can intercept all your requests you make and all their responses.
You can thus also use it to display consistent error messages on each failed request.
You can have a look at Angular's documentation to see how to implement it.
The example the give is very similar to what you need:
import { AuthService } from '../auth.service';
#Injectable()
export class AuthInterceptor implements HttpInterceptor {
constructor(private auth: AuthService) {}
intercept(req: HttpRequest<any>, next: HttpHandler) {
// Get the auth token from the service.
const authToken = this.auth.getAuthorizationToken();
// Clone the request and replace the original headers with
// cloned headers, updated with the authorization.
const authReq = req.clone({
headers: req.headers.set('Authorization', authToken)
});
// send cloned request with header to the next handler.
return next.handle(authReq);
}
}

Not able to set the options of the dropdown to the fields returned by the api in json format

I am calling a WCF service from my angular 6 code and the data that is returned is in JSON. But when I am accepting the returned data it is throwing some error like :
Cannot find a differ supporting object '[{"Company_Prefix":"SCL","Company_Name":"Smart Chip Private Limited","Company_Code":"SCL"},{"Company_Prefix":"SYS","Company_Name":"Syscom Corporation Private Limited","Company_Code":"SYS"},{"Company_Prefix":"V-SCL","Company_Name":"Vihaan Infrasystems India Limited","Company_Code":"V-SCL"},{"Company_Prefix":"OT","Company_Name":"OT Morpho","Company_Code":"OT"}]' of type 'string'. NgFor only supports binding to Iterables such as Arrays.
I have tried MAP and It doesn't help.
TS file:
import { CompanyModel } from './../Models/app.CompanyModel';
import { CommonService } from './../../Shared/Common.service';
import { Component, OnInit } from '#angular/core';
import { CreateEmployeeModel } from '../Models/app.create-EmployeeModel';
#Component({
selector: 'app-create-employee',
templateUrl: './create-employee.component.html',
styleUrls: ['./create-employee.component.css',
'../../Content/vendor/bootstrap/css/bootstrap.min.css']
})
export class CreateEmployeeComponent implements OnInit {
private _employeeModel : CreateEmployeeModel;
UserName:string = sessionStorage.getItem('UserName');
companies: CompanyModel[];
departments: string[];
errorMessage: any;
constructor(private _CommonService:CommonService) { }
ngOnInit(): void {
debugger;
this._employeeModel = new CreateEmployeeModel();
this._employeeModel.UserName=this.UserName;
this._CommonService.BindCompany(this._employeeModel)
.subscribe(data =>
{
debugger;
this.companies=data;
},
error => this.errorMessage = <any>error);
// this._CommonService.BindDepartment(this._employeeModel).subscribe(data=>this.departments=data);
}
**Service:**
import { CompanyModel } from './../HR/Models/app.CompanyModel';
import { CreateEmployeeModel } from './../HR/Models/app.create-EmployeeModel';
import { Injectable } from '#angular/core';
import { Observable, throwError } from 'rxjs';
import { catchError, tap, map } from 'rxjs/operators';
import { HttpClient, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest, HttpErrorResponse, HttpHeaders } from '#angular/common/http';
import { Router } from '#angular/router';
#Injectable({
providedIn: 'root'
})
export class CommonService {
private _parse;
private _response;
private data: any;
constructor(private _http: HttpClient, private _Route: Router) { }
private api='http://localhost:10704/CommonService.svc';
BindCompany(EmployeeModel: CreateEmployeeModel): Observable<CompanyModel[]>
{
let headers = new HttpHeaders({ 'Content-Type': 'application/json' });
debugger;
return this._http.get<CompanyModel[]>( this.api+'/BindCompany/' + EmployeeModel.UserName, { headers: headers } )
.pipe(tap(data=>{
debugger;
// this._parse=JSON.parse(data);
console.log(data);
if(data!=null)
{
return (data);
}
else
{
return null;
}
}),
catchError(this.handleError)
);
}
`````````````````````````````````````````````````````````````````````````
**JSON DATA THAT API RETURNS:**
`````````````````````````````````````````````````````````````````````````
[{"Company_Prefix":"XXX",
"Company_Name":"XXX Private Limited",
"Company_Code":"XXX"},
{"Company_Prefix":"XXX",
"Company_Name":"XXX Corporation Private Limited",
"Company_Code":"XXX"},
{"Company_Prefix":"V-XXX",
"Company_Name":"XXX Infrasystems India Limited",
"Company_Code":"V-XXX"},
{"Company_Prefix":"XXX",
"Company_Name":"OT XXX",
"Company_Code":"XXX"}]
It seems that you're not parsing the response. Make amend to your service while referring to the code below.
return this._http.get<CompanyModel[]>( this.api+'/BindCompany/' + EmployeeModel.UserName, { headers: headers } )
.pipe(tap(data=>{
if(data!=null)
{
return JSON.parse(data);
}
else
{
return null;
}
}),
catchError(this.handleError)
);

Angular 5 Uncaught (in promise): Error: StaticInjectorError(AppModule)

I am doing this development in Angular 5
Visual Studio 2017 version 15.6.7
TargetFramework - netcoreapp2.0
rxjs: 5.5.10
I cant get rid of this error:
ERROR Error: Uncaught (in promise): Error:
StaticInjectorError(AppModule)[VtoReportComponent -> Vtos]:
StaticInjectorError(Platform: core)[VtoReportComponent -> Vtos]:
NullInjectorError: No provider for Vtos!
here is vto.service.ts
import { Component, OnInit } from '#angular/core';
import { Http, HttpModule } from '#angular/http';
import { Observable } from 'rxjs';
import { GroupDescriptor, DataSourceRequestState, DataResult, process, State } from '#progress/kendo-data-query';
import { ExcelExportData } from '#progress/kendo-angular-excel-export';
import { aggregateBy, SortDescriptor, orderBy } from '#progress/kendo-data-query';
import { Vtos } from '../../services/vto.service';
import {
GridComponent,
GridDataResult,
DataStateChangeEvent
} from '#progress/kendo-angular-grid';
#Component({
selector: 'app-vto-report',
templateUrl: './vto-report.component.html',
styleUrls: ['./vto-report.component.css']
})
export class VtoReportComponent {
public multiple = false;
public allowUnsort = true;
public gridData: GridDataResult;
public state: DataSourceRequestState = {
skip: 0,
take: 5,
};
constructor(private vtos: Vtos) {
this.vtos.getVtos(this.state).subscribe(r => this.gridData = r);
}
public dataStateChange(state: DataStateChangeEvent): void {
this.state = state;
this.vtos.getVtos(state)
.subscribe(r => this.gridData = r);
}
vto.service.html
<kendo-grid [data]="gridData"
[pageSize]="state.take"
[skip]="state.skip"
[sort]="state.sort"
[sortable]="true"
[pageable]="true"
[scrollable]="'scrollable'"
[groupable]="true"
[group]="state.group"
[filterable]="true"
[filter]="state.filter"
(dataStateChange)="dataStateChange($event)"
[height]="370">
<kendo-grid-column field="EmployeeID" title="Employee ID" [width]="250"></kendo-grid-column>
<kendo-grid-column field="ReportDate" title="Report Date" width="240" filter="date" format="{0:MM/dd/yyyy}"></kendo-grid-column>
<kendo-grid-column field="BeginTime" title="Begin Time"></kendo-grid-column>
<kendo-grid-column field="EndTime" title="End Time"></kendo-grid-column>
<kendo-grid-column field="TotalHours" title="Total Hours" filter="numeric"></kendo-grid-column>
<kendo-grid-column field="VTOby" title="VTOby">
</kendo-grid-column>
<kendo-grid-column field="Timestamp" title="Time stamp"></kendo-grid-column>
</kendo-grid>
Vto-service.ts
import { Injectable } from '#angular/core';
import { VTO } from './vto';
import { Http, HttpModule } from '#angular/http';
import { Observable } from 'rxjs';
import { HttpClientModule, HttpClient } from '#angular/common/http';
import { Location, LocationStrategy, PathLocationStrategy } from '#angular/common';
import {
toDataSourceRequestString,
translateDataSourceResultGroups,
translateAggregateResults,
DataResult,
DataSourceRequestState
} from '#progress/kendo-data-query';
import 'rxjs/add/operator/map';
import { GridDataResult, DataStateChangeEvent } from '#progress/kendo-angular-grid';
#Injectable()
export class Vtos{
private vtoUrl = location.href.replace(location.hash, '') + '/home/getVtos';
constructor(private http: Http) { }
public getVtos(state: DataSourceRequestState): Observable <DataResult> {
const queryStr = `${toDataSourceRequestString(state)}`; //serialize the state
const hasGroups = state.group && state.group.length;
return this.http
.get(`${this.vtoUrl}?${queryStr}`) //send the state to the server
.map(response => response.json())
.map(({ data, total/*, aggregateResults*/ }) => // process the response
(<GridDataResult>{
//if there are groups convert them to compatible format
data: hasGroups ? translateDataSourceResultGroups(data) : data,
total: total,
// convert the aggregates if such exists
//aggregateResult: translateAggregateResults(aggregateResults)
})
)
}
}
HomeController.cs
public JsonResult GetVtos()
{
var data = Vto.GetVtos();
return Json(data);
}
Vto.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using VTO.DTO;
using System.Data;
using System.Data.SqlClient;
using System.Web;
namespace VTO.DAL
{
public class Vto
{
public static List<DTO.VtoDto> GetVtos()
{
List<DTO.VtoDto> Vtos = new List<DTO.VtoDto>(3);
Vtos.Add(new DTO.VtoDto(1010765345, "2018-03-24", "9:30:00 AM", "2:30:00 PM", 5, "Browning, Mary", "1:39:00 PM"));
Vtos.Add(new DTO.VtoDto(1010756567, "2018-03-22", "9:30:00 AM", "12:30:00 PM", 3, "Cruz, Denise", "1:39:00 PM"));
Vtos.Add(new DTO.VtoDto(1034534568, "2018-03-17", "8:30:00 AM", "2:30:00 PM", 6, "Perez, Breana", "1:39:00 PM"));
return Vtos.ToList();
}
}
}
and lastly AppModule
import { NgModule } from '#angular/core';
import { CommonModule } from '#angular/common';
import { FormsModule } from '#angular/forms';
import { HttpModule } from '#angular/http';
import { RouterModule } from '#angular/router';
import { AppComponent } from './components/app/app.component';
import { NavMenuComponent } from './components/navmenu/navmenu.component';
import { HomeComponent } from './components/home/home.component';
import { FetchDataComponent } from './components/fetchdata/fetchdata.component';
import { CounterComponent } from './components/counter/counter.component';
import { RequestProcessingComponent } from './components/request-processing/request-processing.component';
import { ContingentMaintenanceComponent } from './components/contingent-maintenance/contingent-maintenance.component';
import { TimezoneComponent } from './components/timezone/timezone.component';
import { HierarchyConfigurationComponent } from './components/hierarchy-configuration/hierarchy-configuration.component';
import { VtoReportComponent } from './components/vto-report/vto-report.component';
import { ContingentsReportComponent } from './components/contingents-report/contingents-report.component';
import { DateInputsModule } from '#progress/kendo-angular-dateinputs';
import { BrowserAnimationsModule } from '#angular/platform-browser/animations';
import { DropDownsModule } from '#progress/kendo-angular-dropdowns';
import { JobStatusComponent } from './components/job-status/job-status.component';
import { GridModule, ExcelModule } from '#progress/kendo-angular-grid';
import { HttpClientModule, HttpClient } from '#angular/common/http';
#NgModule({
declarations: [
AppComponent,
NavMenuComponent,
CounterComponent,
FetchDataComponent,
HomeComponent,
RequestProcessingComponent,
ContingentMaintenanceComponent,
TimezoneComponent,
HierarchyConfigurationComponent,
VtoReportComponent,
ContingentsReportComponent,
JobStatusComponent
],
imports: [
CommonModule,
HttpModule,
HttpClientModule,
FormsModule,
DateInputsModule, BrowserAnimationsModule, DropDownsModule, GridModule, ExcelModule,
RouterModule.forRoot([
{ path: '', redirectTo: 'home', pathMatch: 'full' },
{ path: 'home', component: RequestProcessingComponent },
{ path: 'counter', component: CounterComponent },
{ path: 'fetch-data', component: FetchDataComponent },
{ path: 'ContiMain', component: ContingentMaintenanceComponent },
{ path: 'HierConfig', component: HierarchyConfigurationComponent },
{ path: 'vtoreport', component: VtoReportComponent },
{ path: 'ContiReport', component: ContingentsReportComponent },
{ path: '**', redirectTo: 'home' }
])
],
bootstrap: [AppComponent]
})
export class AppModule {
}
In your AppModule you need to import Vto and set it into providers Array if you want that service for the whole app
#NgModule({
declarations: [
AppComponent
],
imports: [],
providers: [ Vtos ],
bootstrap: [AppComponent]
})
or if you want that service only in that component
#Component({
...
providers: [ Vtos ],
})
more info here
The above answer is correct, but in my case, it was the wrong name provided in the import statement of component (a case sensitivity issue):
import { StudentService } from '../../service/Student.service';
The correct one is below:
import { StudentService } from '../../service/student.service';

Passing Data from ASP.NET Core to Typescript in Angular Cli-based App

I am trying to setup a SPA where I can pass data from my appsettings.json to my clientside on server render. I followed the steps to configure SSR using the new SpaServices Templates
https://learn.microsoft.com/en-us/aspnet/core/spa/angular
However I am not understanding how to accomplish the task labeled here
You can pass this to other parts of your app in any way supported by Angular
I see the example that is used is base_url, but it seems that base is injected into the page as a DOM element
<base href='/'>
But it is not clear how to read other items in this manner. I am testing passing whether the app is Https or not, I have this section in my Startup.cs
options.SupplyData = (context, data) =>
{
data["isHttpsRequest"] = context.Request.IsHttps;
};
and in main.server.ts
{ provide: 'isHttps', useValue: params.data.isHttpsRequest }
but it is in main.ts that I get lost, I have something like this
export function getBaseUrl() {
return document.getElementsByTagName('base')[0].href;
}
export function getIsHttps() {
// NO idea what needs to go here
return "";
}
const providers = [
{ provide: 'BASE_URL', useFactory: getBaseUrl, deps: [] },
{ provide: 'isHttps', useFactory: getIsHttps, deps: [] }
];
I am not sure how SpaServices injects the value on Prerender into the app (I looked through the code but it isn't clear to me). Is the value put on window? How do I read the value so I can inject into constructors on components?
Found an answer. You can use Angular's offical TransferState. You'll need to modify the following files
app.server.module.ts (Add ServerTransferStateModule)
import { NgModule } from '#angular/core';
import { ServerModule, ServerTransferStateModule } from '#angular/platform-server';
import { AppModule } from './app.module';
import { AppComponent } from './app.component';
#NgModule({
imports: [
AppModule,
ServerModule,
ServerTransferStateModule
],
bootstrap: [AppComponent],
})
export class AppServerModule { }
app.module.ts (Add BrowserTransferStateModule)
import { BrowserModule, BrowserTransferStateModule } from '#angular/platform-browser';
import { HttpClientModule } from '#angular/common/http';
import { NgModule } from '#angular/core';
import { AppComponent } from './app.component';
#NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule.withServerTransition({ appId: 'universal-demo-v5' }),
HttpClientModule,
BrowserTransferStateModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
This adds the modules needed to use TransferState.
You'll then need to provide the value sent by the dotnet Server on main.server.ts and add a dummy provider on main.ts so as to not raise a conflict.
main.server.ts
import "zone.js/dist/zone-node";
import "reflect-metadata";
import { renderModule, renderModuleFactory } from "#angular/platform-server";
import { APP_BASE_HREF } from "#angular/common";
import { enableProdMode } from "#angular/core";
import { provideModuleMap } from "#nguniversal/module-map-ngfactory-loader";
import { createServerRenderer } from "aspnet-prerendering";
export { AppServerModule } from "./app/app.server.module";
enableProdMode();
export default createServerRenderer(params => {
const { AppServerModule, AppServerModuleNgFactory, LAZY_MODULE_MAP } = (module as any).exports;
const options = {
document: params.data.originalHtml,
url: params.url,
extraProviders: [
provideModuleMap(LAZY_MODULE_MAP),
{ provide: APP_BASE_HREF, useValue: params.baseUrl },
{ provide: "BASE_URL", useValue: params.origin + params.baseUrl},
//Added provider, ServerParams is the data sent on Startup.cs
{ provide: "SERVER_PARAMS", useValue: params.data.ServerParams }
]
};
const renderPromise = AppServerModuleNgFactory
? /* AoT */ renderModuleFactory(AppServerModuleNgFactory, options)
: /* dev */ renderModule(AppServerModule, options);
return renderPromise.then(html => ({ html }));
});
main.ts (Add dummy provider and bootstrap app only when DOM finished loading. This will give us the time to fully save object on TransferState)
import { enableProdMode } from "#angular/core";
import { platformBrowserDynamic } from "#angular/platform-browser-dynamic";
import { AppModule } from "./app/app.module";
import { environment } from "./environments/environment";
export function getBaseUrl() {
return document.getElementsByTagName("base")[0].href;
}
const providers = [
{ provide: "BASE_URL", useFactory: getBaseUrl, deps: [] },
{ provide: "SERVER_PARAMS", useValue: "" }
];
if (environment.production) {
enableProdMode();
}
document.addEventListener("DOMContentLoaded", () => {
platformBrowserDynamic(providers)
.bootstrapModule(AppModule)
.catch(err => console.log(err));
});
Finally, we save the server provided object on app.component.ts on the TransferState
app.component.ts
import { Component, Inject } from "#angular/core";
import { TransferState, makeStateKey } from "#angular/platform-browser";
const SERVER_PARAMS = makeStateKey("serverParams");
#Component({
selector: "app-root",
templateUrl: "./app.component.html",
styleUrls: ["./app.component.css"]
})
export class AppComponent {
serverParams: any;
// Provider set on main.ts and main.server.ts
constructor(#Inject("SERVER_PARAMS") private stateInj: string, private state: TransferState) {
this.serverParams = this.state.get(SERVER_PARAMS, null as any);
if (!this.serverParams) {
this.state.set(SERVER_PARAMS, stateInj as any);
}
}
}
THATS IT
Now you can use Transfer state on any component to get the data as so
home.component.ts
import { Component, Inject } from "#angular/core";
import { TransferState, makeStateKey } from "#angular/platform-browser";
const SERVER_PARAMS = makeStateKey("serverParams");
#Component({
selector: "app-home",
templateUrl: "./home.component.html"
})
export class HomeComponent {
serverParameter = "PLACEHOLDER";
constructor(public state: TransferState) {}
ngOnInit() {
this.serverParameter = this.state.get(SERVER_PARAMS, "");
}
}
So I dove into this and tried to make it work, but unfortunately it's not meant to work like you or I thought it should work. "You can pass this to other parts of your app in any way supported by Angular" means it needs to be accessible to client side code either by using the global namespace or some other means (which I can't think of). When the app is rendered through the main.server.ts, you can embed variables from the server, but that doesn't mean they are available to the client app automatically...kind of a half baked solution in my opinion, and you and I are not the only ones confused by this (see https://github.com/aspnet/JavaScriptServices/issues/1444).
I recommend you look for other methods of including server side data, like a config service on your application bootstrap that gets the config on startup or embedding a JSON object in the Angular hosting page that is written by the server side code.

Angular2 With ASP.NET WebAPI2 Controller sending blank content back

Ok I'm really confused about this one. I have a angular2 app and it successfully does a http GET request to my localhost server. I implement a return type of IHttpActionResult with a return status of Ok and send some content back but when I look at the response on my console the content is blank but ti is retrieving the appropriate status code ie(200 ok). I need to find a way to send back json to my andular2 service.
Below is my WebAPI2 controller:
[Route("api/login")]
public class LoginController : ApiController
{
[HttpGet]
public IHttpActionResult Login()
{
//return Content(HttpStatusCode.OK, Json(new { success = true }));
//return Json(new { success = true });
List<int> myValues = new List<int>(new int[] { 1, 2, 3 });
return Ok(myValues);
}
}
My angular2 Service:
import { Injectable } from '#angular/core';
import { Http, Headers, Response } from '#angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map'
#Injectable()
export class AuthService {
isLoggedIn = false;
private _url = "http://localhost:61333/";
constructor(private _http: Http) { }
login(login) {
return this._http.get(this._url + "api/login").map(res => res.json());
}
logout() {
this.isLoggedIn = false;
}
}
And my angular2 component that is invoking my login function in my service
import { Component, OnInit } from '#angular/core';
import { Router } from '#angular/router';
import { FormGroup } from '#angular/forms';
// Services
import { AuthService } from '../../_services/auth.service';
// Models
import { Login } from '../../_models/login.class';
#Component({
styles: [require('./login.component.css')],
template: require('./login.component.html')
//providers: [AuthService]
})
export class LoginComponent implements OnInit {
loginObj = new Login();
constructor(private _authService: AuthService, private _router: Router) {
}
login(form) {
this.loginObj.email = form.form._value.email;
this.loginObj.password = form.form._value.password;
this._authService.login(this.loginObj).subscribe(
value => { console.log("SUC:" + value); }
);
}
}
Thanks you.
Ross

Categories