1

I want to logout by Angular using POST method, here is my code:

  logout() {
    const url = 'http://localhost:8181/user/logout';
    const xToken = localStorage.getItem('xAuthToken');
    const basicHeader = 'Basic ' + localStorage.getItem('credentials');
    const headers = new Headers({
      'x-auth-token': xToken,
      'Authorization': basicHeader
    });
    // return this.http.get(url, { headers: headers }); // This will work
    return this.http.post(url, { headers: headers }); // This will generate error
  }

And this my backend:

@RequestMapping("/user/logout")
public ResponseEntity<String> logout(){
    SecurityContextHolder.clearContext();
    return new ResponseEntity<String>("Logout Successfully!", HttpStatus.OK);
}

The weird thing is the code above work with this.http.get but will generate below error with this.http.post. And here is error with this.http.post:

POST http://localhost:8181/user/logout 401

If I modify code using HttpClient, like this:

import { Injectable } from '@angular/core';
import { Http, Headers } from '@angular/http';
import { HttpClient } from '@angular/common/http';

@Injectable({
  providedIn: 'root'
})
export class LoginService {

  constructor(private http: HttpClient) {
  }

  sendCredential(username: string, password: string) {
    let url = "http://localhost:8181/token";
    let encodedCredentials = btoa(username + ":" + password);// encode in base64 to send a token
    let basicHeader = "Basic " + encodedCredentials;
    let headers = new Headers({
      'Content-Type': 'application/x-www-form-urlencoded',
      'Authorization': basicHeader
    })
    // send credential method when login component
    return this.http.get(url, { headers: headers }); // Error at this line
  }

  checkSession() {
    const url = 'http://localhost:8181/checkSession';
    const xToken = localStorage.getItem('xAuthToken');
    const basicHeader = 'Basic ' + localStorage.getItem('credentials');
    const headers = new Headers({
      'x-auth-token': xToken,
      'Authorization': basicHeader
    });
    return this.http.get(url, { headers: headers }); // Error at this line
  }

  logout() {
    const url = 'http://localhost:8181/user/logout';
    const xToken = localStorage.getItem('xAuthToken');
    const basicHeader = 'Basic ' + localStorage.getItem('credentials');
    const headers = new Headers({
      'x-auth-token': xToken,
      'Authorization': basicHeader
    });

    return this.http.get(url, { headers: headers }); // Error at this line
  }
}

Then I get error message:

(property) headers?: HttpHeaders | {
    [header: string]: string | string[];
}
Type 'Headers' is not assignable to type 'HttpHeaders | { [header: string]: string | string[]; }'.
  Type 'Headers' is not assignable to type '{ [header: string]: string | string[]; }'.
    Index signature is missing in type 'Headers'.ts(2322)
http.d.ts(1086, 9): The expected type comes from property 'headers' which is declared here on type '{ headers?: HttpHeaders | { [header: string]: string | string[]; }; observe?: "body"; params?: HttpParams | { [param: string]: string | string[]; }; reportProgress?: boolean; responseType?: "json"; withCredentials?: boolean; }'

at line return this.http.get(url, { headers: headers });

Anyone know how to fix it?

mrSmith91
  • 338
  • 1
  • 6
  • 18

3 Answers3

1

Try setting headers like this:

let headers = new HttpHeaders();
headers = headers.set('x-auth-token', xToken).set('Authorization', basicHeader);

and then,

return this.http.post(url, null, headers );

Pass null as it accepts body as in the second parameter.

Using HttpClient:

import { HttpClient } from '@angular/common/http';

constructor(private http: HttpClient) { }

in app.module.ts:

import { HttpClientModule } from '@angular/common/http';

and in @NgModule: imports: [ HttpClientModule ]

