Angular 4-如何从ASP.Net Web API获取数据 [英] Angular 4 - How to get data from ASP.Net web api

查看:141
本文介绍了Angular 4-如何从ASP.Net Web API获取数据的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

使用著名的Visual Studio 2017 Angular 4模板,我测试了导航栏的侧面按钮并可以获取内存中的数据.

然后,我在项目中添加了一个新的ASP.Net Core 2.0 API控制器,该控制器使用Entity Framework连接到数据库,并使其以200 HTTP GET结果运行.

控制器代码:

#region TodoController
namespace TodoAngularUI.Controllers
{
    [Route("api/[controller]")]
    public class TodoController : Controller
    {
        private readonly SchoolContext _context;
        #endregion

        public TodoController(SchoolContext DbContext)
        {
            _context = DbContext;

            if (_context.Todo.Count() == 0)
            {
                _context.Todo.Add(new Todo { TaskName = "Item1" });
                _context.SaveChanges();
            }
        }

        #region snippet_GetAll
        [HttpGet]
        public IEnumerable<Todo> GetAll()
        {
            return _context.Todo.ToList();
        }

        [HttpGet("{id}", Name = "GetTodo")]
        public IActionResult GetById(long id)
        {
            var item = _context.Todo.FirstOrDefault(t => t.Id == id);
            if (item == null)
            {
                return NotFound();
            }
            return new ObjectResult(item);
        }
        #endregion

现在,我想使用Angular显示生成的ASP.Net Core控制器数据,因此我创建了一个名为"todo"的TypeScript组件,如下所示:

import { Component, Inject } from '@angular/core';
import { Http } from '@angular/http';

@Component({
    selector: 'todo',
    templateUrl: './todo.component.html'
})
export class TodoComponent {
    public Todo: task[];

    constructor(http: Http, @Inject('BASE_URL') baseUrl: string) {
        http.get(baseUrl + '/api/todo').subscribe(result => {
            this.Todo = result.json() as task[];
        }, error => console.error(error));
    }
}

interface task {
    Id: number;
    TaskName: string;
    IsComplete: boolean;
}

并如下创建其HTML组件:

<h1>Todo tasks</h1>

<p>This component demonstrates fetching Todo tasks from the server.</p>

<p *ngIf="!todo"><em>Loading...</em></p>

<table class='table' *ngIf="Todo">
    <thead>
        <tr>
            <th>Id</th>
            <th>Task Name</th>
            <th>Is complete</th>
        </tr>
    </thead>
    <tbody>
        <tr *ngFor="let Task of todo">
            <td>{{ Task.Id }}</td>
            <td>{{ Task.TaskName }}</td>
            <td>{{ Task.Iscomplete }}</td>
        </tr>
    </tbody>
</table>

然后在Nav侧边栏菜单中添加其路由,这是TypeScript代码:

import { Component } from '@angular/core';

@Component({
    selector: 'nav-menu',
    templateUrl: './navmenu.component.html',
    styleUrls: ['./navmenu.component.css']
})
export class NavMenuComponent {
}

这是Navbar HTML代码:

<div class='main-nav'>
<div class='navbar navbar-inverse'>
    <div class='navbar-header'>
        <button type='button' class='navbar-toggle' data-toggle='collapse' data-target='.navbar-collapse'>
            <span class='sr-only'>Toggle navigation</span>
            <span class='icon-bar'></span>
            <span class='icon-bar'></span>
            <span class='icon-bar'></span>
            <span class='icon-bar'></span>
        </button>
        <a class='navbar-brand' [routerLink]="['/home']">TodoAngularUI</a>
    </div>
    <div class='clearfix'></div>
    <div class='navbar-collapse collapse'>
        <ul class='nav navbar-nav'>
            <li [routerLinkActive]="['link-active']">
                <a [routerLink]="['/home']">
                    <span class='glyphicon glyphicon-home'></span> Home
                </a>
            </li>
            <li [routerLinkActive]="['link-active']">
                <a [routerLink]="['/counter']">
                    <span class='glyphicon glyphicon-education'></span> Counter
                </a>
            </li>
            <li [routerLinkActive]="['link-active']">
                <a [routerLink]="['/fetch-data']">
                    <span class='glyphicon glyphicon-th-list'></span> Fetch data
                </a>
            </li>
            <li [routerLinkActive]="['link-active']">
                <a [routerLink]="['/api/todo']">
                    <span class='glyphicon glyphicon-apple'></span> Todo api
                </a>
            </li>
        </ul>
    </div>
</div>

还有我的app.component.ts:

import { Component } from '@angular/core';

@Component({
    selector: 'app',
    templateUrl: './app.component.html',
    styleUrls: ['./app.component.css']
})
export class AppComponent {
}

这里的问题是,当我单击菜单按钮以获取数据时,什么都没发生,其他所有按钮都在工作,但不能正常工作,直接使用浏览器URL时仍显示200个结果.

没有错误消息,我无法找到在网络上搜索与Angular中不可单击的按钮以及将数据从ASP.Net传递到Angular有关的问题的解决方案.

我在做什么错了?

解决方案

(答案来自我上面的评论)

在使用Microsoft的Angular 4模板之前,我遇到了类似的问题.


问题

Microsoft提供BASE_URL字符串作为模板的一部分-它是通过​​从index.cshtml中的base标记中提取href属性而获得的(BASE_URL字符串不是Angular框架的一部分)./p>

index.cshtml中的base标记应类似于<base href="~/" />

这意味着在Angular 4项目中使用BASE_URL的任何地方都已经有带有/字符后缀的BASE_URL.

因此,请查看使用该URL调用http.get的组件:

@Component({
    selector: 'todo',
    templateUrl: './todo.component.html'
})
export class TodoComponent {
    public Todo: task[];

