热门标签 | HotTags
当前位置:  开发笔记 > 前端 > 正文

详解如何在Angular优雅编写HTTP请求

这篇文章主要介绍了详解如何在Angular优雅编写HTTP请求,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

引言

基本上当下的应用都会分为前端与后端,当然这种前端定义不在限于桌面浏览器、手机、APP等设备。一个良好的后端会通过一套所有前端都通用的 RESTful API 序列接口作为前后端之间的通信。

这其中对于身份认证都不可能再依赖传统的Session或COOKIE;转而使用诸如OAuth2、JWT等这种更适合API接口的认证方式。当然本文并不讨论如何去构建它们。

一、API 设计

首先虽然并不会讨论身份认证的技术,但不管是OAuth2还是JWT本质上身份认证都全靠一个 Token 来维持;因此,下面统一以 token 来表示身份认证所需要的值。

一套合理的API规则,会让前端编码更优雅。因此,希望在编写Angular之前,能与后端相互达成一种“协议”也很有必要。可以尝试从以下几点进行考虑。

版本号

可以在URL(例:https://demo.com/v1/)或Header(例:headers: { version: 'v1' } )中体现,相比较我更喜欢前者的直接。

业务节点

以一个节点来表示某个业务,比如:

  • 商品 https://demo.com/v1/product/
  • 商品SKU https://demo.com/v1/product/sku/

动作

由HTTP动词来表示:

  • GET 请求一个商品 /product/${ID}
  • POST 新建一个商品 /product
  • PUT 修改一个商品 /product/${ID}
  • DELETE 删除一个商品 /product/${ID}

统一响应

这一点非常重要,特别是当我们新建一个商品时,商品的属性非常多,但如果我们缺少某个属性时。可以使用这样的一种统一的响应格式:

{
  "code": 100, // 0 表示成功
  "errors": { // 错误明细
    "title": "商品名称必填"
  }
}

其中 code 不管成功与否都会有该属性。

状态码

后端响应一个请求是包括状态码和响应内容,而每一种状态码又包含着不同的含义。

  • 200 成功返回请求数据
  • 401 无权限
  • 404 无效资源

二、如何访问Http?

首先,需要导入 HttpClientModule 模块。

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

@NgModule({
  imports: [
    HttpClientModule
  ]
})

然后,在组件类注入 HttpClient。

export class IndexComponent {
  constructor(private http: HttpClient) { }
}

最后,请求点击某个按钮发送一次GET请求。

user: Observable;
getUser() {
  this.user = this.http.get('/assets/data/user.json');
}

打印结果:

{{ user | async | json }}

三个简单的步骤,就是一个完整的HTTP请求步骤。

然后,现实与实际是有一些距离,比如说身份认证、错误处理、状态码处理等问题,在上面并无任何体现。

可,上面已经足够优雅,要让我破坏这种优雅那么此文就变得无意义了!

因此……

三、拦截器

1、HttpInterceptor 接口

正如其名,我们在不改变上面应用层面的代码下,允许我们把身份认证、错误处理、状态码处理问题给解决了!

写一个拦截器也是非常的优雅,只需要实现 HttpInterceptor 接口即可,而且只有一个 intercept 方法。

@Injectable()
export class JWTInterceptor implements HttpInterceptor {

  intercept(req: HttpRequest, next: HttpHandler): Observable | HttpUserEvent> {
    // doing
  }

}

intercept 方法有两个参数,它几乎所当下流行的中间件概念一般,req 表示当前请求数据(包括:url、参数、header等),next 表示调用下一个“中间件”。

2、身份认证

req 有一个 clone 方法,允许对当前的请求参数进行克隆并且这一过程会自行根据一些参数推导,不管如何用它来产生一个新的请求数据,并在这个新数据中加入我们期望的数据,比如:token。

const jwtReq = req.clone({
  headers: req.headers.set('token', 'xxxxxxxxxxxxxxxxxxxxx')
});

当然,你可以再折腾更多请求前的一些配置。

最后,把新请求参数传递给下一个“中间件”。

return next.handle(jwtReq);

等等,都 return 了,说好的状态码、异常处理呢?

3、异常处理

仔细再瞧 next.handle 返回的是一个 Observable 类型。看到 Observable 我们会想到什么?mergeMap、catch 等一大堆东西。

因此,我们可以利用这些操作符来改变响应的值。

mergeMap

请求过程中会会有一些过程状态,比如请求前、上传进度条、请求结束等,Angular在每一次这类动作中都会触次 next。因此,我们只需要在返回 Observable 对象加上 mergeMap 来观察这些值的变更,这样有非常大的自由空间想象。

return next.handle(jwtReq).mergeMap((event: any) => {
    if (event instanceof HttpResponse && event.body.code !== 0) {
      return Observable.create(observer => observer.error(event));
    }
    return Observable.create(observer => observer.next(event));
  })

只会在请求成功才会返回一个 HttpResponse 类型,因此,我们可以大胆判断是否来源于 HttpResponse 来表示HTTP请求已经成功。

这里,统一对业务层级的错误 code !== 0 产生一个错误信号的 Observable。反之,产生一个成功的信息。

catch

catch 来捕获非200以外的其他状态码的错误,比如:401。同时,前面的 mergeMap 所产生的错误信号,也会在这里被捕获到。

.catch((res: HttpResponse) => {
  switch (res.status) {
    case 401:
      // 权限处理
      location.href = ''; // 重新登录
      break;
    case 200:
      // 业务层级错误处理
      alert('业务错误:' + res.body.code);
      break;
    case 404:
      alert('API不存在');
      break;
  }
  return Observable.throw(res);
})

4、完整代码

至此,拦截器所要包括的身份认证token、统一响应处理、异常处理都解决了。

@Injectable()
export class JWTInterceptor implements HttpInterceptor {

  constructor(private notifySrv: NotifyService) {}

  intercept(req: HttpRequest, next: HttpHandler): Observable | HttpUserEvent> {
    console.log('interceptor')
    const jwtReq = req.clone({
      headers: req.headers.set('token', 'asdf')
    });
    return next
      .handle(jwtReq)
      .mergeMap((event: any) => {
        if (event instanceof HttpResponse && event.body.code !== 0) {
          return Observable.create(observer => observer.error(event));
        }
        return Observable.create(observer => observer.next(event));
      })
      .catch((res: HttpResponse) => {
        switch (res.status) {
          case 401:
            // 权限处理
            location.href = ''; // 重新登录
            break;
          case 200:
            // 业务层级错误处理
            this.notifySrv.error('业务错误', `错误代码为:${res.body.code}`);
            break;
          case 404:
            this.notifySrv.error('404', `API不存在`);
            break;
        }
        // 以错误的形式结束本次请求
        return Observable.throw(res);
      })
  }
}

发现没有,我们并没有加一大堆并不认识的事物,单纯都只是对数据流的各种操作而已。

NotifyService 是一个无须依赖HTML模板、极简Angular通知组件。

5、注册拦截器

拦截器构建后,还需要将其注册至 HTTP_INTERCEPTORS 标识符中。

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

@NgModule({
  imports: [
    HttpClientModule
  ],
  providers: [
    { provide: HTTP_INTERCEPTORS, useClass: JWTInterceptor, multi: true}
  ]
})

以上是拦截器的所有内容,在不改变原有的代码的情况下,我们只是利用短短几行的代码实现了身份认证所需要的TOKEN、业务级统一响应处理、错误处理动作。

四、async 管道

一个 Observable 必须被订阅以后才会真正的开始动作,前面在HTML模板中我们利用了 async 管道简化了这种订阅过程。

{{ user | async | json }}

它相当于:

let user: User;
get() {
  this.http.get('/assets/data/user.json').subscribe(res => {
    this.user = res;
  });
}
{{ user | json }}

然而,async 这种简化,并不代表失去某些自由度,比如说当在获取数据过程中显示【加载中……】,怎么办?

{{ user | json }}
加载中……

恩!

五、结论

Angular在HTTP请求过程中使用 Observable 异步数据流控制数据,而利用 rxjs 提供的大量操作符,来改变最终值;从而获得在应用层面最优雅的编码风格。

当我们说到优雅使用HTTP这件事时,易测试是一个非常重要,因此,我建议将HTTP从组件类中剥离并将所有请求放到 Service 当中。当对某个组件编写测试代码时,如果受到HTTP请求结果的限制会让测试更困难。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。


推荐阅读
  • 本文介绍了在开发Android新闻App时,搭建本地服务器的步骤。通过使用XAMPP软件,可以一键式搭建起开发环境,包括Apache、MySQL、PHP、PERL。在本地服务器上新建数据库和表,并设置相应的属性。最后,给出了创建new表的SQL语句。这个教程适合初学者参考。 ... [详细]
  • 使用在线工具jsonschema2pojo根据json生成java对象
    本文介绍了使用在线工具jsonschema2pojo根据json生成java对象的方法。通过该工具,用户只需将json字符串复制到输入框中,即可自动将其转换成java对象。该工具还能解析列表式的json数据,并将嵌套在内层的对象也解析出来。本文以请求github的api为例,展示了使用该工具的步骤和效果。 ... [详细]
  • 使用正则表达式爬取36Kr网站首页新闻的操作步骤和代码示例
    本文介绍了使用正则表达式来爬取36Kr网站首页所有新闻的操作步骤和代码示例。通过访问网站、查找关键词、编写代码等步骤,可以获取到网站首页的新闻数据。代码示例使用Python编写,并使用正则表达式来提取所需的数据。详细的操作步骤和代码示例可以参考本文内容。 ... [详细]
  • 阿里Treebased Deep Match(TDM) 学习笔记及技术发展回顾
    本文介绍了阿里Treebased Deep Match(TDM)的学习笔记,同时回顾了工业界技术发展的几代演进。从基于统计的启发式规则方法到基于内积模型的向量检索方法,再到引入复杂深度学习模型的下一代匹配技术。文章详细解释了基于统计的启发式规则方法和基于内积模型的向量检索方法的原理和应用,并介绍了TDM的背景和优势。最后,文章提到了向量距离和基于向量聚类的索引结构对于加速匹配效率的作用。本文对于理解TDM的学习过程和了解匹配技术的发展具有重要意义。 ... [详细]
  • Lodop中特殊符号打印设计和预览样式不同的问题解析
    本文主要解析了在Lodop中使用特殊符号打印设计和预览样式不同的问题。由于调用的本机ie引擎版本可能不同,导致在不同浏览器下样式解析不同。同时,未指定文字字体和样式设置也会导致打印设计和预览的差异。文章提出了通过指定具体字体和样式来解决问题的方法,并强调了以打印预览和虚拟打印机测试为准。 ... [详细]
  • Final关键字的含义及用法详解
    本文详细介绍了Java中final关键字的含义和用法。final关键字可以修饰非抽象类、非抽象类成员方法和变量。final类不能被继承,final类中的方法默认是final的。final方法不能被子类的方法覆盖,但可以被继承。final成员变量表示常量,只能被赋值一次,赋值后值不再改变。文章还讨论了final类和final方法的应用场景,以及使用final方法的两个原因:锁定方法防止修改和提高执行效率。 ... [详细]
  • 本文介绍了求解gcdexgcd斐蜀定理的迭代法和递归法,并解释了exgcd的概念和应用。exgcd是指对于不完全为0的非负整数a和b,gcd(a,b)表示a和b的最大公约数,必然存在整数对x和y,使得gcd(a,b)=ax+by。此外,本文还给出了相应的代码示例。 ... [详细]
  • EPICS Archiver Appliance存储waveform记录的尝试及资源需求分析
    本文介绍了EPICS Archiver Appliance存储waveform记录的尝试过程,并分析了其所需的资源容量。通过解决错误提示和调整内存大小,成功存储了波形数据。然后,讨论了储存环逐束团信号的意义,以及通过记录多圈的束团信号进行参数分析的可能性。波形数据的存储需求巨大,每天需要近250G,一年需要90T。然而,储存环逐束团信号具有重要意义,可以揭示出每个束团的纵向振荡频率和模式。 ... [详细]
  • VScode格式化文档换行或不换行的设置方法
    本文介绍了在VScode中设置格式化文档换行或不换行的方法,包括使用插件和修改settings.json文件的内容。详细步骤为:找到settings.json文件,将其中的代码替换为指定的代码。 ... [详细]
  • 电销机器人作为一种人工智能技术载体,可以帮助企业提升电销效率并节省人工成本。然而,电销机器人市场缺乏统一的市场准入标准,产品品质良莠不齐。创业者在代理或购买电销机器人时应注意谨防用录音冒充真人语音通话以及宣传技术与实际效果不符的情况。选择电销机器人时需要考察公司资质和产品品质,尤其要关注语音识别率。 ... [详细]
  • 这是原文链接:sendingformdata许多情况下,我们使用表单发送数据到服务器。服务器处理数据并返回响应给用户。这看起来很简单,但是 ... [详细]
  • 如何去除Win7快捷方式的箭头
    本文介绍了如何去除Win7快捷方式的箭头的方法,通过生成一个透明的ico图标并将其命名为Empty.ico,将图标复制到windows目录下,并导入注册表,即可去除箭头。这样做可以改善默认快捷方式的外观,提升桌面整洁度。 ... [详细]
  • Windows下配置PHP5.6的方法及注意事项
    本文介绍了在Windows系统下配置PHP5.6的步骤及注意事项,包括下载PHP5.6、解压并配置IIS、添加模块映射、测试等。同时提供了一些常见问题的解决方法,如下载缺失的msvcr110.dll文件等。通过本文的指导,读者可以轻松地在Windows系统下配置PHP5.6,并解决一些常见的配置问题。 ... [详细]
  • 延迟注入工具(python)的SQL脚本
    本文介绍了一个延迟注入工具(python)的SQL脚本,包括使用urllib2、time、socket、threading、requests等模块实现延迟注入的方法。该工具可以通过构造特定的URL来进行注入测试,并通过延迟时间来判断注入是否成功。 ... [详细]
  • Spring常用注解(绝对经典),全靠这份Java知识点PDF大全
    本文介绍了Spring常用注解和注入bean的注解,包括@Bean、@Autowired、@Inject等,同时提供了一个Java知识点PDF大全的资源链接。其中详细介绍了ColorFactoryBean的使用,以及@Autowired和@Inject的区别和用法。此外,还提到了@Required属性的配置和使用。 ... [详细]
author-avatar
leee
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有