进行API调用时,即使请求计数增加,也会出现404错误

时间:2018-12-11 22:39:44

标签: c# angular visual-studio-2017 iis-express angular7

这与Cannot find module @angular/core完全分开,即使我仍然拥有该解决方案包,以防万一有人有想法。

因此,在同一解决方案中,我有一个Angular7站点和一个Web API项目。我将IIS Express设置为仅使用端口5000;节点将使用端口4200。在Web API项目中,我创建了一个新的“ AccountController”控制器,一个“ LoginAttempt”模型和一个“ LoginResult”模型。

在Angular端,我有login.component.html,login.component.ts,login.service.ts和serviceCall.service.ts文件。 login.component.ts由html文件更新,并将请求传递到login.service.ts文件,后者将内容打包并发送到serviceCall.service.ts文件,以发送到API。

尝试调用时发生的情况是我收到404错误,但是Visual Studio递增了关联到Login调用的“ requests”值。我似乎找不到任何原因来说明为什么我会收到404并仍然在通话尝试中增加请求。

源代码: C#Web API: Startup.cs

   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)
        {
            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.UseDeveloperExceptionPage();
            }

            app.UseMvc();
        }
    }

LoginAttempt.cs

[Serializable]
public class LoginAttempt
{
    public string username { get; set; }
    public string password { get; set; }
}

LoginResult.cs

[Serializable]
public class LoginResult
{
    public string token { get; set; }
    public string message { get; set; }
    public bool success { get; set; }
}

AccountController.cs

[Route("api/[controller]")]
public class AccountController : Controller
{
    Account accountRepo = new Account();

    [HttpPost]
    public LoginResult Login(LoginAttempt input)
    {
        return accountRepo.verifyCredentials(input.username, input.password);
    }

    public IActionResult Index()
    {
        return View();
    }
}

角度7 proxy.conf.json

{
  "exclude": [
    "**/bin",
    "**/bower_components",
    "**/jspm_packages",
    "**/node_modules",
    "**/obj",
    "**/platforms"
  ],
  "/api": {
    "target": "http://localhost:5000",
    "secure": false
  }
}

angular.json

{
  "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
  "version": 1,
  "newProjectRoot": "projects",
  "projects": {
    "smart-goal": {
      "root": "",
      "sourceRoot": "src",
      "projectType": "application",
      "prefix": "app",
      "schematics": {},
      "architect": {
        "build": {
          "builder": "@angular-devkit/build-angular:browser",
          "options": {
            "outputPath": "dist/smart-goal",
            "index": "src/index.html",
            "main": "src/main.ts",
            "polyfills": "src/polyfills.ts",
            "tsConfig": "src/tsconfig.app.json",
            "assets": [
              "src/favicon.ico",
              "src/assets"
            ],
            "styles": [
              "src/styles.css"
            ],
            "scripts": []
          },
          "configurations": {
            "production": {
              "fileReplacements": [
                {
                  "replace": "src/environments/environment.ts",
                  "with": "src/environments/environment.prod.ts"
                }
              ],
              "optimization": true,
              "outputHashing": "all",
              "sourceMap": false,
              "extractCss": true,
              "namedChunks": false,
              "aot": true,
              "extractLicenses": true,
              "vendorChunk": false,
              "buildOptimizer": true,
              "budgets": [
                {
                  "type": "initial",
                  "maximumWarning": "2mb",
                  "maximumError": "5mb"
                }
              ]
            }
          }
        },
        "serve": {
          "builder": "@angular-devkit/build-angular:dev-server",
          "options": {
            "browserTarget": "smart-goal:build",
            "proxyConfig": "proxy.conf.json"
          },
          "configurations": {
            "production": {
              "browserTarget": "smart-goal:build:production"
            }
          }
        },
        "extract-i18n": {
          "builder": "@angular-devkit/build-angular:extract-i18n",
          "options": {
            "browserTarget": "smart-goal:build"
          }
        },
        "test": {
          "builder": "@angular-devkit/build-angular:karma",
          "options": {
            "main": "src/test.ts",
            "polyfills": "src/polyfills.ts",
            "tsConfig": "src/tsconfig.spec.json",
            "karmaConfig": "src/karma.conf.js",
            "styles": [
              "src/styles.css"
            ],
            "scripts": [],
            "assets": [
              "src/favicon.ico",
              "src/assets"
            ]
          }
        },
        "lint": {
          "builder": "@angular-devkit/build-angular:tslint",
          "options": {
            "tsConfig": [
              "src/tsconfig.app.json",
              "src/tsconfig.spec.json"
            ],
            "exclude": [
              "**/node_modules/**"
            ]
          }
        }
      }
    },
    "smart-goal-e2e": {
      "root": "e2e/",
      "projectType": "application",
      "prefix": "",
      "architect": {
        "e2e": {
          "builder": "@angular-devkit/build-angular:protractor",
          "options": {
            "protractorConfig": "e2e/protractor.conf.js",
            "devServerTarget": "smart-goal:serve"
          },
          "configurations": {
            "production": {
              "devServerTarget": "smart-goal:serve:production"
            }
          }
        },
        "lint": {
          "builder": "@angular-devkit/build-angular:tslint",
          "options": {
            "tsConfig": "e2e/tsconfig.e2e.json",
            "exclude": [
              "**/node_modules/**"
            ]
          }
        }
      }
    }
  },
  "defaultProject": "smart-goal"
}

LoginCredentials.ts