    constructor(http: Http, @Inject('BASE_URL') baseUrl: string) {
        http.get(baseUrl + '/api/todo').subscribe(result => {
            this.Todo = result.json() as task[];
        }, error => console.error(error));
    }
}

请注意,您对http.get(baseUrl + '/api/todo')的调用在/api/todo前面有一个/-因此,由于BASE_URL多余的/,传递给http.get的参数看起来像http://example.com//api/todo.


解决方案

改为尝试http.get(baseUrl + 'api/todo')(请注意api/todo前面没有/)-如果模板中的其他内容均未更改,则BASE_URL字符串应已包含该字符串.


更新22-03-2018:使用HTTP POST

根据下面的评论,这是一个用于POST的快速示例函数,假设baseUrlhttp都已注入到构造函数中:

import { Observable } from 'rxjs/rx';
import { Http, Headers, RequestOptions } from '@angular/http';

@Component({
    selector: 'todo',
    templateUrl: './todo.component.html'
})
export class TodoComponent {
    constructor(private http: Http, 
        @Inject('BASE_URL') private baseUrl: string) {
    }

    post(todo: Todo) {    
        let fullUrl = this.baseUrl + 'api/todo';
        let headers = new Headers({ 'Content-Type': 'application/json' });
        let options = new RequestOptions({ headers: headers });
        this.http.post(fullUrl, JSON.stringify(todo), options)
            .subscribe(result => {
                console.log(result);
        }, error => console.error(error));
    }
}

在ASP.NET WebAPI方面(隐式知道如何在HTTP POST请求中处理application/jsonContent-Type):

public class TodoController : Controller
{
    [HttpPost]
    public IActionResult Post([FromBody] Todo todo)
    {
        return Ok();
    }
}

Using the famous Visual Studio 2017 Angular 4 template, I tested the side navbar buttons and could fetch the in-memory data.

Then I added to the project, a new ASP.Net Core 2.0 API controller connected to a database using Entity Framework, and got it to run with the 200 HTTP GET result.

The controller code:

#region TodoController
namespace TodoAngularUI.Controllers
{
    [Route("api/[controller]")]
    public class TodoController : Controller
    {
        private readonly SchoolContext _context;
        #endregion

        public TodoController(SchoolContext DbContext)
        {
            _context = DbContext;

            if (_context.Todo.Count() == 0)
            {
                _context.Todo.Add(new Todo { TaskName = "Item1" });
                _context.SaveChanges();
            }
        }

        #region snippet_GetAll
        [HttpGet]
        public IEnumerable<Todo> GetAll()
        {
            return _context.Todo.ToList();
        }

        [HttpGet("{id}", Name = "GetTodo")]
        public IActionResult GetById(long id)
        {
            var item = _context.Todo.FirstOrDefault(t => t.Id == id);
            if (item == null)
            {
                return NotFound();
            }
            return new ObjectResult(item);
        }
        #endregion

Now, I wanted to display the resulting ASP.Net Core controller data using Angular, so I created a TypeScript component named "todo" as below:

import { Component, Inject } from '@angular/core';
import { Http } from '@angular/http';

@Component({
    selector: 'todo',
    templateUrl: './todo.component.html'
})
export class TodoComponent {
    public Todo: task[];

