├── .gitignore
├── LICENSE
├── README-zh_CN.md
├── README.md
├── docs
├── .vuepress
│ └── config.js
├── README.md
├── dist
│ ├── 404.html
│ ├── assets
│ │ ├── css
│ │ │ └── 0.styles.6467ecbe.css
│ │ ├── img
│ │ │ └── search.83621669.svg
│ │ └── js
│ │ │ ├── 2.3badc248.js
│ │ │ ├── 3.039c8eb4.js
│ │ │ ├── 4.d46c551b.js
│ │ │ ├── 5.923b9e8f.js
│ │ │ ├── 6.399ce124.js
│ │ │ ├── 7.736b3428.js
│ │ │ ├── 8.35838317.js
│ │ │ └── app.11798775.js
│ ├── guide
│ │ ├── advanced.html
│ │ ├── example.html
│ │ ├── getting-started.html
│ │ └── index.html
│ └── index.html
└── guide
│ ├── README.md
│ ├── advanced.md
│ ├── example.md
│ └── getting-started.md
├── lib
├── index.ts
├── public_api.ts
└── src
│ ├── backend.ts
│ ├── client.ts
│ ├── cookie.ts
│ ├── handler.ts
│ ├── headers.ts
│ ├── interceptor.ts
│ ├── jsonp.ts
│ ├── params.ts
│ ├── request.ts
│ ├── response.ts
│ ├── rjax.ts
│ ├── xhr.ts
│ └── xsrf.ts
├── package.json
├── tsconfig.json
├── tslint.json
└── yarn.lock
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 | /dist/
14 |
15 | # misc
16 | .DS_Store
17 | .env.local
18 | .env.development.local
19 | .env.test.local
20 | .env.production.local
21 |
22 | npm-debug.log*
23 | yarn-debug.log*
24 | yarn-error.log*
25 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License
2 |
3 | Copyright (c) 2024 ppjjzz
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README-zh_CN.md:
--------------------------------------------------------------------------------
1 | # rjax
2 |
3 | [](/LICENSE)
4 |
5 | [English](./README.md) | 简体中文
6 |
7 | ## ✨ 特性
8 |
9 | - 移植优秀的@angular/common/http模块。
10 | - 基于rxjs的响应式请求库。
11 | - 使用 TypeScript 构建,提供完整的类型定义文件。
12 | - 功能强大又简单易用。
13 |
14 | ## 📦 安装
15 |
16 | ```bash
17 | yarn add rjax # 或者:npm install rjax --save
18 | ```
19 |
20 | ## 🔨 示例
21 |
22 | ```js
23 | import { Rjax } from 'rjax';
24 | const rjax = new Rjax();
25 | rjax.get(`/user/12345`).subscribe(response => {
26 | // 请求成功回调
27 | console.log(response);
28 | }, error => {
29 | // 请求失败回调
30 | console.log(error);
31 | });
32 | ```
33 |
34 | ## 文档
35 | API 文档及示例 [链接](https://ppjjzz.github.io/rjax/dist/index.html)
36 |
37 | ## 作者
38 |
39 | **rjax** © [ppjjzz](https://github.com/ppjjzz), Released under the [MIT](./LICENSE) License.
40 | Authored and maintained by ppjjzz.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # rjax
2 |
3 | [](/LICENSE)
4 |
5 | English | [简体中文](./README-zh_CN.md)
6 |
7 | ## ✨ Features
8 |
9 | - Porting the excellent @angular/common/http module.
10 | - Responsive ajax library based on rxjs.
11 | - Written in TypeScript with predictable static types.
12 | - Powerful and easy to use.
13 |
14 | ## 📦 Install
15 |
16 | ```bash
17 | yarn add rjax # or: npm install rjax --save
18 | ```
19 |
20 | ## 🔨 Usage
21 |
22 | ```js
23 | import { Rjax } from 'rjax';
24 | const rjax = new Rjax();
25 | rjax.get(`/user/12345`).subscribe(response => {
26 | // Succeed Callback
27 | console.log(response);
28 | }, error => {
29 | // Error Callback
30 | console.log(error);
31 | });
32 | ```
33 |
34 | ## Docs
35 | API document and example [link](https://ppjjzz.github.io/rjax/dist/index.html)
36 |
37 | ## Author
38 |
39 | **rjax** © [ppjjzz](https://github.com/ppjjzz), Released under the [MIT](./LICENSE) License.
40 | Authored and maintained by ppjjzz.
--------------------------------------------------------------------------------
/docs/.vuepress/config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | title: 'Rjax',
3 | description: 'base on rxjs awesome ajax library',
4 | base: '/rjax/dist/',
5 | dest: './docs/dist',
6 | themeConfig: {
7 | repo: 'ppjjzz/rjax',
8 | nav: [
9 | { text: '指南', link: '/guide/' },
10 | ],
11 | sidebar: {
12 | '/guide/': [{
13 | title: '指南',
14 | collapsable: false,
15 | children: [
16 | '',
17 | 'getting-started',
18 | 'advanced',
19 | 'example'
20 | ]
21 | }]
22 | }
23 | },
24 |
25 | }
--------------------------------------------------------------------------------
/docs/README.md:
--------------------------------------------------------------------------------
1 | ---
2 | home: true
3 | heroText: Rjax
4 | tagline: 基于Rxjs的响应式AJAX请求库
5 | actionText: 快速上手 →
6 | actionLink: /guide/
7 | features:
8 | - title: 功能强大
9 | details: 移植于@angular/common/http模块,可享有几乎同样的HttpClient功能。
10 | - title: Rxjs驱动
11 | details: 享受 Rxjs的响应式和函数式编程体验。
12 | - title: 使用 TypeScript 构建
13 | details: 提供完整的类型定义文件和IDE完美提示。
14 | footer: MIT Licensed | Copyright © 2024 ppjjzz
15 | ---
--------------------------------------------------------------------------------
/docs/dist/404.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Rjax
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/docs/dist/assets/css/0.styles.6467ecbe.css:
--------------------------------------------------------------------------------
1 | .home{padding:3.6rem 2rem 0;max-width:960px;margin:0 auto}.home .hero{text-align:center}.home .hero img{max-height:280px;display:block;margin:3rem auto 1.5rem}.home .hero h1{font-size:3rem}.home .hero .action,.home .hero .description,.home .hero h1{margin:1.8rem auto}.home .hero .description{max-width:35rem;font-size:1.6rem;line-height:1.3;color:#6a8bad}.home .hero .action-button{display:inline-block;font-size:1.2rem;color:#fff;background-color:#3eaf7c;padding:.8rem 1.6rem;border-radius:4px;transition:background-color .1s ease;box-sizing:border-box;border-bottom:1px solid #389d70}.home .hero .action-button:hover{background-color:#4abf8a}.home .features{border-top:1px solid #eaecef;padding:1.2rem 0;margin-top:2.5rem;display:flex;flex-wrap:wrap;align-items:flex-start;align-content:stretch;justify-content:space-between}.home .feature{flex-grow:1;flex-basis:30%;max-width:30%}.home .feature h2{font-size:1.4rem;font-weight:500;border-bottom:none;padding-bottom:0;color:#3a5169}.home .feature p{color:#4e6e8e}.home .footer{padding:2.5rem;border-top:1px solid #eaecef;text-align:center;color:#4e6e8e}@media (max-width:719px){.home .features{flex-direction:column}.home .feature{max-width:100%;padding:0 2.5rem}}@media (max-width:419px){.home{padding-left:1.5rem;padding-right:1.5rem}.home .hero img{max-height:210px;margin:2rem auto 1.2rem}.home .hero h1{font-size:2rem}.home .hero .action,.home .hero .description,.home .hero h1{margin:1.2rem auto}.home .hero .description{font-size:1.2rem}.home .hero .action-button{font-size:1rem;padding:.6rem 1.2rem}.home .feature h2{font-size:1.25rem}}.badge[data-v-099ab69c]{display:inline-block;font-size:14px;height:18px;line-height:18px;border-radius:3px;padding:0 6px;color:#fff;margin-right:5px;background-color:#42b983}.badge.middle[data-v-099ab69c]{vertical-align:middle}.badge.top[data-v-099ab69c]{vertical-align:top}.badge.green[data-v-099ab69c],.badge.tip[data-v-099ab69c]{background-color:#42b983}.badge.error[data-v-099ab69c]{background-color:#da5961}.badge.warn[data-v-099ab69c],.badge.warning[data-v-099ab69c],.badge.yellow[data-v-099ab69c]{background-color:#e7c000}.search-box{display:inline-block;position:relative;margin-right:1rem}.search-box input{cursor:text;width:10rem;color:#4e6e8e;display:inline-block;border:1px solid #cfd4db;border-radius:2rem;font-size:.9rem;line-height:2rem;padding:0 .5rem 0 2rem;outline:none;transition:all .2s ease;background:#fff url(/rjax/dist/assets/img/search.83621669.svg) .6rem .5rem no-repeat;background-size:1rem}.search-box input:focus{cursor:auto;border-color:#3eaf7c}.search-box .suggestions{background:#fff;width:20rem;position:absolute;top:1.5rem;border:1px solid #cfd4db;border-radius:6px;padding:.4rem;list-style-type:none}.search-box .suggestions.align-right{right:0}.search-box .suggestion{line-height:1.4;padding:.4rem .6rem;border-radius:4px;cursor:pointer}.search-box .suggestion a{white-space:normal;color:#5d82a6}.search-box .suggestion a .page-title{font-weight:600}.search-box .suggestion a .header{font-size:.9em;margin-left:.25em}.search-box .suggestion.focused{background-color:#f3f4f5}.search-box .suggestion.focused a{color:#3eaf7c}@media (max-width:959px){.search-box input{cursor:pointer;width:0;border-color:transparent;position:relative}.search-box input:focus{cursor:text;left:0;width:10rem}}@media (max-width:959px) and (min-width:719px){.search-box .suggestions{left:0}}@media (max-width:719px){.search-box{margin-right:0}.search-box input{left:1rem}.search-box .suggestions{right:0}}@media (max-width:419px){.search-box .suggestions{width:calc(100vw - 4rem)}.search-box input:focus{width:8rem}}.dropdown-enter,.dropdown-leave-to{height:0!important}.dropdown-wrapper{cursor:pointer}.dropdown-wrapper .dropdown-title{display:block}.dropdown-wrapper .dropdown-title:hover{border-color:transparent}.dropdown-wrapper .dropdown-title .arrow{vertical-align:middle;margin-top:-1px;margin-left:.4rem}.dropdown-wrapper .nav-dropdown .dropdown-item{color:inherit;line-height:1.7rem}.dropdown-wrapper .nav-dropdown .dropdown-item h4{margin:.45rem 0 0;border-top:1px solid #eee;padding:.45rem 1.5rem 0 1.25rem}.dropdown-wrapper .nav-dropdown .dropdown-item .dropdown-subitem-wrapper{padding:0;list-style:none}.dropdown-wrapper .nav-dropdown .dropdown-item .dropdown-subitem-wrapper .dropdown-subitem{font-size:.9em}.dropdown-wrapper .nav-dropdown .dropdown-item a{display:block;line-height:1.7rem;position:relative;border-bottom:none;font-weight:400;margin-bottom:0;padding:0 1.5rem 0 1.25rem}.dropdown-wrapper .nav-dropdown .dropdown-item a.router-link-active,.dropdown-wrapper .nav-dropdown .dropdown-item a:hover{color:#3eaf7c}.dropdown-wrapper .nav-dropdown .dropdown-item a.router-link-active:after{content:"";width:0;height:0;border-left:5px solid #3eaf7c;border-top:3px solid transparent;border-bottom:3px solid transparent;position:absolute;top:calc(50% - 2px);left:9px}.dropdown-wrapper .nav-dropdown .dropdown-item:first-child h4{margin-top:0;padding-top:0;border-top:0}@media (max-width:719px){.dropdown-wrapper.open .dropdown-title{margin-bottom:.5rem}.dropdown-wrapper .nav-dropdown{transition:height .1s ease-out;overflow:hidden}.dropdown-wrapper .nav-dropdown .dropdown-item h4{border-top:0;margin-top:0;padding-top:0}.dropdown-wrapper .nav-dropdown .dropdown-item>a,.dropdown-wrapper .nav-dropdown .dropdown-item h4{font-size:15px;line-height:2rem}.dropdown-wrapper .nav-dropdown .dropdown-item .dropdown-subitem{font-size:14px;padding-left:1rem}}@media (min-width:719px){.dropdown-wrapper{height:1.8rem}.dropdown-wrapper:hover .nav-dropdown{display:block!important}.dropdown-wrapper .dropdown-title .arrow{border-left:4px solid transparent;border-right:4px solid transparent;border-top:6px solid #ccc;border-bottom:0}.dropdown-wrapper .nav-dropdown{display:none;height:auto!important;box-sizing:border-box;max-height:calc(100vh - 2.7rem);overflow-y:auto;position:absolute;top:100%;right:0;background-color:#fff;padding:.6rem 0;border:1px solid #ddd;border-bottom-color:#ccc;text-align:left;border-radius:.25rem;white-space:nowrap;margin:0}}.nav-links{display:inline-block}.nav-links a{line-height:1.4rem;color:inherit}.nav-links a.router-link-active,.nav-links a:hover{color:#3eaf7c}.nav-links .nav-item{position:relative;display:inline-block;margin-left:1.5rem;line-height:2rem}.nav-links .nav-item:first-child{margin-left:0}.nav-links .repo-link{margin-left:1.5rem}@media (max-width:719px){.nav-links .nav-item,.nav-links .repo-link{margin-left:0}}@media (min-width:719px){.nav-links a.router-link-active,.nav-links a:hover{color:#2c3e50}.nav-item>a:not(.external).router-link-active,.nav-item>a:not(.external):hover{margin-bottom:-2px;border-bottom:2px solid #46bd87}}.navbar{padding:.7rem 1.5rem;line-height:2.2rem;position:relative}.navbar a,.navbar img,.navbar span{display:inline-block}.navbar .logo{height:2.2rem;min-width:2.2rem;margin-right:.8rem;vertical-align:top}.navbar .site-name{font-size:1.3rem;font-weight:600;color:#2c3e50;position:relative}.navbar .links{padding-left:1.5rem;box-sizing:border-box;background-color:#fff;white-space:nowrap;font-size:.9rem;position:absolute;right:1.5rem;top:.7rem;display:flex}.navbar .links .search-box{flex:0 0 auto;vertical-align:top}.navbar .links .nav-links{flex:1}@media (max-width:719px){.navbar{padding-left:4rem}.navbar .can-hide{display:none}.navbar .links{padding-left:1.5rem}}.page-edit,.page-nav{max-width:740px;margin:0 auto;padding:2rem 2.5rem}@media (max-width:959px){.page-edit,.page-nav{padding:2rem}}@media (max-width:419px){.page-edit,.page-nav{padding:1.5rem}}.page{padding-bottom:2rem}.page-edit{padding-top:1rem;padding-bottom:1rem;overflow:auto}.page-edit .edit-link{display:inline-block}.page-edit .edit-link a{color:#4e6e8e;margin-right:.25rem}.page-edit .last-updated{float:right;font-size:.9em}.page-edit .last-updated .prefix{font-weight:500;color:#4e6e8e}.page-edit .last-updated .time{font-weight:400;color:#aaa}.page-nav{padding-top:1rem;padding-bottom:0}.page-nav .inner{min-height:2rem;margin-top:0;border-top:1px solid #eaecef;padding-top:1rem;overflow:auto}.page-nav .next{float:right}@media (max-width:719px){.page-edit .edit-link{margin-bottom:.5rem}.page-edit .last-updated{font-size:.8em;float:none;text-align:left}}.sidebar-button{display:none;width:1.25rem;height:1.25rem;position:absolute;padding:.6rem;top:.6rem;left:1rem}.sidebar-button .icon{display:block;width:1.25rem;height:1.25rem}@media (max-width:719px){.sidebar-button{display:block}}.sidebar-group:not(.first){margin-top:1em}.sidebar-group .sidebar-group{padding-left:.5em}.sidebar-group:not(.collapsable) .sidebar-heading{cursor:auto;color:inherit}.sidebar-heading{color:#999;transition:color .15s ease;cursor:pointer;font-size:1.1em;font-weight:700;padding:0 1.5rem;margin-top:0;margin-bottom:.5rem}.sidebar-heading.open,.sidebar-heading:hover{color:inherit}.sidebar-heading .arrow{position:relative;top:-.12em;left:.5em}.sidebar-heading:.open .arrow{top:-.18em}.sidebar-group-items{transition:height .1s ease-out;overflow:hidden}.sidebar ul{padding:0;margin:0;list-style-type:none}.sidebar a{display:inline-block}.sidebar .nav-links{display:none;border-bottom:1px solid #eaecef;padding:.5rem 0 .75rem 0}.sidebar .nav-links a{font-weight:600}.sidebar .nav-links .nav-item,.sidebar .nav-links .repo-link{display:block;line-height:1.25rem;font-size:1.1em;padding:.5rem 0 .5rem 1.5rem}.sidebar .sidebar-links{padding:1.5rem 0}@media (max-width:719px){.sidebar .nav-links{display:block}.sidebar .nav-links .dropdown-wrapper .nav-dropdown .dropdown-item a.router-link-active:after{top:calc(1rem - 2px)}.sidebar .sidebar-links{padding:1rem 0}}.sw-update-popup{position:fixed;right:1em;bottom:1em;padding:1em;border:1px solid #3eaf7c;border-radius:3px;background:#fff;box-shadow:0 4px 16px rgba(0,0,0,.5);text-align:center}.sw-update-popup button{margin-top:.5em;padding:.25em 2em}.sw-update-popup-enter-active,.sw-update-popup-leave-active{transition:opacity .3s,transform .3s}.sw-update-popup-enter,.sw-update-popup-leave-to{opacity:0;transform:translateY(50%) scale(.5)}code[class*=language-],pre[class*=language-]{color:#ccc;background:none;font-family:Consolas,Monaco,Andale Mono,Ubuntu Mono,monospace;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;word-wrap:normal;line-height:1.5;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-hyphens:none;-ms-hyphens:none;hyphens:none}pre[class*=language-]{padding:1em;margin:.5em 0;overflow:auto}:not(pre)>code[class*=language-],pre[class*=language-]{background:#2d2d2d}:not(pre)>code[class*=language-]{padding:.1em;border-radius:.3em;white-space:normal}.token.block-comment,.token.cdata,.token.comment,.token.doctype,.token.prolog{color:#999}.token.punctuation{color:#ccc}.token.attr-name,.token.deleted,.token.namespace,.token.tag{color:#e2777a}.token.function-name{color:#6196cc}.token.boolean,.token.function,.token.number{color:#f08d49}.token.class-name,.token.constant,.token.property,.token.symbol{color:#f8c555}.token.atrule,.token.builtin,.token.important,.token.keyword,.token.selector{color:#cc99cd}.token.attr-value,.token.char,.token.regex,.token.string,.token.variable{color:#7ec699}.token.entity,.token.operator,.token.url{color:#67cdcc}.token.bold,.token.important{font-weight:700}.token.italic{font-style:italic}.token.entity{cursor:help}.token.inserted{color:green}#nprogress{pointer-events:none}#nprogress .bar{background:#3eaf7c;position:fixed;z-index:1031;top:0;left:0;width:100%;height:2px}#nprogress .peg{display:block;position:absolute;right:0;width:100px;height:100%;box-shadow:0 0 10px #3eaf7c,0 0 5px #3eaf7c;opacity:1;transform:rotate(3deg) translateY(-4px)}#nprogress .spinner{display:block;position:fixed;z-index:1031;top:15px;right:15px}#nprogress .spinner-icon{width:18px;height:18px;box-sizing:border-box;border:2px solid transparent;border-top-color:#3eaf7c;border-left-color:#3eaf7c;border-radius:50%;animation:nprogress-spinner .4s linear infinite}.nprogress-custom-parent{overflow:hidden;position:relative}.nprogress-custom-parent #nprogress .bar,.nprogress-custom-parent #nprogress .spinner{position:absolute}@keyframes nprogress-spinner{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}.content code{color:#476582;padding:.25rem .5rem;margin:0;font-size:.85em;background-color:rgba(27,31,35,.05);border-radius:3px}.content pre,.content pre[class*=language-]{line-height:1.4;padding:1.25rem 1.5rem;margin:.85rem 0;background-color:#282c34;border-radius:6px;overflow:auto}.content pre[class*=language-] code,.content pre code{color:#fff;padding:0;background-color:transparent;border-radius:0}div[class*=language-]{position:relative;background-color:#282c34;border-radius:6px}div[class*=language-] .highlight-lines{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;padding-top:1.3rem;position:absolute;top:0;left:0;width:100%;line-height:1.4}div[class*=language-] .highlight-lines .highlighted{background-color:rgba(0,0,0,.66)}div[class*=language-] pre,div[class*=language-] pre[class*=language-]{background:transparent;position:relative;z-index:1}div[class*=language-]:before{position:absolute;z-index:3;top:.8em;right:1em;font-size:.75rem;color:hsla(0,0%,100%,.4)}div[class*=language-]:not(.line-numbers-mode) .line-numbers-wrapper{display:none}div[class*=language-].line-numbers-mode .highlight-lines .highlighted{position:relative}div[class*=language-].line-numbers-mode .highlight-lines .highlighted:before{content:" ";position:absolute;z-index:3;left:0;top:0;display:block;width:3.5rem;height:100%;background-color:rgba(0,0,0,.66)}div[class*=language-].line-numbers-mode pre{padding-left:4.5rem;vertical-align:middle}div[class*=language-].line-numbers-mode .line-numbers-wrapper{position:absolute;top:0;width:3.5rem;text-align:center;color:hsla(0,0%,100%,.3);padding:1.25rem 0;line-height:1.4}div[class*=language-].line-numbers-mode .line-numbers-wrapper .line-number,div[class*=language-].line-numbers-mode .line-numbers-wrapper br{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}div[class*=language-].line-numbers-mode .line-numbers-wrapper .line-number{position:relative;z-index:4;font-size:.85em}div[class*=language-].line-numbers-mode:after{content:"";position:absolute;z-index:2;top:0;left:0;width:3.5rem;height:100%;border-radius:6px 0 0 6px;border-right:1px solid rgba(0,0,0,.66);background-color:#282c34}div[class~=language-js]:before{content:"js"}div[class~=language-ts]:before{content:"ts"}div[class~=language-html]:before{content:"html"}div[class~=language-md]:before{content:"md"}div[class~=language-vue]:before{content:"vue"}div[class~=language-css]:before{content:"css"}div[class~=language-sass]:before{content:"sass"}div[class~=language-scss]:before{content:"scss"}div[class~=language-less]:before{content:"less"}div[class~=language-stylus]:before{content:"stylus"}div[class~=language-go]:before{content:"go"}div[class~=language-java]:before{content:"java"}div[class~=language-c]:before{content:"c"}div[class~=language-sh]:before{content:"sh"}div[class~=language-yaml]:before{content:"yaml"}div[class~=language-py]:before{content:"py"}div[class~=language-javascript]:before{content:"js"}div[class~=language-typescript]:before{content:"ts"}div[class~=language-markup]:before{content:"html"}div[class~=language-markdown]:before{content:"md"}div[class~=language-json]:before{content:"json"}div[class~=language-ruby]:before{content:"rb"}div[class~=language-python]:before{content:"py"}div[class~=language-bash]:before{content:"sh"}.custom-block .custom-block-title{font-weight:600;margin-bottom:-.4rem}.custom-block.danger,.custom-block.tip,.custom-block.warning{padding:.1rem 1.5rem;border-left-width:.5rem;border-left-style:solid;margin:1rem 0}.custom-block.tip{background-color:#f3f5f7;border-color:#42b983}.custom-block.warning{background-color:rgba(255,229,100,.3);border-color:#e7c000;color:#6b5900}.custom-block.warning .custom-block-title{color:#b29400}.custom-block.warning a{color:#2c3e50}.custom-block.danger{background-color:#ffe6e6;border-color:#c00;color:#4d0000}.custom-block.danger .custom-block-title{color:#900}.custom-block.danger a{color:#2c3e50}.arrow{display:inline-block;width:0;height:0}.arrow.up{border-bottom:6px solid #ccc}.arrow.down,.arrow.up{border-left:4px solid transparent;border-right:4px solid transparent}.arrow.down{border-top:6px solid #ccc}.arrow.right{border-left:6px solid #ccc}.arrow.left,.arrow.right{border-top:4px solid transparent;border-bottom:4px solid transparent}.arrow.left{border-right:6px solid #ccc}.content:not(.custom){max-width:740px;margin:0 auto;padding:2rem 2.5rem}@media (max-width:959px){.content:not(.custom){padding:2rem}}@media (max-width:419px){.content:not(.custom){padding:1.5rem}}.table-of-contents .badge{vertical-align:middle}body,html{padding:0;margin:0}body{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;font-size:16px;color:#2c3e50}.page{padding-left:20rem}.navbar{z-index:20;right:0;height:3.6rem;background-color:#fff;box-sizing:border-box;border-bottom:1px solid #eaecef}.navbar,.sidebar-mask{position:fixed;top:0;left:0}.sidebar-mask{z-index:9;width:100vw;height:100vh;display:none}.sidebar{font-size:15px;background-color:#fff;width:20rem;position:fixed;z-index:10;margin:0;top:3.6rem;left:0;bottom:0;box-sizing:border-box;border-right:1px solid #eaecef;overflow-y:auto}.content:not(.custom)>:first-child{margin-top:3.6rem}.content:not(.custom) a:hover{text-decoration:underline}.content:not(.custom) p.demo{padding:1rem 1.5rem;border:1px solid #ddd;border-radius:4px}.content:not(.custom) img{max-width:100%}.content.custom{padding:0;margin:0}.content.custom img{max-width:100%}a{font-weight:500;text-decoration:none}a,p a code{color:#3eaf7c}p a code{font-weight:400}kbd{background:#eee;border:.15rem solid #ddd;border-bottom:.25rem solid #ddd;border-radius:.15rem;padding:0 .15em}blockquote{font-size:1.2rem;color:#999;border-left:.25rem solid #dfe2e5;margin-left:0;padding-left:1rem}ol,ul{padding-left:1.2em}strong{font-weight:600}h1,h2,h3,h4,h5,h6{font-weight:600;line-height:1.25}.content:not(.custom)>h1,.content:not(.custom)>h2,.content:not(.custom)>h3,.content:not(.custom)>h4,.content:not(.custom)>h5,.content:not(.custom)>h6{margin-top:-3.1rem;padding-top:4.6rem;margin-bottom:0}.content:not(.custom)>h1:first-child,.content:not(.custom)>h2:first-child,.content:not(.custom)>h3:first-child,.content:not(.custom)>h4:first-child,.content:not(.custom)>h5:first-child,.content:not(.custom)>h6:first-child{margin-top:-1.5rem;margin-bottom:1rem}.content:not(.custom)>h1:first-child+.custom-block,.content:not(.custom)>h1:first-child+p,.content:not(.custom)>h1:first-child+pre,.content:not(.custom)>h2:first-child+.custom-block,.content:not(.custom)>h2:first-child+p,.content:not(.custom)>h2:first-child+pre,.content:not(.custom)>h3:first-child+.custom-block,.content:not(.custom)>h3:first-child+p,.content:not(.custom)>h3:first-child+pre,.content:not(.custom)>h4:first-child+.custom-block,.content:not(.custom)>h4:first-child+p,.content:not(.custom)>h4:first-child+pre,.content:not(.custom)>h5:first-child+.custom-block,.content:not(.custom)>h5:first-child+p,.content:not(.custom)>h5:first-child+pre,.content:not(.custom)>h6:first-child+.custom-block,.content:not(.custom)>h6:first-child+p,.content:not(.custom)>h6:first-child+pre{margin-top:2rem}h1:hover .header-anchor,h2:hover .header-anchor,h3:hover .header-anchor,h4:hover .header-anchor,h5:hover .header-anchor,h6:hover .header-anchor{opacity:1}h1{font-size:2.2rem}h2{font-size:1.65rem;padding-bottom:.3rem;border-bottom:1px solid #eaecef}h3{font-size:1.35rem}a.header-anchor{font-size:.85em;float:left;margin-left:-.87em;padding-right:.23em;margin-top:.125em;opacity:0}a.header-anchor:hover{text-decoration:none}.line-number,code,kbd{font-family:source-code-pro,Menlo,Monaco,Consolas,Courier New,monospace}ol,p,ul{line-height:1.7}hr{border:0;border-top:1px solid #eaecef}table{border-collapse:collapse;margin:1rem 0;display:block;overflow-x:auto}tr{border-top:1px solid #dfe2e5}tr:nth-child(2n){background-color:#f6f8fa}td,th{border:1px solid #dfe2e5;padding:.6em 1em}.custom-layout{padding-top:3.6rem}.theme-container.sidebar-open .sidebar-mask{display:block}.theme-container.no-navbar .content:not(.custom)>h1,.theme-container.no-navbar h2,.theme-container.no-navbar h3,.theme-container.no-navbar h4,.theme-container.no-navbar h5,.theme-container.no-navbar h6{margin-top:1.5rem;padding-top:0}.theme-container.no-navbar .sidebar{top:0}.theme-container.no-navbar .custom-layout{padding-top:0}@media (min-width:720px){.theme-container.no-sidebar .sidebar{display:none}.theme-container.no-sidebar .page{padding-left:0}}@media (max-width:959px){.sidebar{font-size:15px;width:16.4rem}.page{padding-left:16.4rem}}@media (max-width:719px){.sidebar{top:0;padding-top:3.6rem;transform:translateX(-100%);transition:transform .2s ease}.page{padding-left:0}.theme-container.sidebar-open .sidebar{transform:translateX(0)}.theme-container.no-navbar .sidebar{padding-top:0}}@media (max-width:419px){h1{font-size:1.9rem}.content div[class*=language-]{margin:.85rem -1.5rem;border-radius:0}}.icon.outbound{color:#aaa;display:inline-block}.sidebar .sidebar-sub-headers{padding-left:1rem;font-size:.95em}a.sidebar-link{font-weight:400;display:inline-block;color:#2c3e50;border-left:.25rem solid transparent;padding:.35rem 1rem .35rem 1.25rem;line-height:1.4;width:100%;box-sizing:border-box}a.sidebar-link:hover{color:#3eaf7c}a.sidebar-link.active{font-weight:600;color:#3eaf7c;border-left-color:#3eaf7c}.sidebar-group a.sidebar-link{padding-left:2rem}.sidebar-sub-headers a.sidebar-link{padding-top:.25rem;padding-bottom:.25rem;border-left:none}.sidebar-sub-headers a.sidebar-link.active{font-weight:500}
--------------------------------------------------------------------------------
/docs/dist/assets/img/search.83621669.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/docs/dist/assets/js/2.3badc248.js:
--------------------------------------------------------------------------------
1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[2],{164:function(t,e,n){},165:function(t,e,n){"use strict";var a=n(164);n.n(a).a},167:function(t,e,n){"use strict";n.r(e);var a={functional:!0,props:{type:{type:String,default:"tip"},text:String,vertical:{type:String,default:"top"}},render:function(t,e){var n=e.props,a=e.slots;return t("span",{class:["badge",n.type,n.vertical]},n.text||a().default)}},i=(n(165),n(0)),o=Object(i.a)(a,void 0,void 0,!1,null,"099ab69c",null);o.options.__file="Badge.vue";e.default=o.exports}}]);
--------------------------------------------------------------------------------
/docs/dist/assets/js/3.039c8eb4.js:
--------------------------------------------------------------------------------
1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[3],{169:function(t,n,e){"use strict";e.r(n);var s=e(0),i=Object(s.a)({},function(){var t=this.$createElement;return(this._self._c||t)("div",{staticClass:"content"})},[],!1,null,null,null);i.options.__file="README.md";n.default=i.exports}}]);
--------------------------------------------------------------------------------
/docs/dist/assets/js/4.d46c551b.js:
--------------------------------------------------------------------------------
1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[4],{170:function(a,t,r){"use strict";r.r(t);var e=r(0),i=Object(e.a)({},function(){this.$createElement;this._self._c;return this._m(0)},[function(){var a=this,t=a.$createElement,r=a._self._c||t;return r("div",{staticClass:"content"},[r("h1",{attrs:{id:"介绍"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#介绍","aria-hidden":"true"}},[a._v("#")]),a._v(" 介绍")]),a._v(" "),r("h2",{attrs:{id:"特性"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#特性","aria-hidden":"true"}},[a._v("#")]),a._v(" 特性")]),a._v(" "),r("ul",[r("li",[a._v("移植优秀的@angular/common/http模块。")]),a._v(" "),r("li",[a._v("兼容非angular项目")]),a._v(" "),r("li",[a._v("基于rxjs的响应式请求库。")]),a._v(" "),r("li",[a._v("使用 TypeScript 构建,提供完整的类型定义文件。")]),a._v(" "),r("li",[a._v("功能强大又简单易用。")]),a._v(" "),r("li",[a._v("\b可自定义多个请求响应的拦截器")])]),a._v(" "),r("h2",{attrs:{id:"为什么不是"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#为什么不是","aria-hidden":"true"}},[a._v("#")]),a._v(" 为什么不是...?")]),a._v(" "),r("h3",{attrs:{id:"fetch"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#fetch","aria-hidden":"true"}},[a._v("#")]),a._v(" fetch")]),a._v(" "),r("ul",[r("li",[a._v("fetch浏览器原生内置API")]),a._v(" "),r("li",[a._v("优点:免引入第三方依赖,基于Promise")]),a._v(" "),r("li",[a._v("缺点:API过于简单,没有提供拦截器功能,需要手动序列化和反序列化,一般需要经过封装后才可以使用,兼容性问题")])]),a._v(" "),r("h3",{attrs:{id:"axios"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#axios","aria-hidden":"true"}},[a._v("#")]),a._v(" axios")]),a._v(" "),r("ul",[r("li",[a._v("目前最流行的ajax请求库之一")]),a._v(" "),r("li",[a._v("优点:\b可自定义的配置多,提供拦截器,兼容性好,体积小,基于Promise")]),a._v(" "),r("li",[a._v("缺点:cancel的API比较难用,无法应对复杂的请求场景,不支持jsonp")])]),a._v(" "),r("h3",{attrs:{id:"jquery"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#jquery","aria-hidden":"true"}},[a._v("#")]),a._v(" JQuery")]),a._v(" "),r("ul",[r("li",[a._v("老牌的前端类库")]),a._v(" "),r("li",[a._v("优点:兼容性好")]),a._v(" "),r("li",[a._v("缺点:体积大,没有提供拦截器功能,请求配置优点复杂")])])])}],!1,null,null,null);i.options.__file="README.md";t.default=i.exports}}]);
--------------------------------------------------------------------------------
/docs/dist/assets/js/6.399ce124.js:
--------------------------------------------------------------------------------
1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[6],{171:function(t,s,a){"use strict";a.r(s);var n=a(0),e=Object(n.a)({},function(){this.$createElement;this._self._c;return this._m(0)},[function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"content"},[a("h1",{attrs:{id:"使用示例"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#使用示例","aria-hidden":"true"}},[t._v("#")]),t._v(" 使用示例")]),t._v(" "),a("h2",{attrs:{id:"并发请求"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#并发请求","aria-hidden":"true"}},[t._v("#")]),t._v(" 并发请求")]),t._v(" "),a("p",[t._v("\b并发多个请求,等待所有请求都结束后才执行回调相当于 "),a("strong",[t._v("Promise.all()")])]),t._v(" "),a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" Rjax "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("from")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'rjax'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" combineLatest "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("from")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'rxjs'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 创建实例")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("const")]),t._v(" rjax "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("new")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Rjax")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("const")]),t._v(" p1$ "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" rjax"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("get")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token template-string"}},[a("span",{pre:!0,attrs:{class:"token string"}},[t._v("`/user/12345`")])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("const")]),t._v(" p2$ "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" rjax"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("get")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token template-string"}},[a("span",{pre:!0,attrs:{class:"token string"}},[t._v("`/user/123456`")])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("combineLatest")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("p1$"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" p2$"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("subscribe")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("res1"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" res2"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=>")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 请求成功回调")]),t._v("\n console"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("log")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("res1"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// p1$ 的请求结果")]),t._v("\n console"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("log")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("res2"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// p2$ 的请求结果")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" error "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=>")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 请求失败回调")]),t._v("\n console"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("log")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("error"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])]),a("h2",{attrs:{id:"多数据源合并"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#多数据源合并","aria-hidden":"true"}},[t._v("#")]),t._v(" \b多数据源合并")]),t._v(" "),a("p",[t._v("例如某个图表的数据来源于websocket和ajax手动请求,返回格式是一样的,可以将这两个数据流进行合并创建新的\bObservable,组件只要订阅合并后的数据流而不关心数据的来源")]),t._v(" "),a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" Rjax "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("from")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'rjax'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" merge "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("from")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'rxjs'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" webSocket "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("from")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'rxjs/webSocket'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("const")]),t._v(" rjax "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("new")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Rjax")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("const")]),t._v(" ws$ "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("webSocket")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'wss://echo.websocket.org'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// websocket")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("const")]),t._v(" ajax$ "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" rjax"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("get")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token template-string"}},[a("span",{pre:!0,attrs:{class:"token string"}},[t._v("`/user/12345`")])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// ajax")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 合并数据流")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("merge")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("ws$"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" ajax$"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("subscribe")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("res "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=>")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n console"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("log")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("res"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" err "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=>")]),t._v(" console"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("log")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'请求出错'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])])])}],!1,null,null,null);e.options.__file="example.md";s.default=e.exports}}]);
--------------------------------------------------------------------------------
/docs/dist/assets/js/8.35838317.js:
--------------------------------------------------------------------------------
1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[8],{166:function(n,w,o){}}]);
--------------------------------------------------------------------------------
/docs/dist/guide/example.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | 使用示例 | Rjax
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | 使用示例
并发请求
并发多个请求,等待所有请求都结束后才执行回调相当于 Promise.all()
多数据源合并
例如某个图表的数据来源于websocket和ajax手动请求,返回格式是一样的,可以将这两个数据流进行合并创建新的Observable,组件只要订阅合并后的数据流而不关心数据的来源
53 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/docs/dist/guide/getting-started.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | 快速上手 | Rjax
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
61 |
62 |
63 |
64 |
--------------------------------------------------------------------------------
/docs/dist/guide/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | 介绍 | Rjax
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | 介绍
特性
- 移植优秀的@angular/common/http模块。
- 兼容非angular项目
- 基于rxjs的响应式请求库。
- 使用 TypeScript 构建,提供完整的类型定义文件。
- 功能强大又简单易用。
- 可自定义多个请求响应的拦截器
为什么不是...?
fetch
- fetch浏览器原生内置API
- 优点:免引入第三方依赖,基于Promise
- 缺点:API过于简单,没有提供拦截器功能,需要手动序列化和反序列化,一般需要经过封装后才可以使用,兼容性问题
axios
- 目前最流行的ajax请求库之一
- 优点:可自定义的配置多,提供拦截器,兼容性好,体积小,基于Promise
- 缺点:cancel的API比较难用,无法应对复杂的请求场景,不支持jsonp
JQuery
- 老牌的前端类库
- 优点:兼容性好
- 缺点:体积大,没有提供拦截器功能,请求配置优点复杂
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/docs/dist/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Rjax
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/docs/guide/README.md:
--------------------------------------------------------------------------------
1 | # 介绍
2 |
3 | ## 特性
4 |
5 | - 移植优秀的@angular/common/http模块。
6 | - 兼容非angular项目
7 | - 基于rxjs的响应式请求库。
8 | - 使用 TypeScript 构建,提供完整的类型定义文件。
9 | - 功能强大又简单易用。
10 | - 可自定义多个请求响应的拦截器
11 |
12 | ## 为什么不是...?
13 |
14 | ### fetch
15 | - fetch浏览器原生内置API
16 | - 优点:免引入第三方依赖,基于Promise
17 | - 缺点:API过于简单,没有提供拦截器功能,需要手动序列化和反序列化,一般需要经过封装后才可以使用,兼容性问题
18 |
19 | ### axios
20 | - 目前最流行的ajax请求库之一
21 | - 优点:可自定义的配置多,提供拦截器,兼容性好,体积小,基于Promise
22 | - 缺点:cancel的API比较难用,无法应对复杂的请求场景,不支持jsonp
23 |
24 | ### JQuery
25 | - 老牌的前端类库
26 | - 优点:兼容性好
27 | - 缺点:体积大,没有提供拦截器功能,请求配置优点复杂
--------------------------------------------------------------------------------
/docs/guide/advanced.md:
--------------------------------------------------------------------------------
1 | # 进阶
2 |
3 | ## 拦截请求和响应
4 | 使用拦截机制,你可以声明一些拦截器,用它们监视和转换从应用发送到服务器的 HTTP 请求。 拦截器还可以用监视和转换从服务器返回到本应用的那些响应。 多个选择器会构成一个“请求/响应处理器”的双向链表。
5 |
6 | 拦截器可以用一种常规的、标准的方式对每一次 HTTP 的请求/响应任务执行从认证到记日志等很多种隐式任务。
7 |
8 | 如果没有拦截机制,那么开发人员将不得不对每次 HttpClient 调用显式实现这些任务。
9 |
10 | ### 编写拦截器
11 | 要实现拦截器,就要实现一个实现了 HttpInterceptor 接口中的 intercept() 方法的类。
12 |
13 | ```js
14 | import { HttpResponse } from 'rjax';
15 | import { Observable , throwError as _throw } from 'rxjs';
16 | import { tap, mergeMap, finalize, catchError } from 'rxjs/operators';
17 | import { Rjax } from 'rjax';
18 |
19 | class CustomInterceptor {
20 | intercept(req, next) {
21 | console.log('拦截请求');
22 | // 一定要用clone的方法进行拦截修改,为了保持请求的不可变性!!!!
23 | const newReq = req.clone({
24 | url: req.url.replace('http://', 'https://'), // 修改请求的url
25 | body: {...req.body, name: req.body.name.trim()} // 修改请求体
26 | headers: req.headers.set('Authorization', 'authToken'), // 添加请求头
27 | });
28 | return next.handle(newReq).pipe(
29 | tap(x => console.log('拦截响应', x)),
30 | , mergeMap(event => {
31 | // 这里可根据后台接口约定自行判断
32 | if (event instanceof HttpResponse && (event.status !== 200 || !event.body.success)) {
33 | return Observable.create(observer => observer.error(event));
34 | }
35 | return Observable.create(observer => observer.next(event));
36 | })
37 | , catchError(res => {
38 | switch (res.status) {
39 | case 401:
40 | // 拦截到401错误
41 | break;
42 | case 200:
43 | // 业务层级错误处理
44 | break;
45 | case 404:
46 |
47 | break;
48 | case 500:
49 |
50 | break;
51 | default:
52 |
53 | break;
54 | }
55 | return _throw(res); // 将错误信息抛给下个拦截器或者请求调用方
56 | }), finalize(() => {
57 | // 无论成功或者失败都会执行
58 | // 可以记录日志等等
59 | }
60 | )
61 | );
62 | }
63 | }
64 |
65 | // 创建实例
66 | const rjax = new Rjax({
67 | interceptors: [new CustomInterceptor()] // 设置请求响应拦截器,可设置多组
68 | });
69 | ```
70 |
71 | ### 拦截器的顺序
72 | Rjax 会按照你提供它们的顺序应用这些拦截器。 如果你提供拦截器的顺序是先 A,再 B,再 C,那么请求阶段的执行顺序就是 A->B->C,而响应阶段的执行顺序则是 C->B->A。
73 |
74 | 以后你就再也不能修改这些顺序或移除某些拦截器了。 如果你需要动态启用或禁用某个拦截器,那就要在那个拦截器中自行实现这个功能。
75 |
76 | ## 请求的防抖(debounce)
77 | 当用户在搜索框中输入名字时进行远程检索
78 |
79 | 如果每次击键都发送一次请求就太昂贵了。 最好能等到用户停止输入时才发送请求。 使用 RxJS 的操作符就能轻易实现它,参见下面的代码片段:
80 | ```jsx
81 | import { Subject } from 'rxjs';
82 | import { debounceTime, distinctUntilChanged, switchMap } from 'rxjs/operators';
83 | import { Rjax } from 'rjax';
84 | export default class InputSearch extends Component {
85 | subject$ = new Subject();
86 | state = {
87 | data: []
88 | };
89 | rjax = new Rjax();
90 | componentDidMount() {
91 | this.subjectRef = this.subject.pipe(
92 | debounceTime(500),
93 | distinctUntilChanged(),
94 | switchMap(name => this.rjax.get(`/user`, {params: name})).subscribe(res => {
95 | this.setState({
96 | data: res
97 | });
98 | }, err => console.log('请求出错'));
99 | );
100 | }
101 | componentWillUnmount() {
102 | // 组件卸载时取消订阅
103 | this.subjectRef.unsubscribe();
104 | }
105 | search = ({target: value}) => {
106 | this.subject.next({name: value});
107 | }
108 | render() {
109 | const {data} = this.state;
110 | return (
111 |
112 |
113 |
114 | {data.map(item => - {item}
)}
115 |
116 |
117 | );
118 | }
119 | }
120 | ```
121 |
122 | 除了把每个 input 的值都直接转发给 `/user` 请求 componentDidMount() 中的代码还通过下列三个操作符对这些搜索值进行管道处理:
123 | 1. debounceTime(500) - 等待,直到用户停止输入(这个例子中是停止 1/2 秒)。
124 | 2. distinctUntilChanged() - 等待,直到搜索内容发生了变化。
125 | 3. switchMap() - 把搜索请求发送给rjax。
126 |
127 | 这样,只有当用户停止了输入且搜索值和以前不一样的时候,搜索值才会传给rjax发起请求。
128 |
129 | ### switchMap()
130 | 这个 switchMap() 操作符有三个重要的特征:
131 | 1. 它的参数是一个返回 Observable 的函数。rjax.get() 会返回 Observable,其它请求方法也一样。
132 | 2. 如果以前的搜索结果仍然是在途状态(这会出现在慢速网络中),它会取消那个请求,并发起这个新的搜索。
133 | 3. 它会按照原始的请求顺序返回这些服务的响应,而不用关心服务器实际上是以乱序返回的它们。
134 |
135 | ## 配置请求
136 | 待发送请求的option可以通过传给 Rjax 方法最后一个参数中的配置对象进行配置。
137 |
138 | ### 添加请求头
139 | 很多服务器在进行保存型操作时需要额外的请求头。 比如,它们可能需要一个 Content-Type 头来显式定义请求体的 MIME 类型。 也可能服务器会需要一个认证用的令牌(token)。
140 |
141 | ```js
142 | import { HttpHeaders } from 'rjax';
143 |
144 | const httpOptions = {
145 | headers: new HttpHeaders({
146 | 'Content-Type': 'application/json',
147 | 'Authorization': 'my-auth-token'
148 | })
149 | };
150 |
151 | rjax.post(`/user`,{name: 'xxxx'}, httpOptions)
152 | .subscribe(res => {
153 | console.log(res)
154 | }, err => console.log('请求出错'));
155 | ```
156 |
157 | ### 修改请求头
158 | 你没法直接修改前述配置对象中的现有头,因为这个 HttpHeaders 类的实例是不可变的。
159 |
160 | 改用 set() 方法代替。 它会返回当前实例的一份克隆,其中应用了这些新修改。
161 |
162 | 比如在发起下一个请求之前,如果旧的令牌已经过期了,你可能还要修改认证头。
163 |
164 | ```js
165 | httpOptions.headers =
166 | httpOptions.headers.set('Authorization', 'my-new-auth-token');
167 | ```
168 |
169 | ### URL参数
170 |
171 | 添加 URL 搜索参数也与此类似。
172 |
173 | ```js
174 | import { HttpParams } from 'rjax';
175 |
176 | searchUsers(name) {
177 | name = name.trim();
178 |
179 | const options = term ?
180 | { params: new HttpParams().set('name', name) } : {};
181 |
182 | return this.rjax.get(`/user`, options)
183 | .pipe(
184 | catchError(this.handleError('searchUsers', []))
185 | );
186 | }
187 | ```
188 |
189 | HttpParams 是不可变的,所以你也要使用 set() 方法来修改这些选项。
190 |
191 | ## 错误处理
192 |
193 | 如果这个请求导致了服务器错误怎么办?甚至,在烂网络下请求都没到服务器该怎么办?Rjax 就会返回一个错误(error)而不再是成功的响应。
194 |
195 | 通过在 .subscribe() 中添加第二个回调函数,你可以在组件中处理它:
196 |
197 | ```js
198 | showConfig() {
199 | this.configService.getConfig()
200 | .subscribe(
201 | (data: Config) => this.config = { ...data }, // success path
202 | error => this.error = error // error path
203 | );
204 | }
205 | ```
206 |
207 | 在数据访问失败时给用户一些反馈,确实是个好主意。 不过,直接显示由 Rjax 返回的原始错误数据还远远不够。
208 |
209 | ### 获取错误详情
210 |
211 | 检测错误的发生是第一步,不过如果知道具体发生了什么错误才会更有用。上面例子中传给回调函数的 err 参数的类型是 HttpErrorResponse,它包含了这个错误中一些很有用的信息。
212 |
213 | 可能发生的错误分为两种。如果后端返回了一个失败的返回码(如 404、500 等),它会返回一个错误响应体。
214 |
215 | 或者,如果在客户端这边出了错误(比如在 RxJS 操作符 (operator) 中抛出的异常或某些阻碍完成这个请求的网络错误),就会抛出一个 Error 类型的异常。
216 |
217 | Rjax 会在 HttpErrorResponse 中捕获所有类型的错误信息,你可以查看这个响应体以了解到底发生了什么。
218 |
219 | 你可能首先要设计一个错误处理器,就像这样:
220 |
221 | ```js
222 | handleError(error) {
223 | if (error.error instanceof ErrorEvent) {
224 | // A client-side or network error occurred. Handle it accordingly.
225 | console.error('An error occurred:', error.error.message);
226 | } else {
227 | // The backend returned an unsuccessful response code.
228 | // The response body may contain clues as to what went wrong,
229 | console.error(
230 | `Backend returned code ${error.status}, ` +
231 | `body was: ${error.error}`);
232 | }
233 | // return an observable with a user-facing error message
234 | return throwError(
235 | 'Something bad happened; please try again later.');
236 | };
237 | ```
238 |
239 | 注意,该处理器返回一个带有用户友好的错误信息的 RxJS ErrorObservable 对象。 该服务的消费者期望服务的方法返回某种形式的 Observable,就算是“错误的”也可以。
240 |
241 | 现在,你获取了由 Rjax 方法返回的 Observable,并把它们通过管道传给错误处理器。
242 |
243 | ```js
244 | import { catchError } from 'rxjs/operators';
245 |
246 | getConfig() {
247 | return this.rjax.get(this.configUrl)
248 | .pipe(
249 | catchError(this.handleError)
250 | );
251 | }
252 | ```
253 |
254 | ### retry()
255 |
256 | 有时候,错误只是临时性的,只要重试就可能会自动消失。 比如,在移动端场景中可能会遇到网络中断的情况,只要重试一下就能拿到正确的结果。
257 |
258 | RxJS 库提供了几个 retry 操作符,它们值得仔细看看。 其中最简单的是 retry(),它可以对失败的 Observable 自动重新订阅几次。对 HttpClient 方法调用的结果进行重新订阅会导致重新发起 HTTP 请求。
259 |
260 | 把它插入到 Rjax 方法结果的管道中,就放在错误处理器的紧前面。
261 |
262 | ```js
263 | import { retry, catchError } from 'rxjs/operators';
264 |
265 | getConfig() {
266 | return this.rjax.get(this.configUrl)
267 | .pipe(
268 | retry(3), // retry a failed request up to 3 times
269 | catchError(this.handleError) // then handle the error
270 | );
271 | }
272 | ```
273 |
274 | ## 请求非 JSON 格式的数据
275 |
276 | 不是所有的 API 都会返回 JSON 数据。在下面这个例子中的方法会从服务器读取文本文件, 并把文件的内容记录下来,然后把这些内容使用 Observable 的形式返回给调用者。
277 |
278 | ```js
279 | getTextFile(filename) {
280 | // The Observable returned by get() is of type Observable
281 | // because a text response was specified.
282 | // There's no need to pass a type parameter to get().
283 | return this.rjax.get(filename, {responseType: 'text'})
284 | .pipe(
285 | tap( // Log the result or error
286 | data => this.log(filename, data),
287 | error => this.logError(filename, error)
288 | )
289 | );
290 | }
291 | ```
292 |
293 | 这里的 rjax.get() 返回字符串而不是默认的 JSON 对象,因为它的 responseType 选项是 'text'。
294 |
295 | 这里的 rjax.get() 返回字符串而不是默认的 JSON 对象,因为它的 responseType 选项是 'text'。
296 |
297 | responseType的可选值有 "arraybuffer" | "blob" | "text" | "json"
298 |
299 | ## 读取完整的响应体
300 | 响应体可能并不包含你需要的全部信息。有时候服务器会返回一个特殊的响应头或状态码,以标记出特定的条件,因此读取它们可能是必要的。
301 |
302 | 要这样做,你就要通过 observe 选项来告诉 Rjax,你想要完整的响应信息,而不是只有响应体:
303 |
304 | ```js
305 | getConfigResponse() {
306 | return this.rjax.get(
307 | this.configUrl, { observe: 'response' });
308 | }
309 | ```
310 |
311 | 现在 rjax.get() 会返回一个 [HttpResponse](https://www.angular.cn/api/common/http/HttpResponse) 类型的 Observable,而不只是 JSON 数据。
312 |
313 | ## 监听进度事件
314 | 有时,应用会传输大量数据,并且这些传输可能会花费很长时间。 典型的例子是文件上传。 可以通过在传输过程中提供进度反馈,来提升用户体验。
315 |
316 | 要想开启进度事件的响应,你可以创建一个把 reportProgress 选项设置为 true 的 HttpRequest 实例,以开启进度跟踪事件。
317 |
318 | ```js
319 | import { HttpRequest } from 'rjax';
320 |
321 | const req = new HttpRequest('POST', '/upload/file', file, {
322 | reportProgress: true
323 | });
324 | ```
325 |
326 | ::: warning
327 | 每个进度事件都会触发变更检测,所以,你应该只有当确实希望在 UI 中报告进度时才打开这个选项。
328 | :::
329 |
330 | 接下来,把这个请求对象传给 rjax.request() 方法,它返回一个 HttpEvents 的 Observable,同样也可以在拦截器中处理这些事件。
331 |
332 | ```js
333 | return this.rjax.request(req).pipe(
334 | map(event => this.getEventMessage(event, file)),
335 | tap(message => this.showProgress(message)),
336 | last(), // return last (completed) message to caller
337 | catchError(this.handleError(file))
338 | );
339 | ```
340 |
341 | getEventMessage 方法会解释事件流中的每一个 HttpEvent 类型。
342 |
343 | ```js
344 | import { HttpEventType } from 'rjax';
345 |
346 | /** Return distinct message for sent, upload progress, & response events */
347 | private getEventMessage(event, file) {
348 | switch (event.type) {
349 | case HttpEventType.Sent:
350 | return `Uploading file "${file.name}" of size ${file.size}.`;
351 |
352 | case HttpEventType.UploadProgress:
353 | // Compute and show the % done:
354 | const percentDone = Math.round(100 * event.loaded / event.total);
355 | return `File "${file.name}" is ${percentDone}% uploaded.`;
356 |
357 | case HttpEventType.Response:
358 | return `File "${file.name}" was completely uploaded!`;
359 |
360 | default:
361 | return `File "${file.name}" surprising upload event: ${event.type}.`;
362 | }
363 | }
364 | ```
--------------------------------------------------------------------------------
/docs/guide/example.md:
--------------------------------------------------------------------------------
1 | # 使用示例
2 |
3 | ## 并发请求
4 |
5 | 并发多个请求,等待所有请求都结束后才执行回调相当于 **Promise.all()**
6 |
7 | ```js
8 | import { Rjax } from 'rjax';
9 | import { combineLatest } from 'rxjs';
10 |
11 | // 创建实例
12 | const rjax = new Rjax();
13 |
14 | const p1$ = rjax.get(`/user/12345`);
15 | const p2$ = rjax.get(`/user/123456`);
16 |
17 | combineLatest(p1$, p2$).subscribe(([res1, res2]) => {
18 | // 请求成功回调
19 | console.log(res1); // p1$ 的请求结果
20 | console.log(res2); // p2$ 的请求结果
21 | }, error => {
22 | // 请求失败回调
23 | console.log(error);
24 | });
25 | ```
26 |
27 | ## 多数据源合并
28 |
29 | 例如某个图表的数据来源于websocket和ajax手动请求,返回格式是一样的,可以将这两个数据流进行合并创建新的Observable,组件只要订阅合并后的数据流而不关心数据的来源
30 |
31 | ```js
32 | import { Rjax } from 'rjax';
33 | import { merge } from 'rxjs';
34 | import { webSocket } from 'rxjs/webSocket';
35 |
36 | const rjax = new Rjax();
37 |
38 | const ws$ = webSocket('wss://echo.websocket.org'); // websocket
39 | const ajax$ = rjax.get(`/user/12345`); // ajax
40 |
41 | // 合并数据流
42 | merge(ws$, ajax$).subscribe(res => {
43 | console.log(res)
44 | }, err => console.log('请求出错'));
45 | ```
46 |
47 |
--------------------------------------------------------------------------------
/docs/guide/getting-started.md:
--------------------------------------------------------------------------------
1 | # 快速上手
2 |
3 | ## 安装
4 | ```bash
5 | yarn add rjax # 或者:npm install rjax --save
6 | ```
7 |
8 | ## 使用
9 | ```js
10 | import { Rjax } from 'rjax';
11 |
12 | // 创建实例
13 | const rjax = new Rjax({
14 | baseURL: 'https://some-domain.com/api/', // 设置请求基路径,可选
15 | timeout: 1000, // 设置请求超时时间,可选
16 | interceptors: [] // 设置请求响应拦截器,可设置多组,可选
17 | xsrfCookieName: 'XSRF-TOKEN', // 是用作 xsrf token 的值的cookie的名称,默认'XSRF-TOKEN',可选
18 | xsrfHeaderName: 'X-XSRF-TOKEN', // 是承载 xsrf token 的值的 HTTP 头的名称,默认'X-XSRF-TOKEN',可选
19 | headers: {}, // 添加统一的headers,默认{},可选
20 | withCredentials: false, // 表示跨域请求时是否需要使用凭证,默认false,可选
21 | jsonp: false, // 是否添加jsonp请求功能,默认false,可选
22 | });
23 |
24 | // 发起GET请求
25 | rjax.get(`/user/12345`).subscribe(response => {
26 | // 请求成功回调
27 | console.log(response);
28 | }, error => {
29 | // 请求失败回调
30 | console.log(error);
31 | });
32 | ```
33 | ### 以下是可用的实例方法
34 | ```ts
35 | class HttpClient {
36 | request(first: string | HttpRequest, url?: string, options: { body?: any; headers?: HttpHeaders | { [header: string]: string | string[]; }; observe?: HttpObserve; params?: HttpParams | { [param: string]: string | string[]; }; reportProgress?: boolean; responseType?: "arraybuffer" | ... 2 more ... | "json"; withCredentials?: boolean; } = {}): Observable
37 | delete(url: string, options: { headers?: HttpHeaders | { [header: string]: string | string[]; }; observe?: HttpObserve; params?: HttpParams | { [param: string]: string | string[]; }; reportProgress?: boolean; responseType?: "arraybuffer" | "blob" | "text" | "json"; withCredentials?: boolean; } = {}): Observable
38 | get(url: string, options: { headers?: HttpHeaders | { [header: string]: string | string[]; }; observe?: HttpObserve; params?: HttpParams | { [param: string]: string | string[]; }; reportProgress?: boolean; responseType?: "arraybuffer" | "blob" | "text" | "json"; withCredentials?: boolean; } = {}): Observable
39 | head(url: string, options: { headers?: HttpHeaders | { [header: string]: string | string[]; }; observe?: HttpObserve; params?: HttpParams | { [param: string]: string | string[]; }; reportProgress?: boolean; responseType?: "arraybuffer" | "blob" | "text" | "json"; withCredentials?: boolean; } = {}): Observable
40 | jsonp(url: string, callbackParam: string): Observable
41 | options(url: string, options: { headers?: HttpHeaders | { [header: string]: string | string[]; }; observe?: HttpObserve; params?: HttpParams | { [param: string]: string | string[]; }; reportProgress?: boolean; responseType?: "arraybuffer" | "blob" | "text" | "json"; withCredentials?: boolean; } = {}): Observable
42 | patch(url: string, body: any, options: { headers?: HttpHeaders | { [header: string]: string | string[]; }; observe?: HttpObserve; params?: HttpParams | { [param: string]: string | string[]; }; reportProgress?: boolean; responseType?: "arraybuffer" | "blob" | "text" | "json"; withCredentials?: boolean; } = {}): Observable
43 | post(url: string, body: any, options: { headers?: HttpHeaders | { [header: string]: string | string[]; }; observe?: HttpObserve; params?: HttpParams | { [param: string]: string | string[]; }; reportProgress?: boolean; responseType?: "arraybuffer" | "blob" | "text" | "json"; withCredentials?: boolean; } = {}): Observable
44 | put(url: string, body: any, options: { headers?: HttpHeaders | { [header: string]: string | string[]; }; observe?: HttpObserve; params?: HttpParams | { [param: string]: string | string[]; }; reportProgress?: boolean; responseType?: "arraybuffer" | "blob" | "text" | "json"; withCredentials?: boolean; } = {}): Observable
45 | }
46 | ```
--------------------------------------------------------------------------------
/lib/index.ts:
--------------------------------------------------------------------------------
1 | import { Rjax } from './public_api';
2 | export * from './public_api';
3 | export default Rjax;
4 |
--------------------------------------------------------------------------------
/lib/public_api.ts:
--------------------------------------------------------------------------------
1 | export {Rjax} from './src/rjax';
2 | export {HttpClient} from './src/client';
3 | export {HttpHeaders} from './src/headers';
4 | export {HttpParameterCodec, HttpParams, HttpUrlEncodingCodec} from './src/params';
5 | export {HttpDownloadProgressEvent, HttpErrorResponse, HttpEvent, HttpEventType, HttpHeaderResponse, HttpProgressEvent, HttpResponse, HttpResponseBase, HttpSentEvent, HttpUserEvent} from './src/response';
6 | export {HttpBackend, HttpHandler} from './src/backend';
7 | export {HttpRequest} from './src/request';
8 | export {HttpXhrBackend, XhrFactory} from './src/xhr';
--------------------------------------------------------------------------------
/lib/src/backend.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @license
3 | * Copyright Google Inc. All Rights Reserved.
4 | *
5 | * Use of this source code is governed by an MIT-style license that can be
6 | * found in the LICENSE file at https://angular.io/license
7 | */
8 |
9 | import {Observable} from 'rxjs';
10 | import {HttpRequest} from './request';
11 | import {HttpEvent} from './response';
12 |
13 | /**
14 | * Transforms an `HttpRequest` into a stream of `HttpEvent`s, one of which will likely be a
15 | * `HttpResponse`.
16 | *
17 | * `HttpHandler` is injectable. When injected, the handler instance dispatches requests to the
18 | * first interceptor in the chain, which dispatches to the second, etc, eventually reaching the
19 | * `HttpBackend`.
20 | *
21 | * In an `HttpInterceptor`, the `HttpHandler` parameter is the next interceptor in the chain.
22 | *
23 | * @publicApi
24 | */
25 | export abstract class HttpHandler {
26 | abstract handle(req: HttpRequest): Observable>;
27 | }
28 |
29 | /**
30 | * A final `HttpHandler` which will dispatch the request via browser HTTP APIs to a backend.
31 | *
32 | * Interceptors sit between the `HttpClient` interface and the `HttpBackend`.
33 | *
34 | * When injected, `HttpBackend` dispatches requests directly to the backend, without going
35 | * through the interceptor chain.
36 | *
37 | * @publicApi
38 | */
39 | export abstract class HttpBackend implements HttpHandler {
40 | abstract handle(req: HttpRequest): Observable>;
41 | }
42 |
--------------------------------------------------------------------------------
/lib/src/cookie.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @license
3 | * Copyright Google Inc. All Rights Reserved.
4 | *
5 | * Use of this source code is governed by an MIT-style license that can be
6 | * found in the LICENSE file at https://angular.io/license
7 | */
8 |
9 | export function parseCookieValue(cookieStr: string, name: string): string|null {
10 | name = encodeURIComponent(name);
11 | for (const cookie of cookieStr.split(';')) {
12 | const eqIndex = cookie.indexOf('=');
13 | const [cookieName, cookieValue]: string[] =
14 | eqIndex == -1 ? [cookie, ''] : [cookie.slice(0, eqIndex), cookie.slice(eqIndex + 1)];
15 | if (cookieName.trim() === name) {
16 | return decodeURIComponent(cookieValue);
17 | }
18 | }
19 | return null;
20 | }
21 |
--------------------------------------------------------------------------------
/lib/src/handler.ts:
--------------------------------------------------------------------------------
1 | import { HttpHandler, HttpBackend } from './backend';
2 | import { Observable } from 'rxjs';
3 | import { HttpRequest } from './request';
4 | import { HttpInterceptorHandler, HttpInterceptor, NoopInterceptor } from './interceptor';
5 | import { HttpEvent } from './response';
6 | import { HttpXhrBackend } from './xhr';
7 |
8 | export class HttpInterceptingHandler implements HttpHandler {
9 | private chain: HttpHandler|null = null;
10 | private backend: HttpBackend = new HttpXhrBackend();
11 | constructor(private interceptors: HttpInterceptor[] = [new NoopInterceptor()]) {}
12 |
13 | handle(req: HttpRequest): Observable> {
14 | if (this.chain === null) {
15 | this.chain = this.interceptors.reduceRight(
16 | (next, interceptor) => new HttpInterceptorHandler(next, interceptor), this.backend);
17 | }
18 | return this.chain.handle(req);
19 | }
20 | }
21 |
22 | /**
23 | * Constructs an `HttpHandler` that applies interceptors
24 | * to a request before passing it to the given `HttpBackend`.
25 | *
26 | * Use as a factory function within `HttpClientModule`.
27 | *
28 | *
29 | */
30 | export function interceptingHandler(
31 | backend: HttpBackend, interceptors: HttpInterceptor[] | null = []): HttpHandler {
32 | if (!interceptors) {
33 | return backend;
34 | }
35 | return interceptors.reduceRight(
36 | (next, interceptor) => new HttpInterceptorHandler(next, interceptor), backend);
37 | }
38 |
39 | /**
40 | * Factory function that determines where to store JSONP callbacks.
41 | *
42 | * Ordinarily JSONP callbacks are stored on the `window` object, but this may not exist
43 | * in test environments. In that case, callbacks are stored on an anonymous object instead.
44 | *
45 | *
46 | */
47 | export function jsonpCallbackContext(): Object {
48 | if (typeof window === 'object') {
49 | return window;
50 | }
51 | return {};
52 | }
--------------------------------------------------------------------------------
/lib/src/headers.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @license
3 | * Copyright Google Inc. All Rights Reserved.
4 | *
5 | * Use of this source code is governed by an MIT-style license that can be
6 | * found in the LICENSE file at https://angular.io/license
7 | */
8 |
9 | interface Update {
10 | name: string;
11 | value?: string|string[];
12 | op: 'a'|'s'|'d';
13 | }
14 |
15 | /**
16 | * Immutable set of Http headers, with lazy parsing.
17 | *
18 | * @publicApi
19 | */
20 | export class HttpHeaders {
21 | /**
22 | * Internal map of lowercase header names to values.
23 | */
24 | // TODO(issue/24571): remove '!'.
25 | private headers !: Map;
26 |
27 |
28 | /**
29 | * Internal map of lowercased header names to the normalized
30 | * form of the name (the form seen first).
31 | */
32 | private normalizedNames: Map = new Map();
33 |
34 | /**
35 | * Complete the lazy initialization of this object (needed before reading).
36 | */
37 | // TODO(issue/24571): remove '!'.
38 | private lazyInit !: HttpHeaders | Function | null;
39 |
40 | /**
41 | * Queued updates to be materialized the next initialization.
42 | */
43 | private lazyUpdate: Update[]|null = null;
44 |
45 | constructor(headers?: string|{[name: string]: string | string[]}) {
46 | if (!headers) {
47 | this.headers = new Map();
48 | } else if (typeof headers === 'string') {
49 | this.lazyInit = () => {
50 | this.headers = new Map();
51 | headers.split('\n').forEach(line => {
52 | const index = line.indexOf(':');
53 | if (index > 0) {
54 | const name = line.slice(0, index);
55 | const key = name.toLowerCase();
56 | const value = line.slice(index + 1).trim();
57 | this.maybeSetNormalizedName(name, key);
58 | if (this.headers.has(key)) {
59 | this.headers.get(key) !.push(value);
60 | } else {
61 | this.headers.set(key, [value]);
62 | }
63 | }
64 | });
65 | };
66 | } else {
67 | this.lazyInit = () => {
68 | this.headers = new Map();
69 | Object.keys(headers).forEach(name => {
70 | let values: string|string[] = headers[name];
71 | const key = name.toLowerCase();
72 | if (typeof values === 'string') {
73 | values = [values];
74 | }
75 | if (values.length > 0) {
76 | this.headers.set(key, values);
77 | this.maybeSetNormalizedName(name, key);
78 | }
79 | });
80 | };
81 | }
82 | }
83 |
84 | /**
85 | * Checks for existence of header by given name.
86 | */
87 | has(name: string): boolean {
88 | this.init();
89 |
90 | return this.headers.has(name.toLowerCase());
91 | }
92 |
93 | /**
94 | * Returns first header that matches given name.
95 | */
96 | get(name: string): string|null {
97 | this.init();
98 |
99 | const values = this.headers.get(name.toLowerCase());
100 | return values && values.length > 0 ? values[0] : null;
101 | }
102 |
103 | /**
104 | * Returns the names of the headers
105 | */
106 | keys(): string[] {
107 | this.init();
108 |
109 | return Array.from(this.normalizedNames.values());
110 | }
111 |
112 | /**
113 | * Returns list of header values for a given name.
114 | */
115 | getAll(name: string): string[]|null {
116 | this.init();
117 |
118 | return this.headers.get(name.toLowerCase()) || null;
119 | }
120 |
121 | append(name: string, value: string|string[]): HttpHeaders {
122 | return this.clone({name, value, op: 'a'});
123 | }
124 |
125 | set(name: string, value: string|string[]): HttpHeaders {
126 | return this.clone({name, value, op: 's'});
127 | }
128 |
129 | delete (name: string, value?: string|string[]): HttpHeaders {
130 | return this.clone({name, value, op: 'd'});
131 | }
132 |
133 | private maybeSetNormalizedName(name: string, lcName: string): void {
134 | if (!this.normalizedNames.has(lcName)) {
135 | this.normalizedNames.set(lcName, name);
136 | }
137 | }
138 |
139 | private init(): void {
140 | if (!!this.lazyInit) {
141 | if (this.lazyInit instanceof HttpHeaders) {
142 | this.copyFrom(this.lazyInit);
143 | } else {
144 | this.lazyInit();
145 | }
146 | this.lazyInit = null;
147 | if (!!this.lazyUpdate) {
148 | this.lazyUpdate.forEach(update => this.applyUpdate(update));
149 | this.lazyUpdate = null;
150 | }
151 | }
152 | }
153 |
154 | private copyFrom(other: HttpHeaders) {
155 | other.init();
156 | Array.from(other.headers.keys()).forEach(key => {
157 | this.headers.set(key, other.headers.get(key) !);
158 | this.normalizedNames.set(key, other.normalizedNames.get(key) !);
159 | });
160 | }
161 |
162 | private clone(update: Update): HttpHeaders {
163 | const clone = new HttpHeaders();
164 | clone.lazyInit =
165 | (!!this.lazyInit && this.lazyInit instanceof HttpHeaders) ? this.lazyInit : this;
166 | clone.lazyUpdate = (this.lazyUpdate || []).concat([update]);
167 | return clone;
168 | }
169 |
170 | private applyUpdate(update: Update): void {
171 | const key = update.name.toLowerCase();
172 | switch (update.op) {
173 | case 'a':
174 | case 's':
175 | let value = update.value !;
176 | if (typeof value === 'string') {
177 | value = [value];
178 | }
179 | if (value.length === 0) {
180 | return;
181 | }
182 | this.maybeSetNormalizedName(update.name, key);
183 | const base = (update.op === 'a' ? this.headers.get(key) : undefined) || [];
184 | base.push(...value);
185 | this.headers.set(key, base);
186 | break;
187 | case 'd':
188 | const toDelete = update.value as string | undefined;
189 | if (!toDelete) {
190 | this.headers.delete(key);
191 | this.normalizedNames.delete(key);
192 | } else {
193 | let existing = this.headers.get(key);
194 | if (!existing) {
195 | return;
196 | }
197 | existing = existing.filter(value => toDelete.indexOf(value) === -1);
198 | if (existing.length === 0) {
199 | this.headers.delete(key);
200 | this.normalizedNames.delete(key);
201 | } else {
202 | this.headers.set(key, existing);
203 | }
204 | }
205 | break;
206 | }
207 | }
208 |
209 | /**
210 | * @internal
211 | */
212 | forEach(fn: (name: string, values: string[]) => void) {
213 | this.init();
214 | Array.from(this.normalizedNames.keys())
215 | .forEach(key => fn(this.normalizedNames.get(key) !, this.headers.get(key) !));
216 | }
217 | }
218 |
--------------------------------------------------------------------------------
/lib/src/interceptor.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @license
3 | * Copyright Google Inc. All Rights Reserved.
4 | *
5 | * Use of this source code is governed by an MIT-style license that can be
6 | * found in the LICENSE file at https://angular.io/license
7 | */
8 |
9 | import {Observable} from 'rxjs';
10 |
11 | import {HttpHandler} from './backend';
12 | import {HttpRequest} from './request';
13 | import {HttpEvent} from './response';
14 | import { HttpHeaders } from './headers';
15 | import { timeout } from 'rxjs/operators';
16 |
17 | export interface DefaultConfig {
18 | baseURL: string;
19 | timeout: number;
20 | xsrfCookieName: string;
21 | xsrfHeaderName: string;
22 | withCredentials: boolean;
23 | headers: {[header: string]: string | string[]};
24 | jsonp: boolean;
25 | interceptors?: HttpInterceptor[];
26 | }
27 |
28 | /**
29 | * Creates a new URL by combining the specified URLs
30 | *
31 | * @param {string} baseURL The base URL
32 | * @param {string} relativeURL The relative URL
33 | * @returns {string} The combined URL
34 | */
35 | function combineURLs(baseURL: string, relativeURL: string) {
36 | return relativeURL
37 | ? baseURL.replace(/\/+$/, '') + '/' + relativeURL.replace(/^\/+/, '')
38 | : baseURL;
39 | }
40 |
41 | /**
42 | * Determines whether the specified URL is absolute
43 | *
44 | * @param {string} url The URL to test
45 | * @returns {boolean} True if the specified URL is absolute, otherwise false
46 | */
47 | function isAbsoluteURL(url: string) {
48 | // A URL is considered absolute if it begins with "://" or "//" (protocol-relative URL).
49 | // RFC 3986 defines scheme name as a sequence of characters beginning with a letter and followed
50 | // by any combination of letters, digits, plus, period, or hyphen.
51 | return /^([a-z][a-z\d\+\-\.]*:)?\/\//i.test(url);
52 | }
53 |
54 | /**
55 | * Intercepts `HttpRequest` and handles them.
56 | *
57 | * Most interceptors will transform the outgoing request before passing it to the
58 | * next interceptor in the chain, by calling `next.handle(transformedReq)`.
59 | *
60 | * In rare cases, interceptors may wish to completely handle a request themselves,
61 | * and not delegate to the remainder of the chain. This behavior is allowed.
62 | *
63 | * @publicApi
64 | */
65 | export interface HttpInterceptor {
66 | /**
67 | * Intercept an outgoing `HttpRequest` and optionally transform it or the
68 | * response.
69 | *
70 | * Typically an interceptor will transform the outgoing request before returning
71 | * `next.handle(transformedReq)`. An interceptor may choose to transform the
72 | * response event stream as well, by applying additional Rx operators on the stream
73 | * returned by `next.handle()`.
74 | *
75 | * More rarely, an interceptor may choose to completely handle the request itself,
76 | * and compose a new event stream instead of invoking `next.handle()`. This is
77 | * acceptable behavior, but keep in mind further interceptors will be skipped entirely.
78 | *
79 | * It is also rare but valid for an interceptor to return multiple responses on the
80 | * event stream for a single request.
81 | */
82 | intercept(req: HttpRequest, next: HttpHandler): Observable>;
83 | }
84 |
85 | /**
86 | * `HttpHandler` which applies an `HttpInterceptor` to an `HttpRequest`.
87 | *
88 | *
89 | */
90 | export class HttpInterceptorHandler implements HttpHandler {
91 | constructor(private next: HttpHandler, private interceptor: HttpInterceptor) {}
92 |
93 | handle(req: HttpRequest): Observable> {
94 | return this.interceptor.intercept(req, this.next);
95 | }
96 | }
97 |
98 | /**
99 | * A multi-provider token which represents the array of `HttpInterceptor`s that
100 | * are registered.
101 | *
102 | * @publicApi
103 | */
104 |
105 | export class NoopInterceptor implements HttpInterceptor {
106 | intercept(req: HttpRequest, next: HttpHandler): Observable> {
107 | return next.handle(req);
108 | }
109 | }
110 |
111 | export class DefaultInterceptor implements HttpInterceptor {
112 | constructor(private config: DefaultConfig) {}
113 | intercept(req: HttpRequest, next: HttpHandler): Observable> {
114 | let headers!: HttpHeaders;
115 | if (typeof this.config.headers === 'object') {
116 | for (const key in this.config.headers) {
117 | if (this.config.headers.hasOwnProperty(key)) {
118 | headers = req.headers.set(key, this.config.headers[key]);
119 | }
120 | }
121 | }
122 | let url = req.url;
123 | // Support baseURL config
124 | if (this.config.baseURL && !isAbsoluteURL(url)) {
125 | url = combineURLs(this.config.baseURL, url);
126 | }
127 | const defaultReq = req.clone({
128 | url,
129 | headers,
130 | withCredentials: this.config.withCredentials
131 | });
132 | if (Number(this.config.timeout) > 0) {
133 | return next.handle(defaultReq).pipe(timeout(this.config.timeout));
134 | }
135 | return next.handle(defaultReq);
136 | }
137 | }
--------------------------------------------------------------------------------
/lib/src/jsonp.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @license
3 | * Copyright Google Inc. All Rights Reserved.
4 | *
5 | * Use of this source code is governed by an MIT-style license that can be
6 | * found in the LICENSE file at https://angular.io/license
7 | */
8 |
9 | import {Observable, Observer} from 'rxjs';
10 |
11 | import {HttpBackend, HttpHandler} from './backend';
12 | import {HttpRequest} from './request';
13 | import {HttpErrorResponse, HttpEvent, HttpEventType, HttpResponse} from './response';
14 | import { jsonpCallbackContext } from './handler';
15 |
16 | // Every request made through JSONP needs a callback name that's unique across the
17 | // whole page. Each request is assigned an id and the callback name is constructed
18 | // from that. The next id to be assigned is tracked in a global variable here that
19 | // is shared among all applications on the page.
20 | let nextRequestId: number = 0;
21 |
22 | // Error text given when a JSONP script is injected, but doesn't invoke the callback
23 | // passed in its URL.
24 | export const JSONP_ERR_NO_CALLBACK = 'JSONP injected script did not invoke callback.';
25 |
26 | // Error text given when a request is passed to the JsonpClientBackend that doesn't
27 | // have a request method JSONP.
28 | export const JSONP_ERR_WRONG_METHOD = 'JSONP requests must use JSONP request method.';
29 | export const JSONP_ERR_WRONG_RESPONSE_TYPE = 'JSONP requests must use Json response type.';
30 |
31 | /**
32 | * DI token/abstract type representing a map of JSONP callbacks.
33 | *
34 | * In the browser, this should always be the `window` object.
35 | *
36 | *
37 | */
38 | export abstract class JsonpCallbackContext { [key: string]: (data: any) => void; }
39 |
40 | /**
41 | * `HttpBackend` that only processes `HttpRequest` with the JSONP method,
42 | * by performing JSONP style requests.
43 | *
44 | * @publicApi
45 | */
46 | export class JsonpClientBackend implements HttpBackend {
47 | private document = document;
48 | private callbackMap: JsonpCallbackContext = jsonpCallbackContext();
49 | constructor() {}
50 |
51 | /**
52 | * Get the name of the next callback method, by incrementing the global `nextRequestId`.
53 | */
54 | private nextCallback(): string { return `ng_jsonp_callback_${nextRequestId++}`; }
55 |
56 | /**
57 | * Process a JSONP request and return an event stream of the results.
58 | */
59 | handle(req: HttpRequest): Observable> {
60 | // Firstly, check both the method and response type. If either doesn't match
61 | // then the request was improperly routed here and cannot be handled.
62 | if (req.method !== 'JSONP') {
63 | throw new Error(JSONP_ERR_WRONG_METHOD);
64 | } else if (req.responseType !== 'json') {
65 | throw new Error(JSONP_ERR_WRONG_RESPONSE_TYPE);
66 | }
67 |
68 | // Everything else happens inside the Observable boundary.
69 | return new Observable>((observer: Observer>) => {
70 | // The first step to make a request is to generate the callback name, and replace the
71 | // callback placeholder in the URL with the name. Care has to be taken here to ensure
72 | // a trailing &, if matched, gets inserted back into the URL in the correct place.
73 | const callback = this.nextCallback();
74 | const url = req.urlWithParams.replace(/=JSONP_CALLBACK(&|$)/, `=${callback}$1`);
75 |
76 | // Construct the