I am using JWT Authentication in my project and it is working well.
The issue I am facing is that before using HTTP INTERCEPTOR I was able to get a normal JSON response from the backend (Spring Boot REST API).
But, after using HTTP INTERCEPTOR (for adding AUTHENTICATION header in all HTTP requests) I am not getting JSON response, instead I am getting response as [Object object].
Most important, the backend is giving response in JSON format, I checked it using postman.
auth.interceptor.ts file
intercept(
request: HttpRequest<unknown>,
next: HttpHandler
): Observable<HttpEvent<unknown>> {
//get token from auth service
let token: any = this.authService.getToken();
//check if guest user is a first time visitor
if (!token) {
alert('no token present');
return next.handle(request.clone());
}
//add token and header to the request
request = this.addTokenAndHeader(request, token);
//return
return next.handle(request).pipe(
catchError((err: HttpErrorResponse) => {
alert('inside catch and pipe');
//redirect to login page if error code 401 or 403
if (err.status === 401 || err.status === 403) {
alert(err.status);
this.authService.clear();
this.router.navigateByUrl('/access/login');
}
return throwError('Something went wrong.');
})
);
}
//add token to http request
private addTokenAndHeader(request: HttpRequest<any>, token: string) {
alert('inside add token and header method');
return request.clone({
setHeaders: {
Authorization: `Bearer ${token}`,
},
});
}
loader.interceptor.ts
intercept(
request: HttpRequest<unknown>,
next: HttpHandler
): Observable<HttpEvent<unknown>> {
this.loaderService.isLoading.next(true);
return next.handle(request).pipe(
finalize(() => {
this.loaderService.isLoading.next(false);
})
);
}
app.module.ts file
#NgModule({
declarations: [AppComponent, NoInternetComponent],
imports: [
BrowserModule,
AppRoutingModule,
HttpClientModule,
MaterialModule,
BrowserAnimationsModule,
],
providers: [
{
provide: HTTP_INTERCEPTORS,
useClass: LoaderInterceptor,
multi: true,
},
{
provide: HTTP_INTERCEPTORS,
useClass: AuthInterceptor,
multi: true,
},
],
bootstrap: [AppComponent],
})
export class AppModule {}
user.service.ts - where I am calling API URL. Here I was able to get normal JSON response. But, not getting after using HTTP INTERCEPTOR. Most important, the backend is giving response in JSON format, I checked it using postman.
getUserByPhone(phone: any) {
return new Promise((resolve) => {
this.http
.get(this.constants.apiURL + '/user/get/phone/' + phone)
.subscribe((data: any) => {
alert('inside getuserbyphone method');
alert(data);
resolve(data);
});
});
}
Your help will be highly appreciated. Please come forward to help me in this case, if you have any information reagrding the same. Thanks in advance for solving my problem. Really appreciate it.
There are two ways to handle this , and one of those you already tried i.e using 'JSON.stringify'.Although this may not be a bad option considering interceptors will remain in place. But if you can't/don't want to update your application code already written and just wanted to achieve this via interceptor then in that case I believe you need to update your interceptor code to format response data to a JSON format before consuming it in the application.
You should create a separate interceptor ( best practice and is completely optional if you want to do this in same interceptor) just to format the response. Since you have not share it , do you mind checking the typeof the [Object object] in the response, I assume it should be obviously HTTPResponse type.
By default , you should see response data in the 'body' key of the returned data. I have created a quick example and below are the snippet for the interceptor.
import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest, HttpResponse } from "#angular/common/http";
import { Injectable } from "#angular/core";
import { Observable } from "rxjs";
import { filter, map } from "rxjs/operators";
#Injectable({
providedIn: 'root',
})
export class FormatResponseInterceptor implements HttpInterceptor {
intercept(
request: HttpRequest<any>,
next: HttpHandler
): Observable<HttpEvent<any>> {
return next.handle(request).pipe(
filter(event => event instanceof HttpResponse),
map((event: HttpResponse<any>) => event.clone({ body: event.body }))
);
}
}
so as you see the response if event.body and you can directly consume it in your application , as below :
import { SampleService } from './service/sample.service';
export interface CatInterface {
fact: string | '',
length: number | 0;
}
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent implements OnInit {
name = 'Angular ' + VERSION.major;
apiData: CatInterface;
constructor(private sampleService: SampleService){}
ngOnInit(){
this.invokeAPI();
}
private invokeAPI(){
this.sampleService.readData().subscribe({
next: (res)=>{
this.apiData = {...res};
},
error: (err)=>{},
complete:()=>{console.log('Service Subscription Completed!');
}
})
}
}
in the above code , I just deconstructed the response object.
So you should first need to check the response object structure and consume it accordingly.
For additional notes, here is my app.component.html code :
<hello name="{{ name }}"></hello>
<p><span style="background-color: yellow"> FACT from the API is : </span> <b> {{apiData?.fact}}</b></p>
<p> <span style="background-color: yellow">LENGTH from the API is : </span><b> {{apiData?.length}}</b></p>
and below is the screen shot for the output :
Hopefully this will help you to solve your problem. Please let me know by providing your feedback so that it will help others as well in future.
Related
I need to parse a json response containing two keys.
The response looks like
{
status: 0;
message: 'some error 404'
}
In pure nodejs or React you could just simply do: if (response.status===1)console.log('success').
However, I've been having a tough time doing this in angular. Could someone guide me and tell me how could I parse the JSON Response?
I have attached a mock-up of the code.
import { HttpClient, HttpHeaders } from '#angular/common/http';
import { Component } from '#angular/core';
#Component({
selector: 'app-create-employee',
templateUrl: './create-employee.component.html',
styleUrls: ['./create-employee.component.css']
})
export class CreateEmployeeComponent {
constructor(private http: HttpClient) { };
onFormSubmit() {
let options = {
headers: new HttpHeaders().set('Content-Type', 'application/x-www-form-urlencoded')
};
let body = new URLSearchParams();
body.set('data', 'stackoverflow');
this.http.post('http://localhost:8080/createEmployee', body.toString(), options)
.subscribe(response => {
console.log(response.status);
console.log(response.message);
});
}
}
According to the documentation, Angular can parse for you objects from string responses if you tell it how to do it. You can use this as an example.
First define an interface inside your component just below your imports:
export interface Response {
status: number,
message: string
}
This tells angular how to parse the json response from your server. The final bit is to use this interface in your post request like this:
this.http.post<Response>('http://localhost:8080/createEmployee', body.toString(), options)
.subscribe(response => {
console.log(response.status);
console.log(response.message);
});
I have an angular application and the client wants the path of the Backend in a json file, so he can change it easily whithout needing of another deployment.
Well i did it, but when i refresh the page or close the app and reopen it, the app don't detect the path of the backend, it is like a problem of retard or synchronisation.
This is the error in the console :
http://***/undefinedapi/Leave/GetlistLeave
This is how i did it :
The json file :
{
"ApiRoot": "http://***/"
}
How i read from the constant from the json file :
import { Injectable } from '#angular/core';
import { Http, Response } from '#angular/http';
import { Observable } from 'rxjs';
import { apiRoot } from '../model/model.apiRoot';
import { map } from 'rxjs/operators';
#Injectable({
providedIn: 'root'
})
export class apiRootService {
static apiRoot: string;
constructor(private http: Http) { }
public initialiseApiRoot()
{
this.http.get('./assets/apiRoot/apiRoot.json').pipe(map((response: Response) =>
<apiRoot>response.json())).subscribe(data => {
apiRootService.apiRoot = data['ApiRoot'];
})
}
}
and then i call this function in the constructor of app.component.ts like this :
this.apiRootService.initialiseApiRoot();
and change the call of the api in every servic elike this :
return this.http.get(apiRootService.apiRoot + .....
Any hlp and thanks
Well, let's suppose you're not facing a cache problem. If it isn't a cache problem, maybe it's a matter of timing.
You can try to set your apiRoot while your app is initializing (before app.component.ts is loaded). You can do that by providing an APP_INITIALIZER as described in Angular docs. If you use a factory that returns a function providing a promise, you'll delay your app initialization until your json file is loaded so you can initialize apiRoot. A factory is a useful approach because it will allow you to inject HttpClient service during initialization in the provider (you'll need it to get your json file).
You can do something like (in your app.module.ts):
...
import {APP_INITIALIZER} from '#angular/core';
...
// Angular will inject the HttpClient because you'll
// tell it that this is a dependency of this factory
// in the providers array
export function getApiRoot(http: HttpClient) {
return () => {
return this.http.get('./assets/apiRoot/apiRoot.json').pipe(
map((response: Response) => <apiRoot>response.json()),
tap((data: any) => apiRootService.apiRoot = data['ApiRoot'])
).toPromise();
};
}
...
#NgModule({
imports: [
...
HttpClientModule,
...
],
providers: [
...
{
provide: APP_INTIALIZER,
useFactory: getApiRoot,
multi: true,
deps: [HttpClient]
}
...
]
})
export class AppModule {}
because you are going with wrong approach. you are seeting url after application is initialized. Refer :- https://medium.com/voobans-tech-stories/multiple-environments-with-angular-and-docker-2512e342ab5a. this will give general idea how to achieve build once and deploy anywhere
I tried to make example Ionic application with API with my laravel project. I made Laravel json response api routes. But in my Ionic home page nothing shows up and it reports me this error: Access to XMLHttpRequest at 'http://b.1p1eqpotato.com/ib/?p=1' from origin 'http://localhost:8100' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'. The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute.
zone-evergreen.js:2952 POST http://b.1p1eqpotato.com/ib/?p=1 net::ERR_FAILED.
And this is console.log of my laravel posts (3) [{…}, {…}, {…}]0: {id: 1, name: "John Doe", course: "Udemy Academy", created_at: "2020-02-01 13:14:40", updated_at: "2020-02-01 13:14:40"}1: {id: 2, name: "Mike Doe", course: "Software Engineering", created_at: "2020-02-01 13:16:43", updated_at: "2020-02-01 13:16:43"}2: {id: 3, name: "Trevor Doe", course: "Api Ionic Laravel", created_at: "2020-02-01 13:58:24", updated_at: "2020-02-01 13:58:24"}length: 3__proto__: Array(0)
udnefined home.page.ts:24
So this is my Ionic code.
My app.module.ts:
import { NgModule } from '#angular/core';
import { BrowserModule } from '#angular/platform-browser';
import { RouteReuseStrategy } from '#angular/router';
import { IonicModule, IonicRouteStrategy } from '#ionic/angular';
import { SplashScreen } from '#ionic-native/splash-screen/ngx';
import { StatusBar } from '#ionic-native/status-bar/ngx';
import { AppComponent } from './app.component';
import { AppRoutingModule } from './app-routing.module';
import { HttpClientModule, HttpClient } from '#angular/common/http';
#NgModule({
declarations: [ AppComponent ],
entryComponents: [],
imports: [ BrowserModule, IonicModule.forRoot(), AppRoutingModule, HttpClientModule ],
providers: [ StatusBar, SplashScreen, { provide: RouteReuseStrategy, useClass: IonicRouteStrategy } ],
bootstrap: [ AppComponent ]
})
export class AppModule {}
My api.service.ts:
import { Injectable } from '#angular/core';
import { Observable, of, throwError } from 'rxjs';
import { HttpClient, HttpHeaders, HttpErrorResponse } from '#angular/common/http';
import { catchError, tap, map } from 'rxjs/operators';
const httpOptions = {
headers: new HttpHeaders({'Content-type': 'applications/json'})
}
const apiUrl = "http://localhost:8000/api/students";
#Injectable({
providedIn: 'root'
})
export class ApiService {
constructor(private http: HttpClient) { }
private handleError(error: HttpErrorResponse) {
if(error.error instanceof ErrorEvent) {
console.error('An error occurred:', error.error.message);
} else {
console.error(
`Backend returned code ${error.status},` +
`body was: ${error.error}`
);
}
return throwError('Something is wrong, please try again.');
}
private extractData(res: Response) {
let body = res;
return body || { };
}
getDataUser(): Observable<any> {
return this.http.get(apiUrl, httpOptions).pipe(
map(this.extractData),
catchError(this.handleError)
);
}
}
This is home.page.ts:
import { Component } from '#angular/core';
import { ApiService } from './../api.service';
#Component({
selector: 'app-home',
templateUrl: 'home.page.html',
styleUrls: ['home.page.scss'],
})
export class HomePage {
datauser: any;
constructor(
public api: ApiService
) {}
ngOnInit(){
this.getDataUser();
}
async getDataUser() {
await this.api.getDataUser()
.subscribe(res => {
console.log(res);
this.datauser = res.results;
console.log(this.datauser);
}, err => {
console.log(err);
});
}
}
And this is my home.page.html:
<ion-header>
<ion-toolbar>
<ion-title>
Ionic Blank
</ion-title>
</ion-toolbar>
</ion-header>
<ion-content>
<ion-list>
<ion-item *ngFor="let data of datauser">
{{data.name}}
</ion-item>
</ion-list>
</ion-content>
So it is showing me array in console but on the Home page view there is nothing. And this error:
Is there any solutions for not showing on the view and this XMLHttpRequest error? Please help! Thank you
EDITED
This is my ApiController.php
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Student;
class ApiController extends Controller
{
public function getAllStudents() {
// logic to get all students goes here
$students = Student::get()->toJson(JSON_PRETTY_PRINT);
return response($students, 200);
}
public function createStudent(Request $request) {
// logic to create a student record goes here
$student = new Student;
$student->name = $request->name;
$student->course = $request->course;
$student->save();
return response()->json([
"message" => "student record created"
], 201);
}
public function getStudent($id) {
// logic to get a student record goes here
}
public function updateStudent(Request $request, $id) {
// logic to update a student record goes here
}
public function deleteStudent ($id) {
// logic to delete a student record goes here
}
}
This is my api.php routes:
<?php
use Illuminate\Http\Request;
/*
|--------------------------------------------------------------------------
| API Routes
|--------------------------------------------------------------------------
|
| Here is where you can register API routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| is assigned the "api" middleware group. Enjoy building your API!
|
*/
Route::middleware('auth:api')->get('/user', function (Request $request) {
return $request->user();
});
Route::get('students', 'ApiController#getAllStudents');
Route::get('students/{id}', 'ApiController#getStudent');
//Route::post('students', 'ApiController#createStudent');
Route::put('students/{id}', 'ApiController#updateStudent');
Route::delete('students/{id}','ApiController#deleteStudent');
And this is my Student.php model:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Student extends Model
{
protected $table = 'students';
protected $fillable = ['name', 'course'];
}
According to Mozilla developer website, CORS happen when a web application tried to send requests to resource that has a different origin (domain, protocol, and port) than its own origin. In my case, http request was made from same host as target resource (localhost) but different port number.
Solution
If you google “how to fix/allow CORS” then you’ll find some websites tells you to add extra headers to your HTTP response by modifying web server configuration. But there is another way you can do in case you have a very little experience with server configuration or you were unable to reach infra guys to ask their help, fix it with Middleware.
What is Middleware?
Middleware is a mechanism for filtering HTTP request coming to your application so you can easily modify HTTP Request and Response in a very convenient way.
Now let’s get to step-by-step:
Create The Class
Create a new file CorsMiddleware.php inside directory app\Http\Middleware
<?php
/**
* Location: /app/Http/Middleware
*/
namespace App\Http\Middleware;
use Closure;
class CorsMiddleware
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
$headers = [
'Access-Control-Allow-Origin' => '*',
'Access-Control-Allow-Methods' => 'POST, GET, OPTIONS, PUT, DELETE',
'Access-Control-Allow-Credentials' => 'true',
'Access-Control-Max-Age' => '86400',
'Access-Control-Allow-Headers' => 'Content-Type, Authorization, X-Requested-With'
];
if ($request->isMethod('OPTIONS'))
{
return response()->json('{"method":"OPTIONS"}', 200, $headers);
}
$response = $next($request);
foreach($headers as $key => $value)
{
$response->header($key, $value);
}
return $response;
}
}
This class is used to modify Http Response header, what important here is line number 22 where we tell browser to accept request from any sources (*)
'Access-Control-Allow-Origin' => '*'
Register Middleware
In order to make CorsMiddleware class known by Laravel next you have to Register it in app\Http\Kernel.php
$app->middleware([
//...
App\Http\Middleware\CorsMiddleware::class
]);
Now you should find that error no more.
Full reading source
this is a link to maps.googleapis.com. You get JSON information about the latitude and longitude in the url.
I need to read this JSON using Typescript and Angular2.
I tried a lot of different google suggestions and (among others) the following code (suggested by angular on this link):
private extractData(res: Response) {
let body = res.json();
return body.data || {};
}
// this is fired when I click on my map, this.lat & this.lng are correctly filled
getLongLatClick($event: any) {
this.lat = $event.coords.lat;
this.lng = $event.coords.lng;
this.url = 'https://maps.googleapis.com/maps/api/geocode/json?latlng='+this.lat+','+this.lng+'';
console.log(this.http.get(this.url).map(this.extractData));
But when I debug in chrome, the "extractData" methode doesn't run.. It seems that the googleapis link isn't JSON for some reason
What do I have to do to read the JSON?
You should create a service that makes the http.get to get the data, similiar to :
import { Injectable } from '#angular/core';
import {Headers, Response, Http, RequestOptions} from "#angular/http";
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/toPromise';
#Injectable()
export class DataService{
private gmapsUrl: string = 'http://maps.googleapis.com/maps/api/geocode/json?latlng=52.48278022207823,6.15234375';
constructor(private http: Http) {};
getAll() {
return this.http.get(this.gmapsUrl).map((response: Response) => response.json());
}
}
Cool, now you have a service that gets the data, which is also injectable. You can inject this service into any consumers you wish and consume the data. That is similar to :
import {Component, OnInit, ElementRef} from '#angular/core';
import {DataService} from "path";
#Component ({
moduleId: module.id,
selector: 'custom',
templateUrl: //your metadata,
styleUrls: //your metadata
})
export class ConsumerComponent implements OnInit{
gmapsData: any = [];
constructor(private dataService:Data) {}
ngOnInit(): void {}
private loadAllUsers() {
this.dataService.getAll().subscribe(response=> {
console.log(response.results); //
this.gmapsData = response;
});
}
}
Hope this helps -> This should give you a solid starting point.
What I haven't actually checked is the mapping between the response of the dataService.getAll() inside the consumer to the actual component property gmapsData, but you should be able to infer how to store it from the console.log(response);
You are using the wrong code in your extractData. As you can see in the JSON response:
{
"results" : [
{
"address_components" : [
{
"long_name" : "20",
"short_name" : "20",
"types" : [ "street_number" ]
}
.......
it has results, not data.
So it should be:
private extractData(res: Response) {
let body = res.json();
return body.results || {};
}
So the following should work fine (using the static url in this example):
this.http.get('http://maps.googleapis.com/maps/api/geocode/json?latlng=52.48278022207823,6.15234375')
.map(this.extractData)
.subscribe(data => console.log(data))
Remember to always subscribe to get your response. And do consider making a service where you do the http-calls and map and then in your component call that service method and subscribe the results!
And it's good to check the network tab and see what the response looks like, and to see if you are getting a response at all.
Hope this helps! :)
I have trying to get json data from when i calling service method. I got a return object but i didn't get json data. Which part i have to change?
"goods.services.ts"
getAllData(): Observable<Product>{
let jwtTok1 ="Something";
let headers = new Headers();
headers.append('Content-Type', 'application/json');
headers.append('Authorization', 'Token ' + jwtTok1);
let options = new RequestOptions({ headers: headers, body: {} });
return this.http.get(BASE_URL_GOODS, options)
.map((res:Response) => {return Observable.of ({type: "success", payload: res.json().data})})
.catch(error => this.handleError(error));
}
Another is test case file "goods.component.spec.ts"
import {
inject,
TestBed,fakeAsync,tick
} from '#angular/core/testing';
import { Component } from '#angular/core';
import {
BaseRequestOptions,
ConnectionBackend,
Http
} from '#angular/http';
import { MockBackend,MockConnection } from '#angular/http/testing';
// Load the implementations that should be tested
import { Observable } from 'rxjs/Rx';
import { AppState } from '../app.service';
import { ActionReducer, Action, Store } from '#ngrx/store';
import { AppStore } from '../models/appstore.model';
import {goodsDescriptionComponent } from './goods-desc.component';
import { goodsService } from '../common/service/goods.service';
import { goodsReducer } from '../common/reducers/goods.reducer';
import {provideStore} from '#ngrx/store';
import { goods} from '../common/models/goods.model';
import { Input } from '#angular/core';
describe('GoodsDescriptionComponent', () => {
//let store:Store<AppStore>;
beforeEach(() => TestBed.configureTestingModule({
providers: [
BaseRequestOptions,
MockBackend,
{
provide: Http,
useFactory: function(backend: ConnectionBackend, defaultOptions: BaseRequestOptions) {
return new Http(backend, defaultOptions);
},
deps: [MockBackend, BaseRequestOptions]
},
goodsService, provideStore({goodsData: goodsReducer}),goodsDescriptionComponent
]}));
it('should url will be same ',
inject(
[goodsService, MockBackend],
fakeAsync((service:ProductsService, backend: MockBackend) => {
backend.connections.subscribe((connection: MockConnection) => {
expect(connection.request.url).toBe(
'http://localhost:3001/goodsMS/');
});
service.getAllData();
console.log("goods what we got: ",service.getAllData());
})));
});
Getting Response Result is,
This Response object getting from console in google chrome. Still i can't get correct solution for getting json data from service method call.i can't reach json server. my json server URL is , "http://localhost:3001/goods". please anyone help me. Thanks in advance.
You need to set a response on the connection
backend.connections.subscribe((connection: MockConnection) => {
expect(connection.request.url).toBe(
'http://localhost:3001/goodsMS/');
connection.mockRepond(new Response(new ResponseOptions({
body: `{"some":"json", "response":"body"}`
}));
});
You need to subscribe to your service call
service.getAllData().subscribe((result) => {
expect(somethingWithData)
})
tick()
since you are using fakeAsync, you need to tick to force completion of the asynchronous observable. If you use async instead of fakeAsync, then you don't need to call tick
You should not be returning an Observable in the service method map function. Just return a normal object.
.map((res:Response) => {type: "success", payload: res.json().data})