    constructor(http: Http, @Inject('BASE_URL') baseUrl: string) {
        http.get(baseUrl + '/api/todo').subscribe(result => {
            this.Todo = result.json() as task[];
        }, error => console.error(error));
    }
}

interface task {
    Id: number;
    TaskName: string;
    IsComplete: boolean;
}

And created its HTML component as below:

<h1>Todo tasks</h1>

<p>This component demonstrates fetching Todo tasks from the server.</p>

<p *ngIf="!todo"><em>Loading...</em></p>

<table class='table' *ngIf="Todo">
    <thead>
        <tr>
            <th>Id</th>
            <th>Task Name</th>
            <th>Is complete</th>
        </tr>
    </thead>
    <tbody>
        <tr *ngFor="let Task of todo">
            <td>{{ Task.Id }}</td>
            <td>{{ Task.TaskName }}</td>
            <td>{{ Task.Iscomplete }}</td>
        </tr>
    </tbody>
</table>

Then went to add its routing in Nav side bar menu, here is TypeScript code:

import { Component } from '@angular/core';

@Component({
    selector: 'nav-menu',
    templateUrl: './navmenu.component.html',
    styleUrls: ['./navmenu.component.css']
})
export class NavMenuComponent {
}

And here is Navbar HTML code:

<div class='main-nav'>
<div class='navbar navbar-inverse'>
    <div class='navbar-header'>
        <button type='button' class='navbar-toggle' data-toggle='collapse' data-target='.navbar-collapse'>
            <span class='sr-only'>Toggle navigation</span>
            <span class='icon-bar'></span>
            <span class='icon-bar'></span>
            <span class='icon-bar'></span>
            <span class='icon-bar'></span>
        </button>
        <a class='navbar-brand' [routerLink]="['/home']">TodoAngularUI</a>
    </div>
    <div class='clearfix'></div>
    <div class='navbar-collapse collapse'>
        <ul class='nav navbar-nav'>
            <li [routerLinkActive]="['link-active']">
                <a [routerLink]="['/home']">
                    <span class='glyphicon glyphicon-home'></span> Home
                </a>
            </li>
            <li [routerLinkActive]="['link-active']">
                <a [routerLink]="['/counter']">
                    <span class='glyphicon glyphicon-education'></span> Counter
                </a>
            </li>
            <li [routerLinkActive]="['link-active']">
                <a [routerLink]="['/fetch-data']">
                    <span class='glyphicon glyphicon-th-list'></span> Fetch data
                </a>
            </li>
            <li [routerLinkActive]="['link-active']">
                <a [routerLink]="['/api/todo']">
                    <span class='glyphicon glyphicon-apple'></span> Todo api
                </a>
            </li>
        </ul>
    </div>
</div>

And my app.component.ts:

import { Component } from '@angular/core';

@Component({
    selector: 'app',
    templateUrl: './app.component.html',
    styleUrls: ['./app.component.css']
})
export class AppComponent {
}

The issue here is that when I click the menu button to get data nothing happens, all other buttons are working but not this one, and 200 result is still showing when use directly the browser URL.

No error message, and I failed to find solution searching on the net for issues related to Non clickable buttons in Angular, and related to passing data from ASP.Net to Angular.

What am I doing wrong?

解决方案

(Answer derived from my comments above)

I have had a similar problem to this before using Microsoft's Angular 4 template.


The Problem

Microsoft provides the BASE_URL string as part of their template - it's obtained by extracting the href attribute from the base tag in index.cshtml (the BASE_URL string isn't part of the Angular framework).

The base tag in index.cshtml should look like <base href="~/" />

Which means that anywhere using BASE_URL in your Angular 4 project already has the BASE_URL suffixed with a / character.

So looking at this component calling http.get using that URL:

@Component({
    selector: 'todo',
    templateUrl: './todo.component.html'
})
export class TodoComponent {
    public Todo: task[];

    constructor(http: Http, @Inject('BASE_URL') baseUrl: string) {
        http.get(baseUrl + '/api/todo').subscribe(result => {
            this.Todo = result.json() as task[];
        }, error => console.error(error));
    }
}

Note that your call to http.get(baseUrl + '/api/todo') has a / in front of /api/todo - so the parameter passed into http.get will look like http://example.com//api/todo due to the extra / from BASE_URL.


The Solution

Try http.get(baseUrl + 'api/todo') instead (note the absence of / in front of api/todo) - the BASE_URL string should already include that, if nothing else in the template has been changed.


Update 22-03-2018: Using HTTP POST

As per the comment below, here's a quick example function for POST, assuming that baseUrl and http have both been injected into the constructor:

import { Observable } from 'rxjs/rx';
import { Http, Headers, RequestOptions } from '@angular/http';

@Component({
    selector: 'todo',
    templateUrl: './todo.component.html'
})
export class TodoComponent {
    constructor(private http: Http, 
        @Inject('BASE_URL') private baseUrl: string) {
    }

    post(todo: Todo) {    
        let fullUrl = this.baseUrl + 'api/todo';
        let headers = new Headers({ 'Content-Type': 'application/json' });
        let options = new RequestOptions({ headers: headers });
        this.http.post(fullUrl, JSON.stringify(todo), options)
            .subscribe(result => {
                console.log(result);
        }, error => console.error(error));
    }
}

And on the ASP.NET WebAPI side (which implicitly knows how to handle Content-Type of application/json in an HTTP POST request):

public class TodoController : Controller
{
    [HttpPost]
    public IActionResult Post([FromBody] Todo todo)
    {
        return Ok();
    }
}

这篇关于Angular 4-如何从ASP.Net Web API获取数据的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