export class LoginCredentials {
  username: string | undefined;
  password: string | undefined;
}

LoginResults.ts

export interface ILoginResult {
  token: string,
  message: string,
  success: boolean
}

login.component.html

<p>Login Page</p>
<form>
  <label>Username:</label>
  <input type="text" [(ngModel)]="Username" name="Username"/>
  <label>Password:</label>
  <input type="password" [(ngModel)]="Password" name="Password"/>
  <button type="submit" (click)="LoginAttempt()">Submit</button>
</form>

login.component.ts

import { Component } from '@angular/core';
import { Router } from '@angular/router';
import { Input } from '@angular/core';
import { LoginCredentials } from '../models/loginCredentials';
import { LoginService } from './login.service';

@Component({
  selector: 'login',
  templateUrl: './login.component.html'
})
export class LoginComponent {
  private router: Router;
  private Username: string;
  private Password: string;
  private Login: LoginCredentials;
  private response: undefined;
  private service: LoginService;

  constructor(router: Router, service: LoginService) {
    this.router = router;
    this.Login = new LoginCredentials();
    this.service = service;
    this.Username = "";
    this.Password = "";
  }

  LoginAttempt() {
    let data = new LoginCredentials();
    data.username = this.Username;
    data.password = this.Password;

    this.service.Login(data)
      .subscribe(
        result => {
          let response = JSON.stringify(result);
          alert("SUCCESS - " + response);
        }
      );
  }
}

login.service.ts

import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { LoginCredentials } from '../models/LoginCredentials';
import { ServiceCall } from '../shared/serviceCall.service';
import { ILoginResult } from '../models/LoginResult';
import { map } from 'rxjs/operators';

@Injectable()
export class LoginService {
  call: ServiceCall;
  constructor(call: ServiceCall) {
    this.call = call;
  }

  public Login(loginAttempt: LoginCredentials): Observable<any> {
    let myResponse = new Map<string, string>()
    let data = new Map<string, string>();
    let data2 = new Map<string, string>();
    let url = "Account/Login";
    data.set('Username', loginAttempt.username);
    data.set('Password', loginAttempt.password);
    return this.call.makeCall(url, 'POST', data).pipe(map(response => data2 = response));
  }
}

serviceCall.service.ts

import { Injectable } from '@angular/core';
import { HttpClient, HttpParams, HttpHeaders, HttpRequest, HttpResponse,  } from '@angular/common/http';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';

@Injectable()
export class ServiceCall {
  private _http: HttpClient;
  private _urlAppend: string;
  private _isAuthenticated: boolean;

  constructor(http: HttpClient) {
    this._http = http;
    this._urlAppend = '/api/';
    this._isAuthenticated = false;
  }

  public makeCall(url: string, type: string, data: Map<string, string>): Observable<any> {
    url = this._urlAppend + url;
    let headers = new HttpHeaders();
    headers.append('Content-Type', 'application/json');
    headers.append('charset', 'utf-8');
    let params = new HttpParams();
    let result = new Response();

    data.forEach((value: string, key: string) => {
      params.set(key, value);
    });

    let options = { headers: headers, params: params, withCredentials: this._isAuthenticated };
    let body = JSON.stringify(data);

    if (type == "GET") {
      return this._http
        .get(url, options)
        .pipe(map((result: Response) => result));
    } else if (type == "POST") {
      return this._http
        .post(url, body, options)
        .pipe(map(this.extractData));
    } else {
      Observable.throw("Invalid command.");
    }
  }

  public setAuthentication(input: boolean) {
    this._isAuthenticated = input;
  }

  private extractData(res: Response) {
    let body = res.json();
    return body || {};
  }

  private generateQueryString(input: Map<string, string>) {
    let response = new URLSearchParams();

    input.forEach((value: string, key: string) => {
      response.append(key, value);
    });

    return response;
  }
}

最后,Chrome控制台中显示的404响应:

HttpErrorResponse
{
    error: null
    headers: HttpHeaders
    {
        lazyInit: f()
        lazyUpdate: null
        normalizeNames: Map(0) {}
    }
    message:  "Http failure response for http://localhost:4200/api/Acocunt/Login: 404 Not Found"
    name:  "HttpErrorResponse"
    ok:  false
    status: 404
    statusText:  "Not Found"
    url:  "http://localhost:4200/api/Account/Login"
}

1 个答案:

答案 0 :(得分:0)

U。这个问题。首先,在C#方面:我已经习惯了使用“常规” .NET框架的较早版本的Web API,并且在使用.NET Core时进行了一些更改。在AccountController.cs文件中,需要在类声明的正上方指定该控制器的“分支”,以便在类声明的正上方具有[Route("api/[controller]")]之类的内容。然后,在类中的每个方法上,我必须定义特定方法的URL是什么。在上述情况下,

[HttpPost]
public LoginResult Login(LoginAttempt input)

需要看起来像:

[Route("login")]
[HttpPost]
public IActionResult Login([FromBody]LoginAttempt input)

然后在Typescript方面,Angular2站点忽略了代理信息,而不是尝试转发到我的API端口。我必须在proxy.conf.json文件中使代理信息如下所示:

"/api": {
  "target": "http://localhost:5000",
  "secure": false,
  "changeorigin": true,
  "logLevel": "debug",
  "pathRewrite": {"^/api": ""}
}

我尝试了所有可以找到的版本,直到我添加了“ changeorigin”位,该版本才起作用。这使事情如我所愿。