使用HttpClient和RXJS向Web API POST数据,withCredentials为true。

4
我有一个与.NET Web API连接的Angular服务,我正在尝试将一些数据POST到API。目前我使用的是HTTP,而不是HttpClient,并且没有发送任何数据。但是服务成功连接到了API。
我需要帮助将实际数据从我的Angular控制器传递到服务(从而传递到API),并在服务中实现HttpClient。到目前为止,我的控制器仅调用我的服务的myFunction()函数,并且不传递任何参数,因此没有数据。我不确定在服务的RXJS部分中应该在哪里添加数据。
注意:无论我如何实现,由于我的API配置,仍需要传递withCredentials: true
Web API控制器:
namespace api.controllers
{
    [Authorize]
    public class ValuesController : ApiController
     {
        static List<string> strings = new List<string>()
        {
            "value0", "value1", "value2"
        };

        // GET api/values
        public IEnumerable<string> Get()
        {
            return strings;
        }

        // GET api/values/5
        public string Get(int id)
        {
            return "value";
        }

        // POST api/values
        public void Post([FromBody]string value)
        {
            strings.Add(value);
        }

        // PUT api/values/5
        public void Put(int id, [FromBody]string value)
        {
        }

        // DELETE api/values/5
        public void Delete(int id)
        {
        }

    }
}

Web API web.config 文件(CORS 设置):

<httpProtocol>
  <customHeaders>
    <add name="Access-Control-Allow-Origin" value="http://localhost:5200" />
    <add name="Access-Control-Allow-Headers" value="*" />
    <add name="Access-Control-Allow-Methods" value="GET,POST,PUT,DELETE,OPTIONS" />
    <add name="Access-Control-Allow-Credentials" value="true" />
  </customHeaders>
</httpProtocol>


myComponent.component.ts:

  myService: MyService;

  constructor(myService: MyService) {
      this.myService = myService;
      this.myService.myFunction();
   }


myService.service.ts:

import { Injectable } from '@angular/core';
import { Http, Response, Request, Headers } from '@angular/http';
// import { HttpClient, HttpResponse, HttpRequest, HttpHeaders, HttpInterceptor, HttpHandler, HttpEvent } from '@angular/common/http';

import { Observable } from 'rxjs';
import { from } from 'rxjs';
import { map, filter, catchError, mergeMap } from 'rxjs/operators';

@Injectable({
  providedIn: 'root'
})

export class MyService {
  http: Http;

  constructor(http: Http) {
    this.http = http;
  };

  public myFunction() {
    let request = new Request({
      method: "POST",
      url: "http://localhost:9090/api/values",
      withCredentials: true
    });

    return this.http.request(request)
      .pipe(map(res => res.json()))
      .subscribe(
        data => console.warn(data),
        err => console.error(err),
        () => console.log("empty")
      );
  }
}

我该如何在控制器中向此服务添加一些实际数据?我应该如何调整服务以使用HttpClient呢?我尝试将所有http:Http引用更改为HttpClient,导入了所有的HttpClient并注释掉了.map/json部分,但是当我这样做时,在我的服务的return this.http.request(request)行中的request参数下面仍然会出现红线。
3个回答

4

我希望你也能使用这个概念,它涉及到IT技术。

为你的数据创建属性类(与你的 .net API 类匹配),这样可以提供简单的数据处理方式。 模型

export class MyData
{
 username:string;
 password:string;
 isAuthenticated:boolean;
}

服务

import { Http, Response, Request, Headers } from '@angular/http';

export class MyService {     

  constructor(private http: Http) {}

public myFunction(body:MyData) {
 let url = 'http://localhost:9090/api/values'
    return this.http.post(url,body)
      .pipe(map(res => res.json()))          
  }
}

TS

returnValue:any;
myData:MyData;
constructor(private service:MyService){
this.myData = new MyData();
}
myFunction()
{
 this.myData.username = 'anything';
 this.myData.password = 'mypw';
 this.myData.isAuthenticated = true;

 this.returnValue = this.service.myFunction(this.myData)
 .subscribe(res=>{console.log(res)})
}

.NET API

[HttpPost]
public void MYAPI([FromBody]MyData model)
{

    string uname = model.username;
    string pw = model.password;
}

谢谢!您会注意到在我的示例中,我将withCredentials: true键/值对传递给了服务中的myFunction。由于我的API配置,这是我必须要做的事情。请问如何在您的示例中包含withCredentials: true - Kyle Vassella
嗨,Kyle,你能解决问题了吗?(在进行POST时传递凭据?) - free