Adrita Sharma
  • 21,581
  • 10
  • 69
  • 79
  • I've tried, but got error `Type 'HttpHeaders' is missing the following properties from type 'Headers': forEach, values, toJSON, entries, mayBeSetNormalizedNamets(2739)`. If I change to `return this.http.post(url, headers, null );`, I got the same error – mrSmith91 Jul 02 '19 at 08:46
  • Can you confirm that you are using `HttpClientModule` – Adrita Sharma Jul 02 '19 at 08:50
  • `this.http` --> Please show what your consctructor `(http:HttpClientModule )` – Adrita Sharma Jul 02 '19 at 08:51
  • @mrSmith91 post exists in HttpClientModule, may be there is some syntax issue – Adrita Sharma Jul 02 '19 at 08:57
  • I didn't lie you. I paste error message for you: `Property 'post' does not exist on type 'HttpClientModule'.ts(2339)` – mrSmith91 Jul 02 '19 at 09:00
  • it should be `constructor(private http: HttpClient)`; That issue comes when you try `(http:HttpClientModule )` Please show constructor – Adrita Sharma Jul 02 '19 at 09:04
  • So what is the difference between Http and HttpClient? I used private http: Http in my constructor, but now I need to define another private http:HttpClient for using POST method? Why can't I get both POST and GET method from http or HttpClient – mrSmith91 Jul 02 '19 at 11:08
  • Which version of Angular are you using? HttpModule got obsolete from Angular5. You have to use HttpClientModule from Angular 5 + – Adrita Sharma Jul 02 '19 at 11:10
  • Oh my Lord! I'm using Angular 8. Currently I'm using `Http `, and it works perfectly, but I can't use `Http ` with POST method. Do you have any idea to use both POST and GET by `http` or `HttpClient` ? Because using both seem verbose. – mrSmith91 Jul 02 '19 at 11:17
  • Use both with HttpClient – Adrita Sharma Jul 02 '19 at 11:22
  • And import `HttpClient` from what? `@angular/common/http` or `@angular/common/http/http` or `selenium-webdriver/http`? I tried all, but get error at line `return this.http.get(url, { headers: headers });`, error message: `Type 'Headers' is not assignable to type 'HttpHeaders | { [header: string]: string | string[]; }'. Type 'Headers' is not assignable to type '{ [header: string]: string | string[]; }'. Index signature is missing in type 'Headers'.ts(2322)` – mrSmith91 Jul 02 '19 at 11:29
  • Wait a minute. I am updating the use in the answer – Adrita Sharma Jul 02 '19 at 11:32
  • Updated answer. – Adrita Sharma Jul 02 '19 at 11:36
  • I still get problem. I updated my post with full code when change to `HttpClient `. Please view and help me! – mrSmith91 Jul 02 '19 at 11:40
  • `let headers = new HttpHeaders(); headers = headers.set('x-auth-token', xToken).set('Authorization', basicHeader);` `this.http.get(url, headers );` – Adrita Sharma Jul 02 '19 at 12:03
  • Great, but I got this error `localStorage.setItem('xAuthToken', res.json().token);` because of `Property 'json' does not exist on type 'Object'.`? I changed to `res.toString()`, but won't work. – mrSmith91 Jul 02 '19 at 12:46
  • `res = [object Object]`. Nothing to see – mrSmith91 Jul 02 '19 at 12:53
  • `console.log(typeof res)`. If it is string then ` console.log(JSON.Parse(res)` else expand it and share the screenshot – Adrita Sharma Jul 02 '19 at 12:56
  • I tried this: `https://stackoverflow.com/questions/47484875/angular-5-replacement-for-json-token-in-httpclient?noredirect=1&lq=1`, but it seems OK. Now I will check other things. Thanks for ur help – mrSmith91 Jul 02 '19 at 13:08
  • You are welcome @mrSmith91. If you can somehow show me `res`, I can help u. This is a really small thing – Adrita Sharma Jul 02 '19 at 13:19
1

It's normal, by default @RequestMapping("/user/logout") will accept only GET requests. You have to set the method explicitly

@RequestMapping("/user/logout", method = RequestMethod.POST)
public ResponseEntity<String> logout(){
    SecurityContextHolder.clearContext();
    return new ResponseEntity<String>("Logout Successfully!", HttpStatus.OK);
}

Or use @PostMapping

akuma8
  • 4,160
  • 5
  • 46
  • 82
1

Try this:

constructor(private http:HttpClient){}
logout()
{
    const url = 'http://localhost:8181/user/logout';
    const headers = new HttpHeaders()
      .set('x-auth-token', localStorage.getItem('xAuthToken'));

    return this.http.post(url, '' ,{headers: headers, responseType: 'text'})};

In Spring, my recommendation would be to use @PostMapping or @DeleteMapping annotation instead of @RequestMapping. The reason for using ResponseType as 'text' is because you are providing ResponseEntity<> as of String type and by default Angular perceives the response as JSON.

Further, remember to use localStorage.removeItem('xAuthToken'); in the response when subscribing to that observable & also unsubscribe that observable in ngOnDestroy() lifecycle.

Emad Hanif
  • 11
  • 4
  • So what is the difference between `Http` and `HttpClient`? I used `private http: Http` in my constructor, but now I need to define another `private http:HttpClient` for using POST method? Why can't I get both POST and GET method from `http` or `HttpClient` – mrSmith91 Jul 02 '19 at 11:02
  • `http` module is a deprecated module and `HttpClient` module is a improved replacement of `Http`module. You must use httpClient for all the types of request whether it is GET, POST, PUT, PATCH or DELETE. HttpClient Module was introduced in 4.3 and has many benefits such as progress event, interceptors for middleware logic. The conclusion is use `HttpClient` instead of `Http` as it is deprecated by the Angular Team – Emad Hanif Jul 02 '19 at 11:34
  • I modified to use `HttpClient ` but got other problem. I already updated my code on my post. Please view and help me if possible. – mrSmith91 Jul 02 '19 at 11:45
  • Headers is a part of http module and it is also deprecated. So, now you need to set headers in this way:new HttpHeaders().set('name', value)' For Example, in sendCredential() you have to use `const headers = new HttpHeaders().set( 'Content-Type','application/x-www-form-urlencoded').set('Authorization', basicHeader); return this.http.get(url, { headers: headers });` – Emad Hanif Jul 02 '19 at 12:27
  • Great, but I got this error `localStorage.setItem('xAuthToken', res.json().token);` because of `Property 'json' does not exist on type 'Object'.`? I changed to `res.toString()`, but won't work. – mrSmith91 Jul 02 '19 at 12:46
  • `res.json()` would not be applicable as Angular by default perceives json response. So the best way is: `this.loginService.sendCredential(username,password) .subscribe( (res:any) => { localStorage.setItem('xAuthToken', res.token ) } );` – Emad Hanif Jul 02 '19 at 13:17