1
为了使用HttpClient,您需要在app.module.ts中导入HttpClientModule而不是HttpModule,并开始注入HttpClient对象而不是http
@Injectable({
  providedIn: 'root'
})

export class MyService {

  constructor(private http: HttpClient) {
  };

  public myFunction(body) {
    let requestOptions = { withCredentials : true };
    return this.http.post("http://localhost:9090/api/values",body,requestOptions);
  }
}

当您使用HttpClient时,不需要执行.map(res => res.json())操作。
组件
 constructor(myService: MyService) {
      this.myService = myService;
      this.myService.myFunction({username:'test',password:'123465'})
      .subscribe(data => console.warn(data), err => console.error(err),() => console.log("empty")
      );
   }

通常情况下,您不需要从服务订阅以便将数据传递给组件

ngOnInit

作为一般的良好实践,您应该在ngOninit方法中进行初始api请求

关于http的完整指南RequestOptions

最后,这是typescript的提示

这是简写语法

 constructor(private http: HttpClient) {
  };

到这里

private http: HttpClient;
 constructor(http: HttpClient) {
   this.http = http
  };

2
如果您编写 constructor(private myService: MyService) {...},那么组件会更加清晰。这样您就可以在任何地方使用 this.myService - Daniel Habenicht
我在最后写了一个提示,以便他不会感到困惑。@DanielHabenicht - Muhammed Albarmavi
@KyleVassella 我已经更新了我的答案,并向您展示了如何设置请求选项。 - Muhammed Albarmavi
再次感谢。我已经接近成功了。但是使用您的示例后,我现在遇到了“401(未经授权)”错误和“预检响应具有无效的HTTP状态代码401”,就像在我实施原始“withCredentials:true”之前一样。我在我的示例中所做的方式有效,但您的示例不行。 :/ 我查看了其他答案,它们与您的示例类似:https://dev59.com/96vka4cB1Zd3GeqPycjr 有什么想法吗? - Kyle Vassella
使用您的示例,如果我在body中使用null,则请求将通过而不会出现错误。只有当POST具有数据时才会引发401错误。如果我更改您的示例以使用GET请求,则可以通过并且我可以成功从我的API检索数据。因此,关于POST带有数据的某些内容似乎没有注册withCredentials:true-但是其他请求可以很好地发送这些凭据。如果我在GET请求上设置withCredentials:false,它将失败。如果我在GET请求上设置withCredentials:true,它将成功。然而,包括数据的POST失败。 - Kyle Vassella
显示剩余3条评论

1
您的代码看起来很好,除了服务方面。另外,我发现您的API中有[Authorize]。您需要通过授权。
例如:
const httpOptions = {
 headers: new HttpHeaders({
  'Authorization': myToken
 }),
 withCredentials: true
};

为了避免出现“需要授权”的错误,请在您的Http请求中添加授权信息。

这个参考资料可能会有帮助:https://angular.io/guide/security

使用HttpClient的服务:

 import { Injectable } from '@angular/core';
 import { HttpClient, HttpHeaders } from '@angular/common/http';
 import { Observable } from 'rxjs';

 // api path
 const API_URL = 'http://10.111.1.23:80/api/my_api/api/yourController';

 // add http headers.
 const httpOptions = {
    headers: new HttpHeaders({
    'Content-Type': 'application/json'
   })
  };

 @Injectable()
 export class MyService {

 // inject the Http Client to the constructor
 constructor(private _http: HttpClient) { };

 myFunction(entity: any): Observable<yourModel> {
    // using http client the return value of you api is
    // directly map to your model
    return this._http.post<yourModel>(API_URL ,
      entity, httpOptions);
 );
}

“Authorization” 部分是我卡住的地方。无论我尝试什么,当 POST 数据时,我仍然会得到一个 OPTIONS 401 (未授权) 错误。我已经启用了 cors 等等。**我的 Web API 目前正在使用 Windows Authentication。我相信这可能是 [Authorize] 属性所关联的内容。我甚至可以将 'Authorization': mytoken 的值传递为什么?在 mytoken 的位置应该放什么?感谢您的回答。 - Kyle Vassella
myToken是一个变量,包含jwt令牌(请参见jwt.io)。在我的情况下,我将我的令牌存储在localStorage中,然后检索并将其存储到myToken变量中,然后将其附加到我的授权标头。 - M.Laida

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接