diff --git a/.codegraph/.gitignore b/.codegraph/.gitignore
new file mode 100644
index 00000000..d20c0fe4
--- /dev/null
+++ b/.codegraph/.gitignore
@@ -0,0 +1,5 @@
+# CodeGraph data files — local to each machine, not for committing.
+# Ignore everything in .codegraph/ except this file itself, so transient
+# files (the database, daemon.pid, sockets, logs) never show up in git.
+*
+!.gitignore
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
new file mode 100644
index 00000000..bae24916
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -0,0 +1,31 @@
+---
+name: Bug report
+about: Create a report to help us improve
+title: ''
+labels: ''
+assignees: ''
+
+---
+
+**Describe the bug**
+A clear and concise description of what the bug is.
+
+**To Reproduce**
+Steps to reproduce the behavior:
+1. Go to '...'
+2. Click on '....'
+3. Scroll down to '....'
+4. See error
+
+**Expected behavior**
+A clear and concise description of what you expected to happen.
+
+**Screenshots**
+If applicable, add screenshots to help explain your problem.
+
+**Desktop (please complete the following information):**
+ - OS: [e.g. iOS]
+ - Version [e.g. 22]
+
+**Additional context**
+Add any other context about the problem here.
diff --git a/.github/ISSUE_TEMPLATE/custom.md b/.github/ISSUE_TEMPLATE/custom.md
new file mode 100644
index 00000000..48d5f81f
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/custom.md
@@ -0,0 +1,10 @@
+---
+name: Custom issue template
+about: Describe this issue template's purpose here.
+title: ''
+labels: ''
+assignees: ''
+
+---
+
+
diff --git a/.github/scripts/generate-benchmark-summary.sh b/.github/scripts/generate-benchmark-summary.sh
new file mode 100644
index 00000000..aaefae8e
--- /dev/null
+++ b/.github/scripts/generate-benchmark-summary.sh
@@ -0,0 +1,68 @@
+#!/bin/bash
+
+# Benchmark Results Summary Script
+# 直接从 BenchmarkDotNet 生成的 Markdown 报告中提取结果
+
+echo "# Benchmark Results Summary"
+echo ""
+echo "## Multi-Framework Performance Comparison"
+echo ""
+
+# 定义框架列表(暂时移除 net10.0-aot,因为 BenchmarkDotNet 不支持 AOT)
+frameworks=("netcoreapp3.1" "net5.0" "net8.0" "net10.0")
+
+# 查找所有结果目录
+for fw in "${frameworks[@]}"; do
+ result_dir="BenchmarkDotNet.Artifacts.${fw}/results"
+
+ if [ -d "$result_dir" ]; then
+ echo "### Framework: ${fw}"
+ echo ""
+
+ # 查找 Markdown 报告文件
+ md_files=$(find "$result_dir" -name "*-report-github.md" -type f 2>/dev/null)
+
+ if [ -n "$md_files" ]; then
+ for md_file in $md_files; do
+ benchmark_name=$(basename "$md_file" -report-github.md)
+ echo "#### $benchmark_name"
+ echo ""
+
+ # 直接提取 Markdown 表格(从表头到空行)
+ # 查找包含 "| Method" 的行,然后提取完整表格
+ awk '/\| Method.*\|/ {found=1} found {print} /^$/ && found {exit}' "$md_file" |
+ grep -v "^$" || echo "No table found in $md_file"
+
+ echo ""
+ done
+ else
+ echo "No Markdown reports found in $result_dir"
+ fi
+
+ echo ""
+ echo "---"
+ echo ""
+ else
+ echo "### Framework: ${fw} - **No results found**"
+ echo ""
+ fi
+done
+
+echo "## Performance Metrics Explanation"
+echo ""
+echo "- **Mean**: Average execution time"
+echo "- **Error**: Half of 99.9% confidence interval"
+echo "- **StdDev**: Standard deviation of all measurements"
+echo "- **Ratio**: Performance ratio compared to baseline (lower is better for baseline)"
+echo "- **RatioSD**: Standard deviation of the Ratio"
+echo "- **Gen0**: GC Generation 0 collections per 1000 operations"
+echo "- **Allocated**: Total memory allocated per operation"
+echo "- **Alloc Ratio**: Memory allocation ratio compared to baseline"
+echo ""
+echo "## Notes"
+echo ""
+echo "- **Baseline** method (marked with 🏆 or Ratio=1.00) is the reference point"
+echo "- Lower values are better for Mean, Error, StdDev, and Allocated"
+echo "- Ratio > 1.00 means slower/more memory than baseline"
+echo "- Download the artifact 'benchmark-results' for detailed reports and raw data"
+echo ""
diff --git a/.github/workflows/ci-benchmark.yml b/.github/workflows/ci-benchmark.yml
new file mode 100644
index 00000000..ec5e21e0
--- /dev/null
+++ b/.github/workflows/ci-benchmark.yml
@@ -0,0 +1,210 @@
+name: Performance Benchmark
+
+# 仅支持手动触发
+on:
+ workflow_dispatch:
+
+env:
+ DOTNET_CLI_TELEMETRY_OPTOUT: true
+ DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true
+ DOTNET_NOLOGO: true
+
+jobs:
+ benchmark:
+ name: Run Performance Benchmarks
+ runs-on: ubuntu-latest
+ timeout-minutes: 60
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ # 安装性能测试所需的 .NET SDK 版本
+ - name: Setup .NET Core 3.1
+ uses: actions/setup-dotnet@v4
+ with:
+ dotnet-version: '3.1.x'
+
+ - name: Setup .NET 5.0
+ uses: actions/setup-dotnet@v4
+ with:
+ dotnet-version: '5.0.x'
+
+ - name: Setup .NET 8.0
+ uses: actions/setup-dotnet@v4
+ with:
+ dotnet-version: '8.0.x'
+
+ - name: Setup .NET 10.0
+ uses: actions/setup-dotnet@v4
+ with:
+ dotnet-version: '10.0.x'
+
+ # 安装 OpenSSL 1.1(.NET Core 3.1 和 .NET 5.0 需要)
+ - name: Install OpenSSL 1.1 for older .NET versions
+ run: |
+ # Ubuntu 22.04+ 默认只有 OpenSSL 3.x,需要手动安装 1.1
+ wget http://archive.ubuntu.com/ubuntu/pool/main/o/openssl/libssl1.1_1.1.1f-1ubuntu2_amd64.deb
+ sudo dpkg -i libssl1.1_1.1.1f-1ubuntu2_amd64.deb
+
+ # 验证安装
+ ldconfig -p | grep libssl
+
+ # 验证 SDK 安装
+ - name: Display .NET info
+ run: dotnet --info
+
+ - name: List installed SDKs
+ run: dotnet --list-sdks
+
+ # 缓存 NuGet 包
+ - name: Cache NuGet packages
+ uses: actions/cache@v4
+ with:
+ path: ~/.nuget/packages
+ key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj') }}
+ restore-keys: |
+ ${{ runner.os }}-nuget-
+
+ # 恢复依赖
+ - name: Restore dependencies
+ run: dotnet restore
+
+ # 编译 Benchmarks 项目
+ - name: Build Benchmarks project
+ run: dotnet build WebApiClientCore.Benchmarks/WebApiClientCore.Benchmarks.csproj --configuration Release --no-restore
+
+ # 执行 netcoreapp3.1 性能测试
+ - name: Run benchmarks on netcoreapp3.1
+ run: |
+ dotnet run --project WebApiClientCore.Benchmarks/WebApiClientCore.Benchmarks.csproj --configuration Release --framework netcoreapp3.1 --no-build -- --filter '*' --exporters json --artifacts ./BenchmarkDotNet.Artifacts.netcoreapp3.1
+ # 复制结果到标准位置
+ if [ -d "BenchmarkDotNet.Artifacts/results" ]; then
+ mkdir -p BenchmarkDotNet.Artifacts.netcoreapp3.1
+ cp -r BenchmarkDotNet.Artifacts/results BenchmarkDotNet.Artifacts.netcoreapp3.1/
+ fi
+ continue-on-error: true
+
+ # 执行 net5.0 性能测试
+ - name: Run benchmarks on net5.0
+ run: |
+ dotnet run --project WebApiClientCore.Benchmarks/WebApiClientCore.Benchmarks.csproj --configuration Release --framework net5.0 --no-build -- --filter '*' --exporters json --artifacts ./BenchmarkDotNet.Artifacts.net5.0
+ if [ -d "BenchmarkDotNet.Artifacts/results" ]; then
+ mkdir -p BenchmarkDotNet.Artifacts.net5.0
+ cp -r BenchmarkDotNet.Artifacts/results BenchmarkDotNet.Artifacts.net5.0/
+ fi
+ continue-on-error: true
+
+ # 执行 net8.0 性能测试
+ - name: Run benchmarks on net8.0
+ run: |
+ dotnet run --project WebApiClientCore.Benchmarks/WebApiClientCore.Benchmarks.csproj --configuration Release --framework net8.0 --no-build -- --filter '*' --exporters json --artifacts ./BenchmarkDotNet.Artifacts.net8.0
+ if [ -d "BenchmarkDotNet.Artifacts/results" ]; then
+ mkdir -p BenchmarkDotNet.Artifacts.net8.0
+ cp -r BenchmarkDotNet.Artifacts/results BenchmarkDotNet.Artifacts.net8.0/
+ fi
+ continue-on-error: true
+
+ # 执行 net10.0 性能测试
+ - name: Run benchmarks on net10.0
+ run: |
+ dotnet run --project WebApiClientCore.Benchmarks/WebApiClientCore.Benchmarks.csproj --configuration Release --framework net10.0 --no-build -- --filter '*' --exporters json --artifacts ./BenchmarkDotNet.Artifacts.net10.0
+ if [ -d "BenchmarkDotNet.Artifacts/results" ]; then
+ mkdir -p BenchmarkDotNet.Artifacts.net10.0
+ cp -r BenchmarkDotNet.Artifacts/results BenchmarkDotNet.Artifacts.net10.0/
+ fi
+ continue-on-error: true
+
+ # AOT 性能测试(暂时禁用)
+ # 原因:BenchmarkDotNet 0.15.7 依赖 CommandLineParser 和反射,无法在 AOT 下运行
+ # 错误:System.InvalidOperationException: Type BenchmarkDotNet.ConsoleArguments.CommandLineOptions appears to be immutable
+ # 等待 BenchmarkDotNet 官方完全支持 Native AOT 后再启用
+ #
+ # - name: Build AOT Benchmark project
+ # run: dotnet publish WebApiClientCore.Benchmarks.Aot/WebApiClientCore.Benchmarks.Aot.csproj --configuration Release --output ./aot-publish
+ # continue-on-error: true
+ #
+ # - name: Run AOT benchmarks on net10.0
+ # if: success()
+ # run: |
+ # cd WebApiClientCore.Benchmarks.Aot
+ # chmod +x ../aot-publish/WebApiClientCore.Benchmarks.Aot
+ # ../aot-publish/WebApiClientCore.Benchmarks.Aot --filter '*' --exporters json --artifacts ./BenchmarkDotNet.Artifacts.net10.0-aot
+ # # 复制结果到根目录
+ # if [ -d "BenchmarkDotNet.Artifacts/results" ]; then
+ # mkdir -p ../BenchmarkDotNet.Artifacts.net10.0-aot
+ # cp -r BenchmarkDotNet.Artifacts/results ../BenchmarkDotNet.Artifacts.net10.0-aot/
+ # fi
+ # cd ..
+ # continue-on-error: true
+
+ # 收集性能测试结果
+ - name: List benchmark results
+ if: always()
+ run: |
+ echo "=== Current Directory ==="
+ pwd
+ echo ""
+ echo "=== Benchmark Results Directories ==="
+ find . -name "BenchmarkDotNet.Artifacts*" -type d
+ echo ""
+ echo "=== All Result Files ==="
+ find . -path "*/BenchmarkDotNet.Artifacts*/results/*" -type f
+ echo ""
+ echo "=== Checking for Markdown reports ==="
+ find . -path "*/BenchmarkDotNet.Artifacts*/results/*-report-github.md" -type f | while read file; do
+ echo "Found: $file"
+ echo "Size: $(du -h "$file" | cut -f1)"
+ done
+
+ # 生成详细的性能对比摘要(同时输出到 Summary 和文件)
+ - name: Generate detailed benchmark summary
+ if: always()
+ run: |
+ chmod +x .github/scripts/generate-benchmark-summary.sh
+
+ # 生成标题和基本信息
+ echo "## Performance Benchmark Results" >> $GITHUB_STEP_SUMMARY
+ echo "" >> $GITHUB_STEP_SUMMARY
+ echo "Benchmark execution completed. Download the artifact for detailed results." >> $GITHUB_STEP_SUMMARY
+ echo "" >> $GITHUB_STEP_SUMMARY
+
+ # 执行汇总脚本,同时输出到 Summary 和文件
+ .github/scripts/generate-benchmark-summary.sh | tee benchmark-summary.md >> $GITHUB_STEP_SUMMARY
+
+ echo "" >> $GITHUB_STEP_SUMMARY
+ echo "---" >> $GITHUB_STEP_SUMMARY
+ echo "" >> $GITHUB_STEP_SUMMARY
+ echo "### 📦 Download Instructions" >> $GITHUB_STEP_SUMMARY
+ echo "" >> $GITHUB_STEP_SUMMARY
+ echo "1. Scroll to the bottom of this page" >> $GITHUB_STEP_SUMMARY
+ echo "2. Find the 'Artifacts' section" >> $GITHUB_STEP_SUMMARY
+ echo "3. Click on **benchmark-results** to download" >> $GITHUB_STEP_SUMMARY
+ echo "" >> $GITHUB_STEP_SUMMARY
+ echo "### 📁 Artifact Contents" >> $GITHUB_STEP_SUMMARY
+ echo "" >> $GITHUB_STEP_SUMMARY
+ echo "- **benchmark-summary.md** - Complete multi-framework comparison (same as above)" >> $GITHUB_STEP_SUMMARY
+ echo "- **BenchmarkDotNet.Artifacts.netcoreapp3.1/results/** - .NET Core 3.1 detailed results" >> $GITHUB_STEP_SUMMARY
+ echo "- **BenchmarkDotNet.Artifacts.net5.0/results/** - .NET 5.0 detailed results" >> $GITHUB_STEP_SUMMARY
+ echo "- **BenchmarkDotNet.Artifacts.net8.0/results/** - .NET 8.0 detailed results" >> $GITHUB_STEP_SUMMARY
+ echo "- **BenchmarkDotNet.Artifacts.net10.0/results/** - .NET 10.0 detailed results" >> $GITHUB_STEP_SUMMARY
+ echo "" >> $GITHUB_STEP_SUMMARY
+ echo "### ⚠️ AOT Benchmark Status" >> $GITHUB_STEP_SUMMARY
+ echo "" >> $GITHUB_STEP_SUMMARY
+ echo "Native AOT benchmark is currently disabled because:" >> $GITHUB_STEP_SUMMARY
+ echo "- BenchmarkDotNet 0.15.7 uses reflection and runtime code generation" >> $GITHUB_STEP_SUMMARY
+ echo "- CommandLineParser library is not AOT-compatible" >> $GITHUB_STEP_SUMMARY
+ echo "- Waiting for official Native AOT support from BenchmarkDotNet" >> $GITHUB_STEP_SUMMARY
+
+ # 上传性能测试报告(在汇总生成之后)
+ - name: Upload benchmark results
+ uses: actions/upload-artifact@v4
+ if: always()
+ with:
+ name: benchmark-results
+ path: |
+ BenchmarkDotNet.Artifacts.*/results/**/*
+ WebApiClientCore.Benchmarks.Aot/BenchmarkDotNet.Artifacts.*/results/**/*
+ WebApiClientCore.Benchmarks/results/**/*
+ benchmark-summary.md
+ retention-days: 30
diff --git a/.github/workflows/ci-build-test.yml b/.github/workflows/ci-build-test.yml
new file mode 100644
index 00000000..4d1dad0f
--- /dev/null
+++ b/.github/workflows/ci-build-test.yml
@@ -0,0 +1,104 @@
+name: CI Build and Test
+
+# 仅支持手动触发
+on:
+ workflow_dispatch:
+
+env:
+ DOTNET_CLI_TELEMETRY_OPTOUT: true
+ DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true
+ DOTNET_NOLOGO: true
+
+jobs:
+ build-and-test:
+ name: Build and Test on ${{ matrix.os }}
+ runs-on: ${{ matrix.os }}
+ timeout-minutes: 45
+ strategy:
+ fail-fast: false
+ matrix:
+ os: [ubuntu-latest, windows-latest, macos-latest]
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ # 安装多个 .NET SDK 版本(从旧到新)
+ - name: Setup .NET Core 3.1
+ uses: actions/setup-dotnet@v4
+ with:
+ dotnet-version: '3.1.x'
+ continue-on-error: ${{ matrix.os == 'macos-latest' }}
+
+ - name: Setup .NET 5.0
+ uses: actions/setup-dotnet@v4
+ with:
+ dotnet-version: '5.0.x'
+
+ - name: Setup .NET 6.0
+ uses: actions/setup-dotnet@v4
+ with:
+ dotnet-version: '6.0.x'
+
+ - name: Setup .NET 7.0
+ uses: actions/setup-dotnet@v4
+ with:
+ dotnet-version: '7.0.x'
+
+ - name: Setup .NET 8.0
+ uses: actions/setup-dotnet@v4
+ with:
+ dotnet-version: '8.0.x'
+
+ - name: Setup .NET 10.0
+ uses: actions/setup-dotnet@v4
+ with:
+ dotnet-version: '10.0.x'
+
+ # 验证 SDK 安装
+ - name: Display .NET info
+ run: dotnet --info
+
+ - name: List installed SDKs
+ run: dotnet --list-sdks
+
+ # 缓存 NuGet 包
+ - name: Cache NuGet packages
+ uses: actions/cache@v4
+ with:
+ path: ~/.nuget/packages
+ key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj') }}
+ restore-keys: |
+ ${{ runner.os }}-nuget-
+
+ # 恢复依赖
+ - name: Restore dependencies
+ run: dotnet restore
+
+ # 编译所有项目(Release 配置)
+ - name: Build all projects
+ run: dotnet build --configuration Release --no-restore
+
+ # 对 net6.0 执行测试
+ - name: Test on net6.0
+ run: dotnet test --configuration Release --framework net6.0 --no-build --verbosity normal --logger "trx;LogFileName=test-results-net6.0.trx"
+ continue-on-error: false
+
+ # 对 net8.0 执行测试
+ - name: Test on net8.0
+ run: dotnet test --configuration Release --framework net8.0 --no-build --verbosity normal --logger "trx;LogFileName=test-results-net8.0.trx"
+ continue-on-error: false
+
+ # 对 net10.0 执行测试
+ - name: Test on net10.0
+ run: dotnet test --configuration Release --framework net10.0 --no-build --verbosity normal --logger "trx;LogFileName=test-results-net10.0.trx"
+ continue-on-error: false
+
+ # 上传测试结果
+ - name: Upload test results
+ uses: actions/upload-artifact@v4
+ if: always()
+ with:
+ name: test-results-${{ matrix.os }}
+ path: '**/TestResults/*.trx'
+ retention-days: 7
diff --git a/.gitignore b/.gitignore
index 1fe04743..0e508407 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,33 +1,75 @@
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
+##
+## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
# User-specific files
+*.rsuser
*.suo
*.user
+*.userosscache
*.sln.docstates
-# Build results
+# User-specific files (MonoDevelop/Xamarin Studio)
+*.userprefs
+# Build results
[Dd]ebug/
+[Dd]ebugPublic/
[Rr]elease/
+[Rr]eleases/
x64/
+x86/
+[Aa][Rr][Mm]/
+[Aa][Rr][Mm]64/
+bld/
[Bb]in/
[Oo]bj/
+[Ll]og/
+
+# Visual Studio 2015/2017 cache/options directory
+.vs/
+# Uncomment if you have tasks that create the project's static files in wwwroot
+#wwwroot/
-# Enable "build/" folder in the NuGet Packages folder since NuGet packages use it for MSBuild targets
-!packages/*/build/
+# Visual Studio 2017 auto generated files
+Generated\ Files/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
+# NUNIT
+*.VisualState.xml
+TestResult.xml
+
+# Build Results of an ATL Project
+[Dd]ebugPS/
+[Rr]eleasePS/
+dlldata.c
+
+# Benchmark Results
+BenchmarkDotNet.Artifacts/
+
+# .NET Core
+project.lock.json
+project.fragment.lock.json
+artifacts/
+
+# StyleCop
+StyleCopReport.xml
+
+# Files built by Visual Studio
*_i.c
*_p.c
+*_h.h
*.ilk
*.meta
*.obj
+*.iobj
*.pch
*.pdb
+*.ipdb
*.pgc
*.pgd
*.rsp
@@ -37,26 +79,40 @@ x64/
*.tlh
*.tmp
*.tmp_proj
+*_wpftmp.csproj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
-*.log
+*.svclog
*.scc
+# Chutzpah Test files
+_Chutzpah*
+
# Visual C++ cache files
ipch/
*.aps
*.ncb
+*.opendb
*.opensdf
*.sdf
*.cachefile
+*.VC.db
+*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
+*.sap
+
+# Visual Studio Trace Files
+*.e2e
+
+# TFS 2012 Local Workspace
+$tf/
# Guidance Automation Toolkit
*.gpState
@@ -64,6 +120,10 @@ ipch/
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
+*.DotSettings.user
+
+# JustCode is a .NET coding add-in
+.JustCode
# TeamCity is a build add-in
_TeamCity*
@@ -71,9 +131,25 @@ _TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
+# AxoCover is a Code Coverage Tool
+.axoCover/*
+!.axoCover/settings.json
+
+# Visual Studio code coverage results
+*.coverage
+*.coveragexml
+
# NCrunch
-*.ncrunch*
+_NCrunch_*
.*crunch*.local.xml
+nCrunchTemp_*
+
+# MightyMoose
+*.mm.*
+AutoTest.Net/
+
+# Web workbench (sass)
+.sass-cache/
# Installshield output folder
[Ee]xpress/
@@ -92,72 +168,181 @@ DocProject/Help/html
publish/
# Publish Web Output
-*.Publish.xml
-
-# NuGet Packages Directory
-## TODO: If you have NuGet Package Restore enabled, uncomment the next line
-#packages/
-
-# Windows Azure Build Output
-csx
+*.[Pp]ublish.xml
+*.azurePubxml
+# Note: Comment the next line if you want to checkin your web deploy settings,
+# but database connection strings (with potential passwords) will be unencrypted
+*.pubxml
+*.publishproj
+
+# Microsoft Azure Web App publish settings. Comment the next line if you want to
+# checkin your Azure Web App publish settings, but sensitive information contained
+# in these scripts will be unencrypted
+PublishScripts/
+
+# NuGet Packages
+*.nupkg
+# The packages folder can be ignored because of Package Restore
+**/[Pp]ackages/*
+# except build/, which is used as an MSBuild target.
+!**/[Pp]ackages/build/
+# Uncomment if necessary however generally it will be regenerated when needed
+#!**/[Pp]ackages/repositories.config
+# NuGet v3's project.json files produces more ignorable files
+*.nuget.props
+*.nuget.targets
+
+# Microsoft Azure Build Output
+csx/
*.build.csdef
-# Windows Store app package directory
+# Microsoft Azure Emulator
+ecf/
+rcf/
+
+# Windows Store app package directories and files
AppPackages/
+BundleArtifacts/
+Package.StoreAssociation.xml
+_pkginfo.txt
+*.appx
+
+# Visual Studio cache files
+# files ending in .cache can be ignored
+*.[Cc]ache
+# but keep track of directories ending in .cache
+!?*.[Cc]ache/
# Others
-sql/
-*.Cache
ClientBin/
-[Ss]tyle[Cc]op.*
~$*
*~
*.dbmdl
-*.[Pp]ublish.xml
+*.dbproj.schemaview
+*.jfm
*.pfx
*.publishsettings
+orleans.codegen.cs
+
+# Including strong name files can present a security risk
+# (https://github.com/github/gitignore/pull/2483#issue-259490424)
+#*.snk
+
+# Since there are multiple workflows, uncomment next line to ignore bower_components
+# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
+#bower_components/
# RIA/Silverlight projects
Generated_Code/
-# Backup & report files from converting an old project file to a newer
-# Visual Studio version. Backup files are not needed, because we have git ;-)
+# Backup & report files from converting an old project file
+# to a newer Visual Studio version. Backup files are not needed,
+# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
+ServiceFabricBackup/
+*.rptproj.bak
# SQL Server files
-App_Data/*.mdf
-App_Data/*.ldf
+*.mdf
+*.ldf
+*.ndf
+
+# Business Intelligence projects
+*.rdl.data
+*.bim.layout
+*.bim_*.settings
+*.rptproj.rsuser
+*- Backup*.rdl
+
+# Microsoft Fakes
+FakesAssemblies/
+
+# GhostDoc plugin setting file
+*.GhostDoc.xml
+
+# Node.js Tools for Visual Studio
+.ntvs_analysis.dat
+node_modules/
+
+# Visual Studio 6 build log
+*.plg
+
+# Visual Studio 6 workspace options file
+*.opt
+
+# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
+*.vbw
+
+# Visual Studio LightSwitch build output
+**/*.HTMLClient/GeneratedArtifacts
+**/*.DesktopClient/GeneratedArtifacts
+**/*.DesktopClient/ModelManifest.xml
+**/*.Server/GeneratedArtifacts
+**/*.Server/ModelManifest.xml
+_Pvt_Extensions
+
+# Paket dependency manager
+.paket/paket.exe
+paket-files/
+
+# FAKE - F# Make
+.fake/
+
+# JetBrains Rider
+.idea/
+*.sln.iml
+
+# CodeRush personal settings
+.cr/personal
+
+# Python Tools for Visual Studio (PTVS)
+__pycache__/
+*.pyc
+
+# Cake - Uncomment if you are using it
+# tools/**
+# !tools/packages.config
+
+# Tabs Studio
+*.tss
+
+# Telerik's JustMock configuration file
+*.jmconfig
+
+# BizTalk build output
+*.btp.cs
+*.btm.cs
+*.odx.cs
+*.xsd.cs
+# OpenCover UI analysis results
+OpenCover/
-#LightSwitch generated files
-GeneratedArtifacts/
-_Pvt_Extensions/
-ModelManifest.xml
+# Azure Stream Analytics local run output
+ASALocalRun/
-# =========================
-# Windows detritus
-# =========================
+# MSBuild Binary and Structured Log
+*.binlog
-# Windows image file caches
-Thumbs.db
-ehthumbs.db
+# NVidia Nsight GPU debugger configuration file
+*.nvuser
-# Folder config file
-Desktop.ini
+# MFractors (Xamarin productivity tool) working folder
+.mfractor/
-# Recycle Bin used on file shares
-$RECYCLE.BIN/
+# Local History for Visual Studio
+.localhistory/
-# Mac desktop service store files
-.DS_Store
-/.vs/WebApiClient/v15/Server/sqlite3
-/packages/
-/.vs/config
+# BeatPulse healthcheck temp database
+healthchecksdb
-/WebApiClient/build/BuildTask
-/.vs
+# nuget
+WebApiClientCore/NugetPackage/analyzers/
+WebApiClientCore.Extensions.SourceGenerator/NugetPackage/analyzers/
+#qoder
+.qoder/
diff --git a/App/.config/dotnet-tools.json b/App/.config/dotnet-tools.json
new file mode 100644
index 00000000..4f257cf0
--- /dev/null
+++ b/App/.config/dotnet-tools.json
@@ -0,0 +1,13 @@
+{
+ "version": 1,
+ "isRoot": true,
+ "tools": {
+ "dotnet-ef": {
+ "version": "8.0.6",
+ "commands": [
+ "dotnet-ef"
+ ],
+ "rollForward": false
+ }
+ }
+}
\ No newline at end of file
diff --git a/App/ApiClients/IUserApi.cs b/App/ApiClients/IUserApi.cs
new file mode 100644
index 00000000..46b2ec6d
--- /dev/null
+++ b/App/ApiClients/IUserApi.cs
@@ -0,0 +1,61 @@
+using App.Models;
+using System.IO;
+using System.Net.Http;
+using System.Threading;
+using System.Threading.Tasks;
+using WebApiClientCore;
+using WebApiClientCore.Attributes;
+using WebApiClientCore.Parameters;
+
+namespace App.Clients
+{
+ ///
+ /// 用户操作接口
+ ///
+ [OAuthToken]
+ [LoggingFilter]
+ public interface IUserApi : IHttpApi
+ {
+ [HttpGet("api/users/{account}")]
+ ITask GetAsync(string account);
+
+ [HttpGet("api/users/{account}")]
+ ITask GetAsStringAsync(string account, CancellationToken token = default);
+
+ [HttpGet("api/users/{account}")]
+ ITask GetAsByteArrayAsync(string account, CancellationToken token = default);
+
+ [HttpGet("api/users/{account}")]
+ ITask GetAsStreamAsync(string account, CancellationToken token = default);
+
+ [HttpGet("api/users/{account}")]
+ ITask GetAsModelAsync(string account, CancellationToken token = default);
+
+ [JsonReturn]
+ [HttpGet("api/users/{account}")]
+ ITask GetExpectJsonAsync(string account, CancellationToken token = default);
+
+ [XmlReturn]
+ [HttpGet("api/users/{account}")]
+ ITask GetExpectXmlAsync(string account, CancellationToken token = default);
+
+
+
+ [HttpPost("api/users/body")]
+ Task PostByJsonAsync([JsonContent] User user, CancellationToken token = default);
+
+ [HttpPost("api/users/body")]
+ Task PostByXmlAsync([XmlContent] User user, CancellationToken token = default);
+
+ [HttpPost("api/users/form")]
+ Task PostByFormAsync([FormContent] User user, CancellationToken token = default);
+
+ [HttpPost("api/users/formdata")]
+ Task PostByFormDataAsync([FormDataContent] User user, FormDataFile file, CancellationToken token = default);
+
+
+
+ [HttpDelete("api/users/{account}")]
+ Task DeleteAsync(string account);
+ }
+}
diff --git a/App/ApiClients/UserHostedService.cs b/App/ApiClients/UserHostedService.cs
new file mode 100644
index 00000000..5ca775c1
--- /dev/null
+++ b/App/ApiClients/UserHostedService.cs
@@ -0,0 +1,35 @@
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
+using Microsoft.Extensions.Logging;
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace App.Clients
+{
+ public class UserHostedService : BackgroundService
+ {
+ private readonly IServiceScopeFactory serviceScopeFactory;
+ private readonly ILogger logger;
+
+ public UserHostedService(IServiceScopeFactory serviceScopeFactory, ILogger logger)
+ {
+ this.serviceScopeFactory = serviceScopeFactory;
+ this.logger = logger;
+ }
+
+ protected override async Task ExecuteAsync(CancellationToken stoppingToken)
+ {
+ try
+ {
+ using var scope = this.serviceScopeFactory.CreateScope();
+ var userService = scope.ServiceProvider.GetRequiredService();
+ await userService.RunRequestsAsync();
+ }
+ catch (Exception ex)
+ {
+ this.logger.LogError(ex, ex.Message);
+ }
+ }
+ }
+}
diff --git a/App/ApiClients/UserService.cs b/App/ApiClients/UserService.cs
new file mode 100644
index 00000000..0e77c99d
--- /dev/null
+++ b/App/ApiClients/UserService.cs
@@ -0,0 +1,52 @@
+using App.Models;
+using System;
+using System.Threading.Tasks;
+using WebApiClientCore;
+using WebApiClientCore.Parameters;
+
+namespace App.Clients
+{
+ public class UserService
+ {
+ private readonly IUserApi userApi;
+
+ public UserService(IUserApi userApi)
+ {
+ this.userApi = userApi;
+ }
+
+ public async Task RunRequestsAsync()
+ {
+ var user = new User
+ {
+ Account = "lao九",
+ Password = "123456",
+ BirthDay = DateTime.Parse("2018-01-01 12:30:30"),
+ Email = "laojiu@webapiclient.com",
+ Gender = Gender.Male
+ };
+
+ // 上传的文件
+ var file = new FormDataFile("TextFile.txt");
+
+ var response = await userApi.GetAsync(account: "get1");
+ var @string = await userApi.GetAsStringAsync(account: "get2");
+ var byteArray = await userApi.GetAsByteArrayAsync(account: "get3");
+ var stream = await userApi.GetAsStreamAsync(account: "get4");
+ var model = await userApi.GetAsModelAsync(account: "get5");
+ var jsonUser = await userApi.GetExpectJsonAsync(account: "json");
+ var xmlUser = await this.userApi.GetExpectXmlAsync(account: "xml");
+
+ var post1 = await userApi.PostByJsonAsync(user);
+ var post2 = await userApi.PostByXmlAsync(user);
+ var post3 = await userApi.PostByFormAsync(user);
+ var post4 = await userApi.PostByFormDataAsync(user, file);
+
+ var retry = await userApi.GetAsync(account: "retry")
+ .Retry(maxCount: 3)
+ .WhenCatch();
+
+ await userApi.DeleteAsync(account: "account");
+ }
+ }
+}
diff --git a/App/App.csproj b/App/App.csproj
new file mode 100644
index 00000000..f2d2abfc
--- /dev/null
+++ b/App/App.csproj
@@ -0,0 +1,33 @@
+
+
+
+ Exe
+ enable
+ netcoreapp3.1;net5.0;net6.0;net7.0;net8.0;net10.0
+ false
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+ PreserveNewest
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/App/Controllers/TokenFilterAttribute.cs b/App/Controllers/TokenFilterAttribute.cs
new file mode 100644
index 00000000..f5101a94
--- /dev/null
+++ b/App/Controllers/TokenFilterAttribute.cs
@@ -0,0 +1,22 @@
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.Filters;
+using System;
+using System.Threading.Tasks;
+
+namespace App.Controllers
+{
+ public class TokenFilterAttribute : Attribute, IAsyncActionFilter
+ {
+ public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
+ {
+ if (context.HttpContext.Request.Headers.ContainsKey("Authorization"))
+ {
+ await next();
+ }
+ else
+ {
+ context.Result = new UnauthorizedResult();
+ }
+ }
+ }
+}
diff --git a/App/Controllers/TokensController.cs b/App/Controllers/TokensController.cs
new file mode 100644
index 00000000..c9ed1db9
--- /dev/null
+++ b/App/Controllers/TokensController.cs
@@ -0,0 +1,23 @@
+using Microsoft.AspNetCore.Mvc;
+using WebApiClientCore.Extensions.OAuths;
+using WebApiClientCore.Extensions.OAuths.TokenProviders;
+
+namespace App.Controllers
+{
+ [ApiController]
+ [Route("api/[controller]")]
+ public class TokensController : ControllerBase
+ {
+ [HttpPost]
+ public TokenResult CreateToken([FromForm] ClientCredentials credentials)
+ {
+ return new TokenResult
+ {
+ Access_token = $"Access_token_{credentials.Client_id}_{credentials.Client_secret}",
+ Expires_in = 60 * 60,
+ Id_token = "id",
+ Token_type = "Bearer"
+ };
+ }
+ }
+}
diff --git a/App/Controllers/UsersController.cs b/App/Controllers/UsersController.cs
new file mode 100644
index 00000000..fc7d7c40
--- /dev/null
+++ b/App/Controllers/UsersController.cs
@@ -0,0 +1,42 @@
+using App.Models;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc;
+
+namespace App.Controllers
+{
+ [TokenFilter]
+ [ApiController]
+ [Route("api/[controller]")]
+ public class UsersController : ControllerBase
+ {
+ [HttpGet("{account}")]
+ public User Get(string account)
+ {
+ return new User { Account = account, Password = "password" };
+ }
+
+
+ [HttpPost("body")]
+ public User PostByBody([FromBody] User bodyUser)
+ {
+ return bodyUser;
+ }
+
+ [HttpPost("form")]
+ public User PostByForm([FromForm] User formUser)
+ {
+ return formUser;
+ }
+
+ [HttpPost("formdata")]
+ public User PostByFormData([FromForm] User formDataUser, IFormFile file)
+ {
+ return formDataUser;
+ }
+
+ [HttpDelete("{account}")]
+ public void Delete(string account)
+ {
+ }
+ }
+}
diff --git a/App/Models/Gender.cs b/App/Models/Gender.cs
new file mode 100644
index 00000000..f4ae9ff6
--- /dev/null
+++ b/App/Models/Gender.cs
@@ -0,0 +1,11 @@
+namespace App.Models
+{
+ ///
+ /// 性别
+ ///
+ public enum Gender
+ {
+ Female = 0,
+ Male = 1
+ }
+}
diff --git a/App/Models/User.cs b/App/Models/User.cs
new file mode 100644
index 00000000..1a475c46
--- /dev/null
+++ b/App/Models/User.cs
@@ -0,0 +1,31 @@
+using System;
+using System.ComponentModel.DataAnnotations;
+using System.Text.Json.Serialization;
+using WebApiClientCore.Serialization.JsonConverters;
+
+namespace App.Models
+{
+ ///
+ /// 表示用户模型
+ ///
+ public class User
+ {
+ [Required]
+ [StringLength(10, MinimumLength = 1)]
+ public string Account { get; set; } = string.Empty;
+
+ [Required]
+ [StringLength(10, MinimumLength = 1)]
+ public string Password { get; set; } = string.Empty;
+
+ public string? NickName { get; set; }
+
+ [JsonDateTime("yyyy年MM月dd日")]
+ public DateTime? BirthDay { get; set; }
+
+ public Gender Gender { get; set; }
+
+ [JsonIgnore]
+ public string? Email { get; set; }
+ }
+}
diff --git a/App/Program.cs b/App/Program.cs
new file mode 100644
index 00000000..0c0f82a7
--- /dev/null
+++ b/App/Program.cs
@@ -0,0 +1,35 @@
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.Extensions.Hosting;
+
+namespace App
+{
+ ///
+ /// 应用程序
+ ///
+ public class Program
+ {
+ ///
+ /// 程序入口
+ ///
+ ///
+ public static void Main(string[] args)
+ {
+ CreateHostBuilder(args).Build().Run();
+ }
+
+ ///
+ /// 创建host
+ ///
+ ///
+ ///
+ public static IHostBuilder CreateHostBuilder(string[] args)
+ {
+ return Host
+ .CreateDefaultBuilder(args)
+ .ConfigureWebHostDefaults(webBuilder =>
+ {
+ webBuilder.UseStartup();
+ });
+ }
+ }
+}
diff --git a/App/Properties/launchSettings.json b/App/Properties/launchSettings.json
new file mode 100644
index 00000000..64c69093
--- /dev/null
+++ b/App/Properties/launchSettings.json
@@ -0,0 +1,12 @@
+{
+ "profiles": {
+ "App": {
+ "commandName": "Project",
+ "launchBrowser": false,
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ },
+ "applicationUrl": "http://localhost:5000"
+ }
+ }
+}
\ No newline at end of file
diff --git a/App/README.md b/App/README.md
new file mode 100644
index 00000000..92ef50e6
--- /dev/null
+++ b/App/README.md
@@ -0,0 +1,10 @@
+## App
+这是应用例子,为了方便,服务端和客户端都在同一个程序进程内
+
+### 服务端
+Controllers 为服务端,TokensController 模拟 token 发放的服务端,UsersController 模拟用户资源服务器
+
+### 客户端
+* ApiClients.IUserApi为WebApiClientCore的声明式接口
+* ApiClients.UserService为包装的服务,注入了IUserApi接口
+* ApiClients.UserHostedService为后台服务,启动时获取UserService实例并运行
diff --git a/App/Startup.cs b/App/Startup.cs
new file mode 100644
index 00000000..0d0a8787
--- /dev/null
+++ b/App/Startup.cs
@@ -0,0 +1,93 @@
+using App.Clients;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
+using System;
+
+namespace App
+{
+ ///
+ /// Æô¶¯ҳ
+ ///
+ public class Startup
+ {
+ ///
+ /// »ñȡÅäÖÃ
+ ///
+ public IConfiguration Configuration { get; }
+
+ ///
+ /// »·¾³
+ ///
+ public IWebHostEnvironment Environment { get; }
+
+ ///
+ /// ¹¹ÔìÆ÷
+ ///
+ ///
+ ///
+ public Startup(IConfiguration configuration, IWebHostEnvironment environment)
+ {
+ Configuration = configuration;
+ Environment = environment;
+ }
+
+
+ ///
+ /// Ìí¼ӷþÎñ
+ ///
+ ///
+ public void ConfigureServices(IServiceCollection services)
+ {
+ // Ìí¼ӿØÖÆÆ÷
+ services.AddControllers().AddXmlSerializerFormatters();
+
+ // ȫ¾ÖĬÈÏÅäÖÃ
+ services
+ .AddWebApiClient()
+ .UseJsonFirstApiActionDescriptor();
+
+ // ע²áuserApi
+ services.AddHttpApi(typeof(IUserApi)).ConfigureHttpApi(o =>
+ {
+ o.HttpHost = new Uri("http://localhost:5000/");
+ });
+
+ // ע²áÓëÅäÖÃclientIdģʽµÄtokenÌáÕßѡÏî
+ services.AddClientCredentialsTokenProvider().Configure(o =>
+ {
+ o.Endpoint = new Uri("http://localhost:5000/api/tokens");
+ o.Credentials.Client_id = "clientId";
+ o.Credentials.Client_secret = "xxyyzz";
+ });
+
+ // userApi¿ͻ§¶˺ǫ́·þÎñ
+ services.AddScoped().AddHostedService();
+ }
+
+ ///
+ /// ÅäÖÃÖмä¼þ
+ ///
+ ///
+ public void Configure(IApplicationBuilder app)
+ {
+ if (Environment.IsDevelopment())
+ {
+ app.UseDeveloperExceptionPage();
+ }
+ else
+ {
+ app.UseHsts();
+ }
+
+ app.UseRouting();
+
+ app.UseEndpoints(endpoints =>
+ {
+ endpoints.MapControllers();
+ });
+ }
+ }
+}
diff --git a/App/TextFile.txt b/App/TextFile.txt
new file mode 100644
index 00000000..b2a64c1c
--- /dev/null
+++ b/App/TextFile.txt
@@ -0,0 +1 @@
+这是上传的文件内容
\ No newline at end of file
diff --git a/App/appsettings.json b/App/appsettings.json
new file mode 100644
index 00000000..9dc9c620
--- /dev/null
+++ b/App/appsettings.json
@@ -0,0 +1,11 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "System": "Warning",
+ "Microsoft": "Warning",
+ "Microsoft.Hosting.Lifetime": "Information"
+ }
+ },
+ "AllowedHosts": "*"
+}
diff --git a/AppAot/AppAot.csproj b/AppAot/AppAot.csproj
new file mode 100644
index 00000000..aacb0716
--- /dev/null
+++ b/AppAot/AppAot.csproj
@@ -0,0 +1,22 @@
+
+
+
+ Exe
+ net10.0
+ false
+ enable
+ true
+ true
+ true
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/AppAot/AppData.cs b/AppAot/AppData.cs
new file mode 100644
index 00000000..69b21421
--- /dev/null
+++ b/AppAot/AppData.cs
@@ -0,0 +1,7 @@
+namespace AppAot
+{
+ public class AppData
+ {
+ public string? WebpackCompilationHash { get; set; }
+ }
+}
diff --git a/AppAot/AppHostedService.cs b/AppAot/AppHostedService.cs
new file mode 100644
index 00000000..02f449b2
--- /dev/null
+++ b/AppAot/AppHostedService.cs
@@ -0,0 +1,30 @@
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
+using Microsoft.Extensions.Logging;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace AppAot
+{
+ class AppHostedService : BackgroundService
+ {
+ private readonly IServiceScopeFactory serviceScopeFactory;
+ private readonly ILogger logger;
+
+ public AppHostedService(
+ IServiceScopeFactory serviceScopeFactory,
+ ILogger logger)
+ {
+ this.serviceScopeFactory = serviceScopeFactory;
+ this.logger = logger;
+ }
+
+ protected override async Task ExecuteAsync(CancellationToken stoppingToken)
+ {
+ using var scope = this.serviceScopeFactory.CreateScope();
+ var api = scope.ServiceProvider.GetRequiredService();
+ var appData = await api.GetAppDataAsync();
+ this.logger.LogInformation($"WebpackCompilationHash: {appData.WebpackCompilationHash}");
+ }
+ }
+}
diff --git a/AppAot/AppJsonSerializerContext.cs b/AppAot/AppJsonSerializerContext.cs
new file mode 100644
index 00000000..f64fe137
--- /dev/null
+++ b/AppAot/AppJsonSerializerContext.cs
@@ -0,0 +1,9 @@
+using System.Text.Json.Serialization;
+
+namespace AppAot
+{
+ [JsonSerializable(typeof(AppData[]))]
+ partial class AppJsonSerializerContext : JsonSerializerContext
+ {
+ }
+}
diff --git a/AppAot/ICloudflareApi.cs b/AppAot/ICloudflareApi.cs
new file mode 100644
index 00000000..3e24eb2d
--- /dev/null
+++ b/AppAot/ICloudflareApi.cs
@@ -0,0 +1,13 @@
+using System.Threading.Tasks;
+using WebApiClientCore.Attributes;
+
+namespace AppAot
+{
+ [LoggingFilter]
+ [HttpHost("https://www.cloudflare-cn.com")]
+ public interface ICloudflareApi
+ {
+ [HttpGet("/page-data/app-data.json")]
+ Task GetAppDataAsync();
+ }
+}
diff --git a/AppAot/Program.cs b/AppAot/Program.cs
new file mode 100644
index 00000000..924666cf
--- /dev/null
+++ b/AppAot/Program.cs
@@ -0,0 +1,27 @@
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
+
+namespace AppAot
+{
+ class Program
+ {
+ static void Main(string[] args)
+ {
+ Host.CreateDefaultBuilder(args)
+ .ConfigureServices(services =>
+ {
+ services
+ .AddWebApiClient()
+ .ConfigureHttpApi(options => // json SG生成器配置
+ {
+ options.PrependJsonSerializerContext(AppJsonSerializerContext.Default);
+ });
+
+ services.AddHttpApi();
+ services.AddHostedService();
+ })
+ .Build()
+ .Run();
+ }
+ }
+}
diff --git a/Demo/BuildTask/about.txt b/Demo/BuildTask/about.txt
deleted file mode 100644
index bf8fc05b..00000000
--- a/Demo/BuildTask/about.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-此目录替代WebApiClient.AOT的nuget包里的build功能
-当配置为AOT_Debug或AOT_Release时,将使用到本目录的编译任务程序集
\ No newline at end of file
diff --git a/Demo/BuildTask/net45/Mono.Cecil.Mdb.dll b/Demo/BuildTask/net45/Mono.Cecil.Mdb.dll
deleted file mode 100644
index b4469538..00000000
Binary files a/Demo/BuildTask/net45/Mono.Cecil.Mdb.dll and /dev/null differ
diff --git a/Demo/BuildTask/net45/Mono.Cecil.Pdb.dll b/Demo/BuildTask/net45/Mono.Cecil.Pdb.dll
deleted file mode 100644
index bff1a30e..00000000
Binary files a/Demo/BuildTask/net45/Mono.Cecil.Pdb.dll and /dev/null differ
diff --git a/Demo/BuildTask/net45/Mono.Cecil.Rocks.dll b/Demo/BuildTask/net45/Mono.Cecil.Rocks.dll
deleted file mode 100644
index 8e3e779b..00000000
Binary files a/Demo/BuildTask/net45/Mono.Cecil.Rocks.dll and /dev/null differ
diff --git a/Demo/BuildTask/net45/Mono.Cecil.dll b/Demo/BuildTask/net45/Mono.Cecil.dll
deleted file mode 100644
index 8275c7bb..00000000
Binary files a/Demo/BuildTask/net45/Mono.Cecil.dll and /dev/null differ
diff --git a/Demo/BuildTask/net45/WebApiClient.BuildTask.dll b/Demo/BuildTask/net45/WebApiClient.BuildTask.dll
deleted file mode 100644
index 6524f065..00000000
Binary files a/Demo/BuildTask/net45/WebApiClient.BuildTask.dll and /dev/null differ
diff --git a/Demo/BuildTask/netcoreapp1.1/Mono.Cecil.Mdb.dll b/Demo/BuildTask/netcoreapp1.1/Mono.Cecil.Mdb.dll
deleted file mode 100644
index baf8887a..00000000
Binary files a/Demo/BuildTask/netcoreapp1.1/Mono.Cecil.Mdb.dll and /dev/null differ
diff --git a/Demo/BuildTask/netcoreapp1.1/Mono.Cecil.Pdb.dll b/Demo/BuildTask/netcoreapp1.1/Mono.Cecil.Pdb.dll
deleted file mode 100644
index ebae6fc5..00000000
Binary files a/Demo/BuildTask/netcoreapp1.1/Mono.Cecil.Pdb.dll and /dev/null differ
diff --git a/Demo/BuildTask/netcoreapp1.1/Mono.Cecil.Rocks.dll b/Demo/BuildTask/netcoreapp1.1/Mono.Cecil.Rocks.dll
deleted file mode 100644
index a03100c1..00000000
Binary files a/Demo/BuildTask/netcoreapp1.1/Mono.Cecil.Rocks.dll and /dev/null differ
diff --git a/Demo/BuildTask/netcoreapp1.1/Mono.Cecil.dll b/Demo/BuildTask/netcoreapp1.1/Mono.Cecil.dll
deleted file mode 100644
index ac32a260..00000000
Binary files a/Demo/BuildTask/netcoreapp1.1/Mono.Cecil.dll and /dev/null differ
diff --git a/Demo/BuildTask/netcoreapp1.1/WebApiClient.BuildTask.deps.json b/Demo/BuildTask/netcoreapp1.1/WebApiClient.BuildTask.deps.json
deleted file mode 100644
index 1ee86f55..00000000
--- a/Demo/BuildTask/netcoreapp1.1/WebApiClient.BuildTask.deps.json
+++ /dev/null
@@ -1,236 +0,0 @@
-{
- "runtimeTarget": {
- "name": ".NETCoreApp,Version=v1.1",
- "signature": "47bb22e04a8cdf0b83d6d23e86a37f67618c9e35"
- },
- "compilationOptions": {},
- "targets": {
- ".NETCoreApp,Version=v1.1": {
- "WebApiClient.BuildTask/0.1.5": {
- "dependencies": {
- "Microsoft.Build.Tasks.Core": "15.1.548",
- "Mono.Cecil": "0.10.0"
- },
- "runtime": {
- "WebApiClient.BuildTask.dll": {}
- }
- },
- "Microsoft.Build.Framework/15.1.548": {
- "runtime": {
- "lib/netstandard1.3/Microsoft.Build.Framework.dll": {
- "assemblyVersion": "15.1.0.0",
- "fileVersion": "15.1.548.43366"
- }
- }
- },
- "Microsoft.Build.Tasks.Core/15.1.548": {
- "dependencies": {
- "Microsoft.Build.Framework": "15.1.548",
- "Microsoft.Build.Utilities.Core": "15.1.548",
- "System.Collections.NonGeneric": "4.0.1",
- "System.Diagnostics.TraceSource": "4.0.0",
- "System.Resources.Writer": "4.0.0",
- "System.Runtime.Serialization.Primitives": "4.1.1",
- "System.Runtime.Serialization.Xml": "4.1.1"
- },
- "runtime": {
- "lib/netstandard1.3/Microsoft.Build.Tasks.Core.dll": {
- "assemblyVersion": "15.1.0.0",
- "fileVersion": "15.1.548.43366"
- }
- }
- },
- "Microsoft.Build.Utilities.Core/15.1.548": {
- "dependencies": {
- "Microsoft.Build.Framework": "15.1.548",
- "System.Collections.NonGeneric": "4.0.1",
- "System.Diagnostics.TraceSource": "4.0.0",
- "System.Runtime.Serialization.Primitives": "4.1.1",
- "System.Runtime.Serialization.Xml": "4.1.1"
- },
- "runtime": {
- "lib/netstandard1.3/Microsoft.Build.Utilities.Core.dll": {
- "assemblyVersion": "15.1.0.0",
- "fileVersion": "15.1.548.43366"
- }
- }
- },
- "Mono.Cecil/0.10.0": {
- "runtime": {
- "lib/netstandard1.3/Mono.Cecil.Mdb.dll": {
- "assemblyVersion": "0.10.0.0",
- "fileVersion": "0.10.0.0"
- },
- "lib/netstandard1.3/Mono.Cecil.Pdb.dll": {
- "assemblyVersion": "0.10.0.0",
- "fileVersion": "0.10.0.0"
- },
- "lib/netstandard1.3/Mono.Cecil.Rocks.dll": {
- "assemblyVersion": "0.10.0.0",
- "fileVersion": "0.10.0.0"
- },
- "lib/netstandard1.3/Mono.Cecil.dll": {
- "assemblyVersion": "0.10.0.0",
- "fileVersion": "0.10.0.0"
- }
- }
- },
- "System.Collections.NonGeneric/4.0.1": {
- "runtime": {
- "lib/netstandard1.3/System.Collections.NonGeneric.dll": {
- "assemblyVersion": "4.0.1.0",
- "fileVersion": "1.0.24212.1"
- }
- }
- },
- "System.Diagnostics.TraceSource/4.0.0": {
- "runtimeTargets": {
- "runtimes/unix/lib/netstandard1.3/System.Diagnostics.TraceSource.dll": {
- "rid": "unix",
- "assetType": "runtime",
- "assemblyVersion": "4.0.0.0",
- "fileVersion": "1.0.24212.1"
- },
- "runtimes/win/lib/netstandard1.3/System.Diagnostics.TraceSource.dll": {
- "rid": "win",
- "assetType": "runtime",
- "assemblyVersion": "4.0.0.0",
- "fileVersion": "1.0.24212.1"
- }
- }
- },
- "System.Private.DataContractSerialization/4.1.1": {
- "dependencies": {
- "System.Runtime.Serialization.Primitives": "4.1.1",
- "System.Xml.XmlSerializer": "4.0.11"
- },
- "runtime": {
- "lib/netstandard1.3/System.Private.DataContractSerialization.dll": {
- "assemblyVersion": "4.1.1.0",
- "fileVersion": "1.0.24212.1"
- }
- }
- },
- "System.Resources.Writer/4.0.0": {
- "runtime": {
- "lib/netstandard1.3/System.Resources.Writer.dll": {
- "assemblyVersion": "4.0.0.0",
- "fileVersion": "1.0.24212.1"
- }
- }
- },
- "System.Runtime.Serialization.Primitives/4.1.1": {
- "runtime": {
- "lib/netstandard1.3/System.Runtime.Serialization.Primitives.dll": {
- "assemblyVersion": "4.1.1.0",
- "fileVersion": "1.0.24212.1"
- }
- }
- },
- "System.Runtime.Serialization.Xml/4.1.1": {
- "dependencies": {
- "System.Private.DataContractSerialization": "4.1.1",
- "System.Runtime.Serialization.Primitives": "4.1.1"
- },
- "runtime": {
- "lib/netstandard1.3/System.Runtime.Serialization.Xml.dll": {
- "assemblyVersion": "4.1.1.0",
- "fileVersion": "1.0.24212.1"
- }
- }
- },
- "System.Xml.XmlSerializer/4.0.11": {
- "runtime": {
- "lib/netstandard1.3/System.Xml.XmlSerializer.dll": {
- "assemblyVersion": "4.0.11.0",
- "fileVersion": "1.0.24212.1"
- }
- }
- }
- }
- },
- "libraries": {
- "WebApiClient.BuildTask/0.1.5": {
- "type": "project",
- "serviceable": false,
- "sha512": ""
- },
- "Microsoft.Build.Framework/15.1.548": {
- "type": "package",
- "serviceable": true,
- "sha512": "sha512-wO17o9zmhAq1YYXdlwXtSsX/LzHgF1mWmKZ/X2CQE8n1WWQnzcm0gqyAa4qpp89emRZ4N0shHwq+43/9IafloA==",
- "path": "microsoft.build.framework/15.1.548",
- "hashPath": "microsoft.build.framework.15.1.548.nupkg.sha512"
- },
- "Microsoft.Build.Tasks.Core/15.1.548": {
- "type": "package",
- "serviceable": true,
- "sha512": "sha512-gWJmhD2pZQbM0VBnxf2kLMIpilaRYfL+odcPVHGRMJxEljnx/68DOcw+rj8csgRLQhADBFsgyyf77Ozyw+zhPQ==",
- "path": "microsoft.build.tasks.core/15.1.548",
- "hashPath": "microsoft.build.tasks.core.15.1.548.nupkg.sha512"
- },
- "Microsoft.Build.Utilities.Core/15.1.548": {
- "type": "package",
- "serviceable": true,
- "sha512": "sha512-gY0QMmhhG9HCDFK7T6xS+XAw/+vTfdumUwq9Sfk0KvWYnHFqrta6rwOYdcDIjDyZ2bKYVFcYH2c40mufjQZIgw==",
- "path": "microsoft.build.utilities.core/15.1.548",
- "hashPath": "microsoft.build.utilities.core.15.1.548.nupkg.sha512"
- },
- "Mono.Cecil/0.10.0": {
- "type": "package",
- "serviceable": true,
- "sha512": "sha512-nHSF7wvyZRPAGXl49zgULPFubXHpsXlOH9RXFRUKb0TX0/tKkKljci6yBszVNI09PIDNQ8IP9WJTYvmBkMbbHw==",
- "path": "mono.cecil/0.10.0",
- "hashPath": "mono.cecil.0.10.0.nupkg.sha512"
- },
- "System.Collections.NonGeneric/4.0.1": {
- "type": "package",
- "serviceable": true,
- "sha512": "sha512-hMxFT2RhhlffyCdKLDXjx8WEC5JfCvNozAZxCablAuFRH74SCV4AgzE8yJCh/73bFnEoZgJ9MJmkjQ0dJmnKqA==",
- "path": "system.collections.nongeneric/4.0.1",
- "hashPath": "system.collections.nongeneric.4.0.1.nupkg.sha512"
- },
- "System.Diagnostics.TraceSource/4.0.0": {
- "type": "package",
- "serviceable": true,
- "sha512": "sha512-6WVCczFZKXwpWpzd/iJkYnsmWTSFFiU24Xx/YdHXBcu+nFI/ehTgeqdJQFbtRPzbrO3KtRNjvkhtj4t5/WwWsA==",
- "path": "system.diagnostics.tracesource/4.0.0",
- "hashPath": "system.diagnostics.tracesource.4.0.0.nupkg.sha512"
- },
- "System.Private.DataContractSerialization/4.1.1": {
- "type": "package",
- "serviceable": true,
- "sha512": "sha512-lcqFBUaCZxPiUkA4dlSOoPZGtZsAuuElH2XHgLwGLxd7ZozWetV5yiz0qGAV2AUYOqw97MtZBjbLMN16Xz4vXA==",
- "path": "system.private.datacontractserialization/4.1.1",
- "hashPath": "system.private.datacontractserialization.4.1.1.nupkg.sha512"
- },
- "System.Resources.Writer/4.0.0": {
- "type": "package",
- "serviceable": true,
- "sha512": "sha512-Hz+ZS81dVSNy93YyJhhL3GwzmMhfcQ8FbUooAt9MO4joIe0vPM4gclv0C82ko1tuN/Kw6CvZFLYkgk6n9xvEkg==",
- "path": "system.resources.writer/4.0.0",
- "hashPath": "system.resources.writer.4.0.0.nupkg.sha512"
- },
- "System.Runtime.Serialization.Primitives/4.1.1": {
- "type": "package",
- "serviceable": true,
- "sha512": "sha512-HZ6Du5QrTG8MNJbf4e4qMO3JRAkIboGT5Fk804uZtg3Gq516S7hAqTm2UZKUHa7/6HUGdVy3AqMQKbns06G/cg==",
- "path": "system.runtime.serialization.primitives/4.1.1",
- "hashPath": "system.runtime.serialization.primitives.4.1.1.nupkg.sha512"
- },
- "System.Runtime.Serialization.Xml/4.1.1": {
- "type": "package",
- "serviceable": true,
- "sha512": "sha512-yqfKHkWUAdI0hdDIdD9KDzluKtZ8IIqLF3O7xIZlt6UTs1bOvFRpCvRTvGQva3Ak/ZM9/nq9IHBJ1tC4Ybcrjg==",
- "path": "system.runtime.serialization.xml/4.1.1",
- "hashPath": "system.runtime.serialization.xml.4.1.1.nupkg.sha512"
- },
- "System.Xml.XmlSerializer/4.0.11": {
- "type": "package",
- "serviceable": true,
- "sha512": "sha512-FrazwwqfIXTfq23mfv4zH+BjqkSFNaNFBtjzu3I9NRmG8EELYyrv/fJnttCIwRMFRR/YKXF1hmsMmMEnl55HGw==",
- "path": "system.xml.xmlserializer/4.0.11",
- "hashPath": "system.xml.xmlserializer.4.0.11.nupkg.sha512"
- }
- }
-}
\ No newline at end of file
diff --git a/Demo/BuildTask/netcoreapp1.1/WebApiClient.BuildTask.dll b/Demo/BuildTask/netcoreapp1.1/WebApiClient.BuildTask.dll
deleted file mode 100644
index e68f988d..00000000
Binary files a/Demo/BuildTask/netcoreapp1.1/WebApiClient.BuildTask.dll and /dev/null differ
diff --git a/Demo/Demo.csproj b/Demo/Demo.csproj
deleted file mode 100644
index a65adef2..00000000
Binary files a/Demo/Demo.csproj and /dev/null differ
diff --git a/Demo/HttpClients/IUserApi.cs b/Demo/HttpClients/IUserApi.cs
deleted file mode 100644
index 57d429d2..00000000
--- a/Demo/HttpClients/IUserApi.cs
+++ /dev/null
@@ -1,86 +0,0 @@
-using System.ComponentModel.DataAnnotations;
-using System.Net.Http;
-using System.Threading;
-using System.Threading.Tasks;
-using WebApiClient;
-using WebApiClient.Attributes;
-using WebApiClient.Parameterables;
-
-namespace Demo.HttpClients
-{
- ///
- /// 用户操作接口
- ///
- [TraceFilter] // 生产环境需要注释掉这个特性
- [HttpHost("http://localhost:9999/")] // HttpHost可以在Config传入覆盖
- public interface IUserApi : IHttpApi
- {
- // GET {url}?account={account}&password={password}&something={something}
- [HttpGet]
- [Timeout(10 * 1000)] // 10s超时
- Task GetAboutAsync(
- [Uri] string url,
- UserInfo user,
- string something);
-
- // /GET webapi/user/GetById?id=id001
- // Return HttpResponseMessage
- [HttpGet("webapi/user/GetById/{id}")]
- [BasicAuth("userName", "password")]
- ITask GetByIdAsync(
- [Required]string id,
- CancellationToken token);
-
- // GET webapi/user/GetByAccount?account=laojiu
- // Return 原始string内容
- [HttpGet("webapi/user/GetByAccount")]
- ITask GetByAccountAsync(
- [Required]string account,
- CancellationToken token);
-
-
- // POST webapi/user/UpdateWithForm
- // Body Account=laojiu&Password=123456&name=value&nickName=老九&age=18
- // Return json或xml内容
- [HttpPost("webapi/user/UpdateWithForm")]
- [FormField("name", "value")] // 固定的参数值可以这么写
- ITask UpdateWithFormAsync(
- [FormContent, Required] UserInfo user,
- [FormField] string nickName,
- [FormField, Range(1, 100)] int? age);
-
- // POST webapi/user/UpdateWithJson
- // Body {"Account":"laojiu","Password":"123456"}
- // Return json或xml内容
- [HttpPost("webapi/user/UpdateWithJson")]
- ITask UpdateWithJsonAsync(
- [JsonContent, Required] UserInfo user);
-
- // POST webapi/user/UpdateWithXml
- // Body laojiu123456
- // Return xml内容
- [HttpPost("webapi/user/UpdateWithXml")]
- ITask UpdateWithXmlAsync(
- [XmlContent, Required] UserInfo user);
-
- // POST webapi/user/UpdateWithMulitpart
- // Body multipart/form-data
- // Return json或xml内容
- [HttpPost("webapi/user/UpdateWithMulitpart")]
- [TraceFilter(Enable = false)]
- ITask UpdateWithMulitpartAsync(
- [MulitpartContent, Required] UserInfo user,
- [MulitpartText] string nickName,
- [MulitpartText] int age,
- MulitpartFile file);
-
- ///
- /// 下载文件
- ///
- /// 文件相对或绝对路径
- ///
- [HttpGet]
- [TraceFilter(Enable = false)]
- ITask DownloadFileAsync([Uri, Required] string uri);
- }
-}
diff --git a/Demo/HttpClients/UserInfo.cs b/Demo/HttpClients/UserInfo.cs
deleted file mode 100644
index 317c3c47..00000000
--- a/Demo/HttpClients/UserInfo.cs
+++ /dev/null
@@ -1,41 +0,0 @@
-using System;
-using System.Text;
-using System.Linq;
-using WebApiClient.DataAnnotations;
-using System.ComponentModel.DataAnnotations;
-
-namespace Demo.HttpClients
-{
- ///
- /// 表示用户模型
- ///
- public class UserInfo
- {
- [Required]
- [StringLength(10, MinimumLength = 1)]
- public string Account { get; set; }
-
- [Required]
- [AliasAs("password")]
- [StringLength(10, MinimumLength = 1)]
- public string Password { get; set; }
-
- [IgnoreWhenNull]
- [DateTimeFormat("yyyy-MM-dd")]
- public DateTime? BirthDay { get; set; }
-
- public Gender Gender { get; set; }
-
- [IgnoreSerialized]
- public string Email { get; set; }
- }
-
- ///
- /// 性别
- ///
- public enum Gender
- {
- Female = 0,
- Male = 1
- }
-}
diff --git a/Demo/HttpServices/HttpPlug.cs b/Demo/HttpServices/HttpPlug.cs
deleted file mode 100644
index cf73b775..00000000
--- a/Demo/HttpServices/HttpPlug.cs
+++ /dev/null
@@ -1,18 +0,0 @@
-using NetworkSocket;
-using System;
-
-namespace Demo.HttpServices
-{
- class HttpPlug : NetworkSocket.Plugs.PlugBase
- {
- protected override void OnConnected(object sender, IContenxt context)
- {
- Console.WriteLine($"HttpServer-> {DateTime.Now} {context.Session}连接..");
- }
-
- protected override void OnDisconnected(object sender, IContenxt context)
- {
- Console.WriteLine($"HttpServer-> {DateTime.Now} {context.Session}断开..");
- }
- }
-}
diff --git a/Demo/HttpServices/HttpServer.cs b/Demo/HttpServices/HttpServer.cs
deleted file mode 100644
index 23a2dfd6..00000000
--- a/Demo/HttpServices/HttpServer.cs
+++ /dev/null
@@ -1,27 +0,0 @@
-using NetworkSocket;
-using NetworkSocket.Http;
-
-namespace Demo.HttpServices
-{
- ///
- /// 表示http服务器
- ///
- static class HttpServer
- {
- ///
- /// http服务器
- ///
- private static readonly TcpListener httpServer = new TcpListener();
-
- ///
- /// 启动Http服务
- ///
- /// 服务端口
- public static void Start(int port = 9999)
- {
- httpServer.Use();
- httpServer.UsePlug();
- httpServer.Start(port);
- }
- }
-}
diff --git a/Demo/HttpServices/UserController.cs b/Demo/HttpServices/UserController.cs
deleted file mode 100644
index 19c64f42..00000000
--- a/Demo/HttpServices/UserController.cs
+++ /dev/null
@@ -1,65 +0,0 @@
-using Demo.HttpClients;
-using NetworkSocket.Http;
-using System.IO;
-using System.Xml.Linq;
-using WebApiClient;
-
-namespace Demo.HttpServices
-{
- ///
- /// User控制器
- ///
- [Route("/webapi/{controller}/{action}")]
- public class UserController : HttpController
- {
- [HttpGet]
- public ActionResult About(UserInfo user, string something)
- {
- return Content("This is from NetworkSocket.Http");
- }
-
- [HttpGet]
- [Route("/webapi/{controller}/{action}/{id}")]
- public JsonResult GetById()
- {
- var id = this.CurrentContext.Action.RouteDatas["id"];
- var model = new UserInfo { Account = id };
- return Json(model);
- }
-
- [HttpGet]
- public JsonResult GetByAccount(string account)
- {
- var model = new UserInfo { Account = account };
- return Json(model);
- }
-
- [HttpPost]
- public JsonResult UpdateWithForm(UserInfo user, string name, string nickName, int? age)
- {
- return Json(user);
- }
-
- [HttpPost]
- public JsonResult UpdateWithJson([Body] UserInfo user)
- {
- return Json(user);
- }
-
- [HttpPost]
- public ActionResult UpdateWithXml()
- {
- var stream = new MemoryStream(Request.Body);
- var xml = XDocument.Load(stream).ToString();
- var user = HttpApiConfig.DefaultXmlFormatter.Deserialize(xml, typeof(UserInfo))as UserInfo;
- user.Email = "laojiu@qq.com";
- return new XmlResult(user);
- }
-
- [HttpPost]
- public ActionResult UpdateWithMulitpart(UserInfo user, string nickName, int? age)
- {
- return Json(user);
- }
- }
-}
diff --git a/Demo/HttpServices/XmlResult.cs b/Demo/HttpServices/XmlResult.cs
deleted file mode 100644
index 643c7d70..00000000
--- a/Demo/HttpServices/XmlResult.cs
+++ /dev/null
@@ -1,32 +0,0 @@
-using NetworkSocket.Http;
-using System.Text;
-using WebApiClient;
-
-namespace Demo.HttpServices
-{
- class XmlResult : ActionResult
- {
- private readonly object data;
-
- private readonly Encoding encoding;
-
- public XmlResult(object data)
- : this(data, null)
- {
- }
-
- public XmlResult(object data, Encoding encoding)
- {
- this.data = data;
- this.encoding = encoding ?? Encoding.UTF8;
- }
-
- public override void ExecuteResult(RequestContext context)
- {
- var xml = HttpApiConfig.DefaultXmlFormatter.Serialize(this.data, this.encoding);
- context.Response.Charset = encoding;
- context.Response.ContentType = "application/xml";
- context.Response.WriteResponse(xml);
- }
- }
-}
diff --git a/Demo/Program.cs b/Demo/Program.cs
deleted file mode 100644
index 5963517d..00000000
--- a/Demo/Program.cs
+++ /dev/null
@@ -1,81 +0,0 @@
-using Demo.HttpClients;
-using Demo.HttpServices;
-using Microsoft.Extensions.Logging;
-using System;
-using System.Threading;
-using System.Threading.Tasks;
-using WebApiClient;
-using WebApiClient.Parameterables;
-
-namespace Demo
-{
- class Program
- {
- static void Main(string[] args)
- {
- Program.Init();
- Program.RequestAsync().Wait();
- Console.ReadLine();
- }
-
- ///
- /// 初始化配置
- ///
- static void Init()
- {
- HttpServer.Start(9999);
-
- // 注册与配置IUserApi接口
- HttpApiFactory.Add().ConfigureHttpApiConfig(c =>
- {
- c.HttpHost = new Uri("http://localhost:9999/");
- c.LoggerFactory = new LoggerFactory().AddConsole();
- c.FormatOptions.DateTimeFormat = DateTimeFormats.ISO8601_WithMillisecond;
- });
- }
-
- ///
- /// 请求接口
- ///
- ///
- private static async Task RequestAsync()
- {
- // userApi由HttpApiFactory创建,自动接管其生命周期
- // 不应该将其做为全局变量缓存起来
- var userApi = HttpApiFactory.Create();
-
- var user = new UserInfo
- {
- Account = "laojiu",
- Password = "123456",
- BirthDay = DateTime.Parse("2018-01-01 12:30:30"),
- Email = "laojiu@webapiclient.com",
- Gender = Gender.Male
- };
-
- var about = await userApi
- .GetAboutAsync("webapi/user/about", user, "somevalue");
-
- var user1 = await userApi
- .GetByIdAsync("id001", CancellationToken.None);
-
- var user2 = await userApi
- .GetByAccountAsync("laojiu", CancellationToken.None);
-
- var user3 = await userApi
- .UpdateWithFormAsync(user, nickName: "老九", age: 18)
- .Retry(3, i => TimeSpan.FromSeconds(i))
- .WhenCatch();
-
- var user4 = await userApi
- .UpdateWithJsonAsync(user);
-
- var user5 = await userApi
- .UpdateWithXmlAsync(user).HandleAsDefaultWhenException();
-
- var file = new MulitpartFile("file.data");
- var user6 = await userApi
- .UpdateWithMulitpartAsync(user, "老九", 18, file);
- }
- }
-}
diff --git a/Demo/TokenFilter.cs b/Demo/TokenFilter.cs
deleted file mode 100644
index 785570d6..00000000
--- a/Demo/TokenFilter.cs
+++ /dev/null
@@ -1,63 +0,0 @@
-using System;
-using System.Threading.Tasks;
-using WebApiClient.AuthTokens;
-
-namespace Demo
-{
- ///
- /// 表示提供client_credentials方式的token过滤器
- ///
- public class TokenFilter : AuthTokenFilter
- {
- ///
- /// 获取提供Token获取的Url节点
- ///
- public string TokenEndpoint { get; private set; }
-
- ///
- /// 获取client_id
- ///
- public string ClientId { get; private set; }
-
- ///
- /// 获取client_secret
- ///
- public string ClientSecret { get; private set; }
-
- ///
- /// OAuth授权的token过滤器
- ///
- /// 提供Token获取的Url节点
- /// 客户端id
- /// 客户端密码
- public TokenFilter(string tokenEndPoint, string client_id, string client_secret)
- {
- this.TokenEndpoint = tokenEndPoint ?? throw new ArgumentNullException(nameof(tokenEndPoint));
- this.ClientId = client_id ?? throw new ArgumentNullException(nameof(client_id));
- this.ClientSecret = client_secret ?? throw new ArgumentNullException(nameof(client_secret));
- }
-
- ///
- /// 请求获取token
- /// 可以使用TokenClient来请求
- ///
- ///
- protected override async Task RequestTokenResultAsync()
- {
- var tokenClient = new TokenClient(this.TokenEndpoint);
- return await tokenClient.RequestClientCredentialsAsync(this.ClientId, this.ClientSecret);
- }
-
- ///
- /// 请求刷新token
- /// 可以使用TokenClient来刷新
- ///
- /// 获取token时返回的refresh_token
- ///
- protected override async Task RequestRefreshTokenAsync(string refresh_token)
- {
- var tokenClient = new TokenClient(this.TokenEndpoint);
- return await tokenClient.RequestRefreshTokenAsync(this.ClientId, this.ClientSecret, refresh_token);
- }
- }
-}
diff --git a/Demo/file.data b/Demo/file.data
deleted file mode 100644
index 434ce334..00000000
--- a/Demo/file.data
+++ /dev/null
@@ -1 +0,0 @@
-这是要上传的文件内容
\ No newline at end of file
diff --git a/Directory.Build.props b/Directory.Build.props
new file mode 100644
index 00000000..61fae1ac
--- /dev/null
+++ b/Directory.Build.props
@@ -0,0 +1,37 @@
+
+
+ 2.1.6
+ Copyright © laojiu 2017-2026
+ IDE0290;NETSDK1138
+
+
+
+ laojiu
+ laojiu
+ latest
+ true
+ true
+ MIT
+ https://github.com/dotnetcore/WebApiClient
+ icon.png
+ true
+
+
+
+ True
+ snupkg
+ true
+ true
+
+
+
+
+
+
+
+
+ True
+ \
+
+
+
\ No newline at end of file
diff --git a/LICENSE b/LICENSE
index ea99c7d9..b9c55fcf 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,6 +1,6 @@
MIT License
-Copyright (c) 2018 老九
+Copyright (c) 2020 .NET Core Community
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/README.md b/README.md
index f5b1206a..f03a11d4 100644
--- a/README.md
+++ b/README.md
@@ -1,72 +1,32 @@
-## WebApiClient
-### 1 Nuget包
+[README](README.md) | [中文文档](README_zh.md)
-> WebApiClient.JIT
+## WebApiClient
+A REST API library with better functionality, performance, and scalability than refit.
- PM> install-package WebApiClient.JIT
-* 可以在项目中直接引用WebApiClient.JIT.dll就能使用;
-* 不适用于不支持JIT技术的平台(IOS、UWP);
-* 接口要求为public;
+### Features
+#### Semantic Declaration
+Client development only requires semantic declaration of C# interfaces.
+#### Diverse serialization
+Supports json, xml, form and other custom serialization methods.
-> WebApiClient.AOT
+#### Full trimmed and AOT
+Supports full trimmed and AOT publishing of .NET8.
- PM> install-package WebApiClient.AOT
-* 项目必须使用nuget安装WebApiClient.AOT才能正常使用;
-* 没有JIT,支持的平台广泛;
-* 接口不要求为public,可以嵌套在类里面;
+#### Aspect-Oriented Programming
+Supports multiple interceptors, filters, logs, retries, custom caches and other aspects.
+#### Code Syntax Analysis
+Provides syntax analysis and prompts for interface code declarations to help developers avoid using improper syntax when declaring interfaces.
+#### Quick access
+Supports OAuth2 and token management extension packages to facilitate identity authentication and authorization.
-### 2. Http请求
-> 接口的声明
+#### Swagger to code
+Supports parsing local or remote OpenApi documents to generate WebApiClientCore interface code, which simplifies the workload of interface declaration.
-```c#
-[HttpHost("http://www.webapiclient.com")]
-public interface IMyWebApi : IHttpApi
-{
- // GET webapi/user?account=laojiu
- // Return 原始string内容
- [HttpGet("/webapi/user")]
- ITask GetUserByAccountAsync(string account);
-
- // POST webapi/user
- // Body Account=laojiu&password=123456
- // Return json或xml内容
- [HttpPost("/webapi/user")]
- ITask UpdateUserWithFormAsync([FormContent] UserInfo user);
-}
-
-public class UserInfo
-{
- public string Account { get; set; }
-
- [AliasAs("password")]
- public string Password { get; set; }
-
- [IgnoreSerialized]
- public string Email { get; set; }
-}
-```
-
-> 接口的调用
-
-```c#
-var client = HttpApiClient.Create();
-var user = new UserInfo { Account = "laojiu", Password = "123456" };
-var user1 = await client.GetUserByAccountAsync("laojiu");
-var user2 = await client.UpdateUserWithFormAsync(user);
-```
-
-
-#### 3. Wiki文档
-1. [WebApiClient基础](https://github.com/xljiulang/WebApiClient/wiki/WebApiClient%E5%9F%BA%E7%A1%80)
-2. [WebApiClient进阶](https://github.com/dotnetcore/WebApiClient/wiki/WebApiClient%E8%BF%9B%E9%98%B6)
-3. [WebApiClient高级](https://github.com/xljiulang/WebApiClient/wiki/WebApiClient%E9%AB%98%E7%BA%A7)
-4. [WebApiClient.Extensions](https://github.com/xljiulang/WebApiClient.Extensions)
-5. [WebApiClient.Tools.Swagger](https://github.com/xljiulang/WebApiClient.Tools)
-
-#### 4. 联系方式
-1. 加Q群439800853 注明WeApiClient
-2. 邮箱366193849@qq.com,不重要的尽量不要发
+#### Powerful performance
+In [BenchmarkDotNet](WebApiClientCore.Benchmarks/results), the performance is 2.X times ahead of the similar product [refit](https://github.com/reactiveui/refit) under various requests.
+### Documentation support
+[https://webapiclient.github.io/](https://webapiclient.github.io/en/)
diff --git a/README_zh.md b/README_zh.md
new file mode 100644
index 00000000..ee68c603
--- /dev/null
+++ b/README_zh.md
@@ -0,0 +1,32 @@
+[README](README.md) | [中文文档](README_zh.md)
+
+## WebApiClient
+一个在功能、性能和可扩展性均优于 refit 的 REST API 库
+
+### 功能特性
+#### 语义化声明
+客户端的开发,只需语义化的声明接口。
+
+#### 多样序列化
+支持json、xml、form等序列化和其它自定义序列化方式。
+
+#### 裁剪与AOT
+支持.net8的代码完全裁剪和AOT发布。
+
+#### 面向切面
+支持多种拦截器、过滤器、日志、重试、缓存自定义等功能。
+
+#### 语法分析
+提供接口声明的语法分析与提示,帮助开发者声明接口时避免使用不当的语法。
+
+#### 快速接入
+支持OAuth2与token管理扩展包,方便实现身份认证和授权。
+
+#### 自动代码
+支持将本地或远程OpenApi文档解析生成WebApiClientCore接口代码的dotnet tool,简化接口声明的工作量
+
+#### 性能强劲
+在[BenchmarkDotNet](WebApiClientCore.Benchmarks/results)中,各种请求下2.X倍性能领先于同类产品[refit](https://github.com/reactiveui/refit)。
+
+### 文档支持
+https://webapiclient.github.io/
diff --git a/WebApiClient.BuildTask/CeAssembly.cs b/WebApiClient.BuildTask/CeAssembly.cs
deleted file mode 100644
index 235065cc..00000000
--- a/WebApiClient.BuildTask/CeAssembly.cs
+++ /dev/null
@@ -1,139 +0,0 @@
-using Mono.Cecil;
-using System;
-using System.IO;
-using System.Linq;
-
-namespace WebApiClient.BuildTask
-{
- ///
- /// 表示程序集
- ///
- class CeAssembly : IDisposable
- {
- ///
- /// 日志
- ///
- private readonly Logger logger;
-
- ///
- /// 程序集
- ///
- private readonly AssemblyDefinition assembly;
-
- ///
- /// 获取所有已知类型
- ///
- public TypeDefinition[] KnowTypes { get; private set; }
-
- ///
- /// 获取主模块
- ///
- public ModuleDefinition MainMdule { get; private set; }
-
- ///
- /// 程序集
- ///
- /// 文件路径
- /// 依赖项搜索目录
- /// 日志
- ///
- public CeAssembly(string fileName, string[] searchDirectories, Logger logger)
- {
- if (File.Exists(fileName) == false)
- {
- throw new FileNotFoundException("找不到文件编译输出的程序集");
- }
-
- var resolver = new DefaultAssemblyResolver();
- foreach (var dir in searchDirectories)
- {
- logger.Message("添加搜索目录", dir);
- resolver.AddSearchDirectory(dir);
- }
-
- var parameter = new ReaderParameters
- {
- ReadWrite = true,
- ReadSymbols = true,
- AssemblyResolver = resolver
- };
-
- this.logger = logger;
- this.assembly = AssemblyDefinition.ReadAssembly(fileName, parameter);
-
- this.MainMdule = this.assembly.MainModule;
- this.KnowTypes = this.MainMdule
- .AssemblyReferences
- .Select(asm => resolver.Resolve(asm).MainModule)
- .SelectMany(item => item.GetTypes())
- .ToArray();
- }
-
- ///
- /// 写入代理类型
- ///
- ///
- public bool WirteProxyTypes()
- {
- var httpApiInterfaces = this.assembly
- .MainModule
- .GetTypes()
- .Select(item => new CeInterface(this, item))
- .Where(item => item.IsHttpApiInterface())
- .ToArray();
-
- var willSave = false;
- foreach (var @interface in httpApiInterfaces)
- {
- var proxyType = new CeProxyType(this, @interface).Build();
- if (this.IsDefinded(proxyType) == true)
- {
- continue;
- }
-
- this.logger.Message("正在写入IL", proxyType.FullName);
- if (proxyType.DeclaringType != null)
- {
- proxyType.DeclaringType.NestedTypes.Add(proxyType);
- }
- else
- {
- this.assembly.MainModule.Types.Add(proxyType);
- }
- willSave = true;
- }
-
- if (willSave == true)
- {
- this.logger.Message("正在保存修改", this.assembly.FullName);
- var parameters = new WriterParameters
- {
- WriteSymbols = true
- };
- this.assembly.Write(parameters);
- }
- return willSave;
- }
-
- ///
- /// 返回类型是否在模块中已声明
- ///
- /// 类型
- ///
- public bool IsDefinded(TypeDefinition typeDefinition)
- {
- return this.assembly
- .MainModule
- .GetTypes()
- .Any(item => item.FullName == typeDefinition.FullName);
- }
-
- ///
- /// 释放资源
- ///
- public void Dispose()
- {
- this.assembly.Dispose();
- }
- }
-}
diff --git a/WebApiClient.BuildTask/CeInterface.cs b/WebApiClient.BuildTask/CeInterface.cs
deleted file mode 100644
index fe17fbb5..00000000
--- a/WebApiClient.BuildTask/CeInterface.cs
+++ /dev/null
@@ -1,229 +0,0 @@
-using Mono.Cecil;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading.Tasks;
-
-namespace WebApiClient.BuildTask
-{
- ///
- /// 表示接口
- ///
- class CeInterface : CeMetadata
- {
- ///
- /// 获取接口类型
- ///
- public TypeDefinition Type { get; private set; }
-
- ///
- /// 表示接口
- ///
- /// 程序集
- /// 接口类型
- public CeInterface(CeAssembly assembly, TypeDefinition @interface)
- : base(assembly)
- {
- this.Type = @interface;
- }
-
- ///
- /// 返回是否为继承IHttpApi的接口
- ///
- ///
- public bool IsHttpApiInterface()
- {
- if (this.Type.IsInterface == false)
- {
- return false;
- }
- return this.Type.Interfaces.Any(i => this.IsTypeEquals(i.InterfaceType, typeof(IHttpApi)));
- }
-
- ///
- /// 生成对应的代理类型
- ///
- /// 类型名称前缀
- ///
- public TypeDefinition MakeProxyType(string prefix = "$")
- {
- var @namespace = this.Type.Namespace;
- var proxyTypeName = $"{prefix}{this.Type.Name}";
- var classAttributes = this.GetProxyTypeAttributes();
- var baseType = this.ImportType();
-
- var proxyType = new TypeDefinition(@namespace, proxyTypeName, classAttributes, baseType)
- {
- DeclaringType = this.Type.DeclaringType
- };
-
- // 继承的接口,泛型接口就声明泛型类
- if (this.Type.HasGenericParameters == true)
- {
- var genericParameters = this.Type
- .GenericParameters
- .Select(p => this.MakeGenericParameter(p, proxyType))
- .ToArray();
-
- var genericInterface = new GenericInstanceType(this.Type);
- foreach (var arg in genericParameters)
- {
- proxyType.GenericParameters.Add(arg);
- genericInterface.GenericArguments.Add(arg);
- }
- proxyType.Interfaces.Add(new InterfaceImplementation(genericInterface));
- }
- else
- {
- proxyType.Interfaces.Add(new InterfaceImplementation(this.Type));
- }
- return proxyType;
- }
-
- ///
- /// 生成代理类的泛型参数
- ///
- /// 代理类型
- ///
- private GenericParameter MakeGenericParameter(GenericParameter source, TypeDefinition proxyType)
- {
- var parameter = new GenericParameter(source.Name, proxyType)
- {
- Attributes = source.Attributes
- & ~GenericParameterAttributes.Covariant
- & ~GenericParameterAttributes.Contravariant
- };
-
- foreach (var type in source.Constraints)
- {
- parameter.Constraints.Add(type);
- }
- return parameter;
- }
-
- ///
- /// 返回代理类型的可见性
- ///
- ///
- private TypeAttributes GetProxyTypeAttributes()
- {
- if (this.Type.IsNestedPrivate)
- {
- return TypeAttributes.NestedPrivate | TypeAttributes.BeforeFieldInit;
- }
- if (this.Type.IsNestedAssembly)
- {
- return TypeAttributes.NestedAssembly | TypeAttributes.BeforeFieldInit;
- }
- if (this.Type.IsNestedPublic)
- {
- return TypeAttributes.NestedPublic | TypeAttributes.BeforeFieldInit;
- }
- if (this.Type.IsPublic)
- {
- return TypeAttributes.Public;
- }
- return TypeAttributes.NotPublic;
- }
-
- ///
- /// 获取接口类型及其继承的接口的所有方法
- /// 忽略IHttpApi接口的方法
- ///
- ///
- ///
- public MethodDefinition[] GetAllApis()
- {
- var excepts = this.ImportType()
- .Resolve()
- .Interfaces
- .Select(item => item.InterfaceType.Resolve());
-
- var interfaces = new[] { this.Type }.Concat(this.Type.Interfaces.Select(i => i.InterfaceType.Resolve()))
- .Except(excepts, TypeDefinitionComparer.Instance)
- .ToArray();
-
- var apiMethods = interfaces
- .SelectMany(item => item.Methods)
- .OrderBy(item => base.GetMethodFullName(item))
- .ToArray();
-
- foreach (var method in apiMethods)
- {
- this.EnsureApiMethod(method);
- }
- return apiMethods;
- }
-
- ///
- /// 确保方法是支持的Api接口
- ///
- ///
- private void EnsureApiMethod(MethodDefinition method)
- {
- if (method.HasGenericParameters == true)
- {
- throw new NotSupportedException($"不支持泛型方法:{method}");
- }
-
- if (method.IsSpecialName == true)
- {
- throw new NotSupportedException($"不支持属性访问器:{method}");
- }
-
- var genericType = method.ReturnType;
- if (genericType.IsGenericInstance == true)
- {
- genericType = genericType.GetElementType();
- }
-
- var isTaskType = this.IsTypeEquals(genericType, typeof(Task<>)) || this.IsTypeEquals(genericType, typeof(ITask<>));
- if (isTaskType == false)
- {
- var message = $"返回类型必须为Task<>或ITask<>:{method}";
- throw new NotSupportedException(message);
- }
-
- foreach (var parameter in method.Parameters)
- {
- if (parameter.ParameterType.IsByReference == true)
- {
- var message = $"接口参数不支持ref/out修饰:{parameter}";
- throw new NotSupportedException(message);
- }
- }
- }
-
- ///
- /// TypeDefinition比较器
- ///
- private class TypeDefinitionComparer : IEqualityComparer
- {
- ///
- /// 获取唯一实例
- ///
- public static readonly TypeDefinitionComparer Instance = new TypeDefinitionComparer();
-
- ///
- /// 是否相等
- ///
- ///
- ///
- ///
- public bool Equals(TypeDefinition x, TypeDefinition y)
- {
- return true;
- }
-
- ///
- /// 返回哈希值
- ///
- ///
- ///
- public int GetHashCode(TypeDefinition obj)
- {
- return obj.FullName.GetHashCode();
- }
- }
- }
-}
diff --git a/WebApiClient.BuildTask/CeMetadata.cs b/WebApiClient.BuildTask/CeMetadata.cs
deleted file mode 100644
index 430862ef..00000000
--- a/WebApiClient.BuildTask/CeMetadata.cs
+++ /dev/null
@@ -1,191 +0,0 @@
-using Mono.Cecil;
-using System;
-using System.Linq;
-using System.Reflection;
-using System.Text;
-
-namespace WebApiClient.BuildTask
-{
- ///
- /// 表示cecil元数据抽象
- ///
- abstract class CeMetadata
- {
- ///
- /// 所在程序集
- ///
- private readonly ModuleDefinition module;
-
- ///
- /// 所有已知类型
- ///
- private readonly TypeDefinition[] knowTypes;
-
- ///
- /// 获取系统类型
- ///
- public TypeSystem TypeSystem { get; private set; }
-
- ///
- /// cecil元数据抽象
- ///
- /// 所在程序集
- public CeMetadata(CeAssembly assembly)
- {
- this.module = assembly.MainMdule;
- this.knowTypes = assembly.KnowTypes;
- this.TypeSystem = this.module.TypeSystem;
- }
-
- ///
- /// 返回导入外部类型后的类型
- ///
- /// 目标类型
- ///
- ///
- ///
- protected TypeReference ImportType()
- {
- var type = typeof(T);
- if (type.IsArray == true)
- {
- throw new NotSupportedException("不支持数组类型");
- }
-
- var knowType = this.knowTypes.FirstOrDefault(item => item.FullName == type.FullName);
- if (knowType == null)
- {
- // 本程序集的类型不作直接导入
- if (this.IsThisAssemblyType(type) == true)
- {
- throw new TypeLoadException($"找不到类型:{type.FullName}");
- }
- return this.module.ImportReference(type);
- }
- else
- {
- return this.module.ImportReference(knowType);
- }
- }
-
-
- ///
- /// 返回指定类型是在本程序集范围内
- ///
- /// 类型
- ///
- private bool IsThisAssemblyType(Type type)
- {
-#if NETCOREAPP1_1
- return type.GetTypeInfo().Assembly == this.GetType().GetTypeInfo().Assembly;
-#else
- return type.Assembly == this.GetType().Assembly;
-#endif
- }
-
-
- ///
- /// 返回导入外部类型的指定方法后的方法
- ///
- /// 方法名
- ///
- ///
- ///
- ///
- protected MethodReference ImportMethod(string methodName)
- {
- return this.ImportMethod(item => item.Name == methodName);
- }
-
- ///
- /// 返回导入外部类型的指定方法后的方法
- ///
- /// 方法过滤器
- ///
- ///
- ///
- ///
- ///
- protected MethodReference ImportMethod(Func filter)
- {
- if (filter == null)
- {
- throw new ArgumentNullException(nameof(filter));
- }
-
- var method = this.ImportType().Resolve().Methods.FirstOrDefault(filter);
- if (method == null)
- {
- throw new ArgumentException("无法找到指定的方法");
- }
- return this.module.ImportReference(method);
- }
-
- ///
- /// 比较两类型类型是一样
- ///
- /// 类型
- /// 目标类型
- ///
- ///
- protected bool IsTypeEquals(TypeReference source, Type target)
- {
- if (source == null)
- {
- throw new ArgumentNullException(nameof(source));
- }
-
- if (target == null)
- {
- throw new ArgumentNullException(nameof(target));
- }
- return source.FullName == target.FullName;
- }
-
- ///
- /// 返回方法的完整名称
- ///
- /// 方法
- ///
- protected string GetMethodFullName(MethodReference method)
- {
- var builder = new StringBuilder();
- foreach (var p in method.Parameters)
- {
- if (builder.Length > 0)
- {
- builder.Append(",");
- }
- builder.Append(GetTypeName(p.ParameterType));
- }
- var insert = $"{GetTypeName(method.ReturnType)} {method.Name}(";
- return builder.Insert(0, insert).Append(")").ToString();
- }
-
- ///
- /// 返回类型不含namespace的名称
- ///
- /// 类型
- ///
- private static string GetTypeName(TypeReference type)
- {
- if (type.IsGenericInstance == false)
- {
- return type.Name;
- }
-
- var builder = new StringBuilder();
- var parameters = ((GenericInstanceType)type).GenericArguments;
- foreach (var p in parameters)
- {
- if (builder.Length > 0)
- {
- builder.Append(",");
- }
- builder.Append(GetTypeName(p));
- }
-
- return builder.Insert(0, $"{type.Name}<").Append(">").ToString();
- }
- }
-}
diff --git a/WebApiClient.BuildTask/CeProxyType.cs b/WebApiClient.BuildTask/CeProxyType.cs
deleted file mode 100644
index 8a4b7597..00000000
--- a/WebApiClient.BuildTask/CeProxyType.cs
+++ /dev/null
@@ -1,190 +0,0 @@
-using Mono.Cecil;
-using Mono.Cecil.Cil;
-using Mono.Cecil.Rocks;
-using System.Linq;
-using System.Reflection;
-
-namespace WebApiClient.BuildTask
-{
- ///
- /// 表示接口的代理类型
- ///
- class CeProxyType : CeMetadata
- {
- ///
- /// 接口
- ///
- private readonly CeInterface @interface;
-
- ///
- /// 代理类型生成器
- ///
- /// 程序集
- /// 接口
- public CeProxyType(CeAssembly assembly, CeInterface @interface)
- : base(assembly)
- {
- this.@interface = @interface;
- }
-
- ///
- /// 转换为TypeDefinition
- ///
- ///
- public TypeDefinition Build()
- {
- var proxyType = this.@interface.MakeProxyType();
- var interceptorType = this.ImportType();
- var apiMethodsType = this.ImportType().MakeArrayType();
-
- var fieldInterceptor = this.BuildField(proxyType, "interceptor", interceptorType);
- var fieldApiMethods = this.BuildField(proxyType, "apiMethods", apiMethodsType);
- this.BuildCtor(proxyType, fieldInterceptor, fieldApiMethods);
-
- var apiMethos = this.@interface.GetAllApis();
- this.BuildMethods(proxyType, apiMethos, fieldInterceptor, fieldApiMethods);
-
- return proxyType;
- }
-
-
- ///
- /// 生成代理类型的字段
- ///
- /// 代理类型
- /// 字段名称
- /// 字段类型
- ///
- private FieldDefinition BuildField(TypeDefinition proxyType, string fieldName, TypeReference type)
- {
- const Mono.Cecil.FieldAttributes filedAttribute = Mono.Cecil.FieldAttributes.Private | Mono.Cecil.FieldAttributes.InitOnly; ;
- var filed = new FieldDefinition(fieldName, filedAttribute, type);
- proxyType.Fields.Add(filed);
- return filed;
- }
-
- ///
- /// 生成代理类型的构造器
- ///
- /// 代理类型
- /// 拦截器字段
- /// 接口方法集合字段
- ///
- private void BuildCtor(TypeDefinition proxyType, FieldDefinition fieldInterceptor, FieldDefinition fieldApiMethods)
- {
- // void .ctor(IApiInterceptor interceptor, MethodInfo[] methods):base(interceptor)
- var ctor = new MethodDefinition(".ctor", Mono.Cecil.MethodAttributes.Public, this.TypeSystem.Void)
- {
- Attributes = Mono.Cecil.MethodAttributes.Public
- | Mono.Cecil.MethodAttributes.HideBySig
- | Mono.Cecil.MethodAttributes.SpecialName
- | Mono.Cecil.MethodAttributes.RTSpecialName
- };
-
- var parameterInterceptor = new ParameterDefinition(fieldInterceptor.FieldType);
- var parameterApiMethods = new ParameterDefinition(fieldApiMethods.FieldType);
- ctor.Parameters.Add(parameterInterceptor);
- ctor.Parameters.Add(parameterApiMethods);
-
- var il = ctor.Body.GetILProcessor();
-
- il.Emit(OpCodes.Ldarg_0);
- il.Emit(OpCodes.Ldarg_1);
-
- var baseConstructor = this.ImportMethod(item => item.IsConstructor);
- il.Emit(OpCodes.Call, baseConstructor);
-
- // this.interceptor = 第一个参数
- il.Emit(OpCodes.Ldarg_0);
- il.Emit(OpCodes.Ldarg_1);
- il.Emit(OpCodes.Stfld, fieldInterceptor);
-
- // this.apiMethods = 第二个参数
- il.Emit(OpCodes.Ldarg_0);
- il.Emit(OpCodes.Ldarg_2);
- il.Emit(OpCodes.Stfld, fieldApiMethods);
-
- il.Emit(OpCodes.Ret);
-
- proxyType.Methods.Add(ctor);
- }
-
- ///
- /// 生成代理类型的接口实现方法
- ///
- /// 代理类型
- /// 接口方法集合
- /// 拦截器字段
- /// 接口方法集合字段
- private void BuildMethods(TypeDefinition proxyType, MethodDefinition[] apiMethods, FieldDefinition fieldInterceptor, FieldDefinition fieldApiMethods)
- {
- const Mono.Cecil.MethodAttributes implementAttribute = Mono.Cecil.MethodAttributes.Public
- | Mono.Cecil.MethodAttributes.Virtual
- | Mono.Cecil.MethodAttributes.Final
- | Mono.Cecil.MethodAttributes.NewSlot
- | Mono.Cecil.MethodAttributes.HideBySig;
-
- for (var i = 0; i < apiMethods.Length; i++)
- {
- var apiMethod = apiMethods[i];
- var apiParameters = apiMethod.Parameters;
- var parameterTypes = apiParameters.Select(p => p.ParameterType).ToArray();
-
- var implMethod = new MethodDefinition(apiMethod.Name, implementAttribute, apiMethod.ReturnType);
- foreach (var item in apiParameters)
- {
- var parameter = new ParameterDefinition(item.ParameterType);
- implMethod.Parameters.Add(parameter);
- }
- proxyType.Methods.Add(implMethod);
-
- var iL = implMethod.Body.GetILProcessor();
-
- // this.interceptor
- iL.Emit(OpCodes.Ldarg_0);
- iL.Emit(OpCodes.Ldfld, fieldInterceptor);
-
- // 加载target参数
- iL.Emit(OpCodes.Ldarg_0);
-
- // 加载method参数 this.apiMethods[i]
- iL.Emit(OpCodes.Ldarg_0);
- iL.Emit(OpCodes.Ldfld, fieldApiMethods);
- iL.Emit(OpCodes.Ldc_I4, i);
- iL.Emit(OpCodes.Ldelem_Ref);
-
- // var parameters = new object[parameters.Length]
- var parameters = new VariableDefinition(new ArrayType(this.TypeSystem.Object));
- implMethod.Body.Variables.Add(parameters);
-
- iL.Emit(OpCodes.Ldc_I4, apiParameters.Count);
- iL.Emit(OpCodes.Newarr, this.TypeSystem.Object);
- iL.Emit(OpCodes.Stloc, parameters);
-
- for (var j = 0; j < apiParameters.Count; j++)
- {
- iL.Emit(OpCodes.Ldloc, parameters);
- iL.Emit(OpCodes.Ldc_I4, j);
- iL.Emit(OpCodes.Ldarg, j + 1);
-
- var parameterType = parameterTypes[j];
- if (parameterType.IsValueType || parameterType.IsGenericParameter)
- {
- iL.Emit(OpCodes.Box, parameterType);
- }
- iL.Emit(OpCodes.Stelem_Ref);
- }
-
- // 加载parameters参数
- iL.Emit(OpCodes.Ldloc, parameters);
-
- // Intercep(this, method, parameters)
- var interceptMethod = this.ImportMethod(nameof(IApiInterceptor.Intercept));
- iL.Emit(OpCodes.Callvirt, interceptMethod);
-
- iL.Emit(OpCodes.Castclass, apiMethod.ReturnType);
- iL.Emit(OpCodes.Ret);
- }
- }
- }
-}
diff --git a/WebApiClient.BuildTask/Logger.cs b/WebApiClient.BuildTask/Logger.cs
deleted file mode 100644
index af78cb91..00000000
--- a/WebApiClient.BuildTask/Logger.cs
+++ /dev/null
@@ -1,61 +0,0 @@
-using Microsoft.Build.Framework;
-using Microsoft.Build.Utilities;
-
-namespace WebApiClient.BuildTask
-{
- ///
- /// 表示日志
- ///
- class Logger
- {
- ///
- /// 日志标签
- ///
- private const string tagName = "WebApiClient";
-
- ///
- /// 包装的日志类
- ///
- private readonly TaskLoggingHelper logger;
-
- ///
- /// 表示日志
- ///
- ///
- public Logger(TaskLoggingHelper logger)
- {
- this.logger = logger;
- }
-
- ///
- /// 输出消息
- ///
- /// 行为
- /// 值
- public void Message(string action, string value)
- {
- var message = $"{action}: {value}";
- this.Message(message);
- }
-
- ///
- /// 输出消息
- ///
- /// 消息
- public void Message(string message)
- {
- var log = $"{tagName} -> {message}";
- this.logger.LogMessage(MessageImportance.High, log);
- }
-
- ///
- /// 输出异常
- ///
- /// 异常
- public void Error(string message)
- {
- var log = $"{tagName} -> {message}";
- this.logger.LogError(log);
- }
- }
-}
diff --git a/WebApiClient.BuildTask/OuterTypes/about.txt b/WebApiClient.BuildTask/OuterTypes/about.txt
deleted file mode 100644
index a371646c..00000000
--- a/WebApiClient.BuildTask/OuterTypes/about.txt
+++ /dev/null
@@ -1,2 +0,0 @@
- 本目录的所有类型是外链类型,将参与到编译中;
- 给目标程序集插入IL时,目标程序集使用着WebApiClient.AOT.dll中对应本类型的类型
\ No newline at end of file
diff --git a/WebApiClient.BuildTask/ProxyTask.cs b/WebApiClient.BuildTask/ProxyTask.cs
deleted file mode 100644
index e2c58cea..00000000
--- a/WebApiClient.BuildTask/ProxyTask.cs
+++ /dev/null
@@ -1,82 +0,0 @@
-using Microsoft.Build.Framework;
-using Microsoft.Build.Utilities;
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-
-namespace WebApiClient.BuildTask
-{
- ///
- /// 表示插入代理IL任务
- ///
- public class ProxyTask : Task
- {
- ///
- /// 插入代理的程序集
- ///
- [Required]
- public string TargetAssembly { get; set; }
-
- ///
- /// 引用的程序集
- /// 逗号分隔
- ///
- [Required]
- public ITaskItem[] References { get; set; }
-
- ///
- /// 执行任务
- ///
- ///
- public override bool Execute()
- {
- var logger = new Logger(this.Log);
- if (File.Exists(this.TargetAssembly) == false)
- {
- logger.Message($"找不到文件编译输出的程序集{this.TargetAssembly}");
- return true;
- }
-
- try
- {
- logger.Message(this.GetType().AssemblyQualifiedName);
- var searchDirectories = this.GetSearchDirectories().Distinct().ToArray();
- using (var assembly = new CeAssembly(this.TargetAssembly, searchDirectories, logger))
- {
- assembly.WirteProxyTypes();
- }
- return true;
- }
- catch (Exception ex)
- {
- logger.Error(ex.Message);
- return false;
- }
- }
-
- ///
- /// 返回依赖搜索目录
- ///
- ///
- private IEnumerable GetSearchDirectories()
- {
- if (this.References == null)
- {
- yield break;
- }
-
- foreach (var item in this.References)
- {
- if (string.IsNullOrEmpty(item.ItemSpec) == false)
- {
- var path = Path.GetDirectoryName(item.ItemSpec);
- if (Directory.Exists(path) == true)
- {
- yield return path;
- }
- }
- }
- }
- }
-}
diff --git a/WebApiClient.BuildTask/WebApiClient.BuildTask.csproj b/WebApiClient.BuildTask/WebApiClient.BuildTask.csproj
deleted file mode 100644
index 060968c1..00000000
--- a/WebApiClient.BuildTask/WebApiClient.BuildTask.csproj
+++ /dev/null
@@ -1,44 +0,0 @@
-
-
-
- Library
- net45;netcoreapp1.1;
- AOT_Debug;AOT_Release
- 0.1.5
- 0.1.5.0
- true
- sign.snk
-
-
-
- TRACE;DEBUG
- false
-
-
-
- RELEASE
- true
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/WebApiClient.BuildTask/sign.snk b/WebApiClient.BuildTask/sign.snk
deleted file mode 100644
index b90892b5..00000000
Binary files a/WebApiClient.BuildTask/sign.snk and /dev/null differ
diff --git a/WebApiClient.Test/Attributes/FormFieldAttributeTest.cs b/WebApiClient.Test/Attributes/FormFieldAttributeTest.cs
deleted file mode 100644
index 8e31a82c..00000000
--- a/WebApiClient.Test/Attributes/FormFieldAttributeTest.cs
+++ /dev/null
@@ -1,66 +0,0 @@
-using System;
-using System.Net.Http;
-using System.Threading.Tasks;
-using WebApiClient;
-using WebApiClient.Attributes;
-using WebApiClient.Contexts;
-using Xunit;
-
-
-namespace WebApiClient.Test.Attributes
-{
- public class FormFieldAttributeTest
- {
- [Fact]
- public async Task IApiParameterAttributeTest()
- {
- var context = new TestActionContext(
- httpApi: null,
- httpApiConfig: new HttpApiConfig(),
- apiActionDescriptor: new ApiActionDescriptor(typeof(IMyApi).GetMethod("PostAsync")));
-
- context.RequestMessage.RequestUri = new Uri("http://www.webapi.com/");
- context.RequestMessage.Method = HttpMethod.Post;
-
- var parameter = context.ApiActionDescriptor.Parameters[0].Clone("laojiu");
- IApiParameterAttribute attr = new FormFieldAttribute();
- await attr.BeforeRequestAsync(context, parameter);
- var body = await context.RequestMessage.Content.ReadAsStringAsync();
- Assert.Equal("name=laojiu", body);
-
- // IgnoreWhenNull Test
- parameter = parameter.Clone(null);
- ((FormFieldAttribute)attr).IgnoreWhenNull = true;
- await attr.BeforeRequestAsync(context, parameter);
- body = await context.RequestMessage.Content.ReadAsStringAsync();
- Assert.Equal("name=laojiu", body);
- }
-
- [Fact]
- public async Task IApiActionAttributeTest()
- {
- var context = new TestActionContext(
- httpApi: null,
- httpApiConfig: new HttpApiConfig(),
- apiActionDescriptor: new ApiActionDescriptor(typeof(IMyApi).GetMethod("PostAsync")));
-
- context.RequestMessage.RequestUri = new Uri("http://www.webapi.com/");
- context.RequestMessage.Method = HttpMethod.Post;
-
- var attr = new FormFieldAttribute("name", "laojiu");
- await attr.BeforeRequestAsync(context);
- var body = await context.RequestMessage.Content.ReadAsStringAsync();
- Assert.Equal("name=laojiu", body);
-
-
- // IgnoreWhenNull Test
- var attr2 = new FormFieldAttribute("age", null)
- {
- IgnoreWhenNull = true
- };
- await attr2.BeforeRequestAsync(context);
- body = await context.RequestMessage.Content.ReadAsStringAsync();
- Assert.Equal("name=laojiu", body);
- }
- }
-}
diff --git a/WebApiClient.Test/Attributes/HeaderAttributeTest.cs b/WebApiClient.Test/Attributes/HeaderAttributeTest.cs
deleted file mode 100644
index c9e0a73d..00000000
--- a/WebApiClient.Test/Attributes/HeaderAttributeTest.cs
+++ /dev/null
@@ -1,48 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Net.Http;
-using System.Threading.Tasks;
-using WebApiClient;
-using WebApiClient.Attributes;
-using WebApiClient.Contexts;
-using Xunit;
-
-namespace WebApiClient.Test.Attributes
-{
- public class HeaderAttributeTest
- {
- [Fact]
- public async Task IApiParameterAttributeTest()
- {
- var context = new TestActionContext(
- httpApi: null,
- httpApiConfig: new HttpApiConfig(),
- apiActionDescriptor: new ApiActionDescriptor(typeof(IMyApi).GetMethod("PostAsync")));
-
- var parameter = context.ApiActionDescriptor.Parameters[0].Clone("laojiu");
-
- IApiParameterAttribute attr = new HeaderAttribute("MyHeader");
- await attr.BeforeRequestAsync(context, parameter);
-
- context.RequestMessage.Headers.TryGetValues("MyHeader", out IEnumerable values);
- Assert.Equal("laojiu", values.First());
- }
-
- [Fact]
- public async Task IApiActionAttributeTest()
- {
- var context = new TestActionContext(
- httpApi: null,
- httpApiConfig: new HttpApiConfig(),
- apiActionDescriptor: new ApiActionDescriptor(typeof(IMyApi).GetMethod("PostAsync")));
-
- var attr = new HeaderAttribute("MyHeader", "laojiu");
- await attr.BeforeRequestAsync(context);
-
- context.RequestMessage.Headers.TryGetValues("MyHeader", out IEnumerable values);
- Assert.Equal("laojiu", values.First());
- }
- }
-}
-
diff --git a/WebApiClient.Test/Attributes/HttpActionAttributes/BasicAuthAttributeTest.cs b/WebApiClient.Test/Attributes/HttpActionAttributes/BasicAuthAttributeTest.cs
deleted file mode 100644
index 3c0b8a81..00000000
--- a/WebApiClient.Test/Attributes/HttpActionAttributes/BasicAuthAttributeTest.cs
+++ /dev/null
@@ -1,29 +0,0 @@
-using System;
-using System.Text;
-using System.Threading.Tasks;
-using WebApiClient;
-using WebApiClient.Attributes;
-using WebApiClient.Contexts;
-using Xunit;
-
-
-namespace WebApiClient.Test.Attributes.HttpActionAttributes
-{
- public class BasicAuthAttributeTest
- {
- [Fact]
- public async Task BeforeRequestAsyncTest()
- {
- var context = new TestActionContext(
- httpApi: null,
- httpApiConfig: new HttpApiConfig(),
- apiActionDescriptor: new ApiActionDescriptor(typeof(IMyApi).GetMethod("PostAsync")));
-
- var attr = new BasicAuthAttribute("laojiu", "123456");
- await attr.BeforeRequestAsync(context);
-
- var auth = Convert.ToBase64String(Encoding.ASCII.GetBytes("laojiu:123456"));
- Assert.True(context.RequestMessage.Headers.Authorization.Parameter == auth);
- }
- }
-}
diff --git a/WebApiClient.Test/Attributes/HttpActionAttributes/HttpDeleteAttributeTest.cs b/WebApiClient.Test/Attributes/HttpActionAttributes/HttpDeleteAttributeTest.cs
deleted file mode 100644
index afcef595..00000000
--- a/WebApiClient.Test/Attributes/HttpActionAttributes/HttpDeleteAttributeTest.cs
+++ /dev/null
@@ -1,40 +0,0 @@
-using System;
-using System.Net.Http;
-using System.Threading.Tasks;
-using WebApiClient;
-using WebApiClient.Attributes;
-using WebApiClient.Contexts;
-using Xunit;
-
-
-namespace WebApiClient.Test.Attributes.HttpActionAttributes
-{
- public class HttpDeleteAttributeTest
- {
- [Fact]
- public async Task BeforeRequestAsyncTest()
- {
- var context = new TestActionContext(
- httpApi: null,
- httpApiConfig: new HttpApiConfig(),
- apiActionDescriptor: new ApiActionDescriptor(typeof(IMyApi).GetMethod("PostAsync")));
-
- context.RequestMessage.RequestUri = new Uri("http://www.webapi.com/");
- context.RequestMessage.Method = HttpMethod.Post;
-
- var attr = new HttpDeleteAttribute();
- await attr.BeforeRequestAsync(context);
- Assert.True(context.RequestMessage.Method == HttpMethod.Delete);
-
- var attr2 = new HttpDeleteAttribute("/login");
- await attr2.BeforeRequestAsync(context);
- Assert.True(context.RequestMessage.Method == HttpMethod.Delete);
- Assert.True(context.RequestMessage.RequestUri == new Uri("http://www.webapi.com/login"));
-
- var attr3 = new HttpDeleteAttribute("http://www.baidu.com");
- await attr3.BeforeRequestAsync(context);
- Assert.True(context.RequestMessage.Method == HttpMethod.Delete);
- Assert.True(context.RequestMessage.RequestUri == new Uri("http://www.baidu.com"));
- }
- }
-}
diff --git a/WebApiClient.Test/Attributes/HttpActionAttributes/HttpGetAttributeTest.cs b/WebApiClient.Test/Attributes/HttpActionAttributes/HttpGetAttributeTest.cs
deleted file mode 100644
index e89284c6..00000000
--- a/WebApiClient.Test/Attributes/HttpActionAttributes/HttpGetAttributeTest.cs
+++ /dev/null
@@ -1,41 +0,0 @@
-using System;
-using System.Net.Http;
-using System.Threading.Tasks;
-using WebApiClient;
-using WebApiClient.Attributes;
-using WebApiClient.Contexts;
-using Xunit;
-
-
-namespace WebApiClient.Test.Attributes.HttpActionAttributes
-{
- public class HttpGetAttributeTest
- {
- [Fact]
- public async Task BeforeRequestAsyncTest()
- {
- var context = new TestActionContext(
- httpApi: null,
- httpApiConfig: new HttpApiConfig(),
- apiActionDescriptor: new ApiActionDescriptor(typeof(IMyApi).GetMethod("PostAsync")));
-
- context.RequestMessage.RequestUri = new Uri("http://www.webapi.com/");
- context.RequestMessage.Method = HttpMethod.Post;
-
-
- var attr = new HttpGetAttribute();
- await attr.BeforeRequestAsync(context);
- Assert.True(context.RequestMessage.Method == HttpMethod.Get);
-
- var attr2 = new HttpGetAttribute("/login");
- await attr2.BeforeRequestAsync(context);
- Assert.True(context.RequestMessage.Method == HttpMethod.Get);
- Assert.True(context.RequestMessage.RequestUri == new Uri("http://www.webapi.com/login"));
-
- var attr3 = new HttpGetAttribute("http://www.baidu.com");
- await attr3.BeforeRequestAsync(context);
- Assert.True(context.RequestMessage.Method == HttpMethod.Get);
- Assert.True(context.RequestMessage.RequestUri == new Uri("http://www.baidu.com"));
- }
- }
-}
diff --git a/WebApiClient.Test/Attributes/HttpActionAttributes/HttpHeadAttributeTest.cs b/WebApiClient.Test/Attributes/HttpActionAttributes/HttpHeadAttributeTest.cs
deleted file mode 100644
index 9865f73b..00000000
--- a/WebApiClient.Test/Attributes/HttpActionAttributes/HttpHeadAttributeTest.cs
+++ /dev/null
@@ -1,40 +0,0 @@
-using System;
-using System.Net.Http;
-using System.Threading.Tasks;
-using WebApiClient;
-using WebApiClient.Attributes;
-using WebApiClient.Contexts;
-using Xunit;
-
-
-namespace WebApiClient.Test.Attributes.HttpActionAttributes
-{
- public class HttpHeadAttributeTest
- {
- [Fact]
- public async Task BeforeRequestAsyncTest()
- {
- var context = new TestActionContext(
- httpApi: null,
- httpApiConfig: new HttpApiConfig(),
- apiActionDescriptor: new ApiActionDescriptor(typeof(IMyApi).GetMethod("PostAsync")));
-
- context.RequestMessage.RequestUri = new Uri("http://www.webapi.com/");
- context.RequestMessage.Method = HttpMethod.Post;
-
- var attr = new HttpHeadAttribute();
- await attr.BeforeRequestAsync(context);
- Assert.True(context.RequestMessage.Method == HttpMethod.Head);
-
- var attr2 = new HttpHeadAttribute("/login");
- await attr2.BeforeRequestAsync(context);
- Assert.True(context.RequestMessage.Method == HttpMethod.Head);
- Assert.True(context.RequestMessage.RequestUri == new Uri("http://www.webapi.com/login"));
-
- var attr3 = new HttpHeadAttribute("http://www.baidu.com");
- await attr3.BeforeRequestAsync(context);
- Assert.True(context.RequestMessage.Method == HttpMethod.Head);
- Assert.True(context.RequestMessage.RequestUri == new Uri("http://www.baidu.com"));
- }
- }
-}
diff --git a/WebApiClient.Test/Attributes/HttpActionAttributes/HttpHostAttributeTest.cs b/WebApiClient.Test/Attributes/HttpActionAttributes/HttpHostAttributeTest.cs
deleted file mode 100644
index f6d63d0c..00000000
--- a/WebApiClient.Test/Attributes/HttpActionAttributes/HttpHostAttributeTest.cs
+++ /dev/null
@@ -1,30 +0,0 @@
-using System;
-using System.Net.Http;
-using System.Threading.Tasks;
-using WebApiClient;
-using WebApiClient.Attributes;
-using WebApiClient.Contexts;
-using Xunit;
-
-
-namespace WebApiClient.Test.Attributes.HttpActionAttributes
-{
- public class HttpHostAttributeTest
- {
- [Fact]
- public async Task BeforeRequestAsyncTest()
- {
- var context = new TestActionContext(
- httpApi: null,
- httpApiConfig: new HttpApiConfig(),
- apiActionDescriptor: new ApiActionDescriptor(typeof(IMyApi).GetMethod("PostAsync")));
-
- Assert.Throws(() => new HttpHostAttribute(null));
- Assert.Throws(() => new HttpHostAttribute("/"));
-
- var attr = new HttpHostAttribute("http://www.webapiclient.com");
- await attr.BeforeRequestAsync(context);
- Assert.True(context.RequestMessage.RequestUri == new Uri("http://www.webapiclient.com"));
- }
- }
-}
diff --git a/WebApiClient.Test/Attributes/HttpActionAttributes/HttpOptionsAttributeTest.cs b/WebApiClient.Test/Attributes/HttpActionAttributes/HttpOptionsAttributeTest.cs
deleted file mode 100644
index 79abca2c..00000000
--- a/WebApiClient.Test/Attributes/HttpActionAttributes/HttpOptionsAttributeTest.cs
+++ /dev/null
@@ -1,41 +0,0 @@
-using System;
-using System.Net.Http;
-using System.Threading.Tasks;
-using WebApiClient;
-using WebApiClient.Attributes;
-using WebApiClient.Contexts;
-using Xunit;
-
-
-namespace WebApiClient.Test.Attributes.HttpActionAttributes
-{
- public class HttpOptionsAttributeTest
- {
- [Fact]
- public async Task BeforeRequestAsyncTest()
- {
- var context = new TestActionContext(
- httpApi: null,
- httpApiConfig: new HttpApiConfig(),
- apiActionDescriptor: new ApiActionDescriptor(typeof(IMyApi).GetMethod("PostAsync")));
-
- context.RequestMessage.RequestUri = new Uri("http://www.webapi.com/");
- context.RequestMessage.Method = HttpMethod.Post;
-
-
- var attr = new HttpOptionsAttribute();
- await attr.BeforeRequestAsync(context);
- Assert.True(context.RequestMessage.Method == HttpMethod.Options);
-
- var attr2 = new HttpOptionsAttribute("/login");
- await attr2.BeforeRequestAsync(context);
- Assert.True(context.RequestMessage.Method == HttpMethod.Options);
- Assert.True(context.RequestMessage.RequestUri == new Uri("http://www.webapi.com/login"));
-
- var attr3 = new HttpOptionsAttribute("http://www.baidu.com");
- await attr3.BeforeRequestAsync(context);
- Assert.True(context.RequestMessage.Method == HttpMethod.Options);
- Assert.True(context.RequestMessage.RequestUri == new Uri("http://www.baidu.com"));
- }
- }
-}
diff --git a/WebApiClient.Test/Attributes/HttpActionAttributes/HttpPostAttributeTest.cs b/WebApiClient.Test/Attributes/HttpActionAttributes/HttpPostAttributeTest.cs
deleted file mode 100644
index 62f9b503..00000000
--- a/WebApiClient.Test/Attributes/HttpActionAttributes/HttpPostAttributeTest.cs
+++ /dev/null
@@ -1,41 +0,0 @@
-using System;
-using System.Net.Http;
-using System.Threading.Tasks;
-using WebApiClient;
-using WebApiClient.Attributes;
-using WebApiClient.Contexts;
-using Xunit;
-
-
-namespace WebApiClient.Test.Attributes.HttpActionAttributes
-{
- public class HttpPostAttributeTest
- {
- [Fact]
- public async Task BeforeRequestAsyncTest()
- {
- var context = new TestActionContext(
- httpApi: null,
- httpApiConfig: new HttpApiConfig(),
- apiActionDescriptor: new ApiActionDescriptor(typeof(IMyApi).GetMethod("PostAsync")));
-
- context.RequestMessage.RequestUri = new Uri("http://www.webapi.com/");
- context.RequestMessage.Method = HttpMethod.Post;
-
-
- var attr = new HttpPostAttribute();
- await attr.BeforeRequestAsync(context);
- Assert.True(context.RequestMessage.Method == HttpMethod.Post);
-
- var attr2 = new HttpPostAttribute("/login");
- await attr2.BeforeRequestAsync(context);
- Assert.True(context.RequestMessage.Method == HttpMethod.Post);
- Assert.True(context.RequestMessage.RequestUri == new Uri("http://www.webapi.com/login"));
-
- var attr3 = new HttpPostAttribute("http://www.baidu.com");
- await attr3.BeforeRequestAsync(context);
- Assert.True(context.RequestMessage.Method == HttpMethod.Post);
- Assert.True(context.RequestMessage.RequestUri == new Uri("http://www.baidu.com"));
- }
- }
-}
diff --git a/WebApiClient.Test/Attributes/HttpActionAttributes/HttpPutAttributeTest.cs b/WebApiClient.Test/Attributes/HttpActionAttributes/HttpPutAttributeTest.cs
deleted file mode 100644
index 7d43f61c..00000000
--- a/WebApiClient.Test/Attributes/HttpActionAttributes/HttpPutAttributeTest.cs
+++ /dev/null
@@ -1,40 +0,0 @@
-using System;
-using System.Net.Http;
-using System.Threading.Tasks;
-using WebApiClient;
-using WebApiClient.Attributes;
-using WebApiClient.Contexts;
-using Xunit;
-
-
-namespace WebApiClient.Test.Attributes.HttpActionAttributes
-{
- public class HttpPutAttributeTest
- {
- [Fact]
- public async Task BeforeRequestAsyncTest()
- {
- var context = new TestActionContext(
- httpApi: null,
- httpApiConfig: new HttpApiConfig(),
- apiActionDescriptor: new ApiActionDescriptor(typeof(IMyApi).GetMethod("PostAsync")));
-
- context.RequestMessage.RequestUri = new Uri("http://www.webapi.com/");
- context.RequestMessage.Method = HttpMethod.Post;
-
- var attr = new HttpPutAttribute();
- await attr.BeforeRequestAsync(context);
- Assert.True(context.RequestMessage.Method == HttpMethod.Put);
-
- var attr2 = new HttpPutAttribute("/login");
- await attr2.BeforeRequestAsync(context);
- Assert.True(context.RequestMessage.Method == HttpMethod.Put);
- Assert.True(context.RequestMessage.RequestUri == new Uri("http://www.webapi.com/login"));
-
- var attr3 = new HttpPutAttribute("http://www.baidu.com");
- await attr3.BeforeRequestAsync(context);
- Assert.True(context.RequestMessage.Method == HttpMethod.Put);
- Assert.True(context.RequestMessage.RequestUri == new Uri("http://www.baidu.com"));
- }
- }
-}
diff --git a/WebApiClient.Test/Attributes/HttpActionAttributes/ProxyAttributeTest.cs b/WebApiClient.Test/Attributes/HttpActionAttributes/ProxyAttributeTest.cs
deleted file mode 100644
index ed2c5e01..00000000
--- a/WebApiClient.Test/Attributes/HttpActionAttributes/ProxyAttributeTest.cs
+++ /dev/null
@@ -1,34 +0,0 @@
-using System;
-using System.Net.Http;
-using System.Threading.Tasks;
-using WebApiClient;
-using WebApiClient.Attributes;
-using WebApiClient.Contexts;
-using Xunit;
-
-
-namespace WebApiClient.Test.Attributes.HttpActionAttributes
-{
- public class ProxyAttributeTest
- {
- [Fact]
- public async Task BeforeRequestAsyncTest()
- {
- var context = new TestActionContext(
- httpApi: null,
- httpApiConfig: new HttpApiConfig(),
- apiActionDescriptor: new ApiActionDescriptor(typeof(IMyApi).GetMethod("PostAsync")));
-
- var attr = new ProxyAttribute("localhost", 5555);
- await attr.BeforeRequestAsync(context);
- var handler = context.HttpApiConfig.HttpHandler;
- Assert.True(handler.UseProxy == true);
- Assert.True(handler.Proxy != null);
- Assert.True(handler.Proxy.Credentials == null);
- Assert.True(handler.Proxy.GetProxy(new Uri("http://www.baidu.com")).Authority == "localhost:5555");
-
- var attr2 = new ProxyAttribute("localhost", 5555, "laojiu", "123456");
- await Assert.ThrowsAsync(() => attr2.BeforeRequestAsync(context));
- }
- }
-}
diff --git a/WebApiClient.Test/Attributes/HttpActionAttributes/TimeoutAttributeTest.cs b/WebApiClient.Test/Attributes/HttpActionAttributes/TimeoutAttributeTest.cs
deleted file mode 100644
index a66582ba..00000000
--- a/WebApiClient.Test/Attributes/HttpActionAttributes/TimeoutAttributeTest.cs
+++ /dev/null
@@ -1,30 +0,0 @@
-using System;
-using System.Net.Http;
-using System.Threading.Tasks;
-using WebApiClient;
-using WebApiClient.Attributes;
-using WebApiClient.Contexts;
-using Xunit;
-
-
-namespace WebApiClient.Test.Attributes.HttpActionAttributes
-{
- public class TimeoutAttributeTest
- {
- [Fact]
- public async Task BeforeRequestAsyncTest()
- {
- var context = new TestActionContext(
- httpApi: null,
- httpApiConfig: new HttpApiConfig(),
- apiActionDescriptor: new ApiActionDescriptor(typeof(IMyApi).GetMethod("PostAsync")));
-
- var attr = new TimeoutAttribute(50);
- await attr.BeforeRequestAsync(context);
-
- await Task.Delay(100);
- var canceled = context.CancellationTokens[0].IsCancellationRequested;
- Assert.True(canceled);
- }
- }
-}
diff --git a/WebApiClient.Test/Attributes/IMyApi.cs b/WebApiClient.Test/Attributes/IMyApi.cs
deleted file mode 100644
index 890b8d84..00000000
--- a/WebApiClient.Test/Attributes/IMyApi.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-using System;
-using System.Net.Http;
-using WebApiClient;
-
-namespace WebApiClient.Test.Attributes
-{
- public interface IMyApi : IDisposable
- {
- ITask PostAsync(string name);
- }
-}
diff --git a/WebApiClient.Test/Attributes/MulitpartTextAttributeTest.cs b/WebApiClient.Test/Attributes/MulitpartTextAttributeTest.cs
deleted file mode 100644
index 6f3b46c2..00000000
--- a/WebApiClient.Test/Attributes/MulitpartTextAttributeTest.cs
+++ /dev/null
@@ -1,75 +0,0 @@
-using System;
-using System.Net.Http;
-using System.Text;
-using System.Threading.Tasks;
-using WebApiClient;
-using WebApiClient.Attributes;
-using WebApiClient.Contexts;
-using Xunit;
-
-
-namespace WebApiClient.Test.Attributes
-{
- public class MulitpartTextAttributeTest
- {
- private string get(string name, string value)
- {
- return $@"Content-Disposition: form-data; name=""{name}""
-
-{HttpUtility.UrlEncode(value, Encoding.UTF8)}";
- }
-
- [Fact]
- public async Task IApiParameterAttributeTest()
- {
- var context = new TestActionContext(
- httpApi: null,
- httpApiConfig: new HttpApiConfig(),
- apiActionDescriptor: new ApiActionDescriptor(typeof(IMyApi).GetMethod("PostAsync")));
-
- context.RequestMessage.RequestUri = new Uri("http://www.webapi.com/");
- context.RequestMessage.Method = HttpMethod.Post;
-
- var parameter = context.ApiActionDescriptor.Parameters[0].Clone("laojiu");
-
- IApiParameterAttribute attr = new MulitpartTextAttribute();
- await attr.BeforeRequestAsync(context, parameter);
- var body = await context.RequestMessage.Content.ReadAsStringAsync();
- Assert.Contains(get("name", "laojiu"), body);
-
- // IgnoreWhenNull Test
- parameter = parameter.Clone(null);
- ((MulitpartTextAttribute)attr).IgnoreWhenNull = true;
- await attr.BeforeRequestAsync(context, parameter);
- body = await context.RequestMessage.Content.ReadAsStringAsync();
- Assert.DoesNotContain("age", body);
- }
-
- [Fact]
- public async Task IApiActionAttributeTest()
- {
- var context = new TestActionContext(
- httpApi: null,
- httpApiConfig: new HttpApiConfig(),
- apiActionDescriptor: new ApiActionDescriptor(typeof(IMyApi).GetMethod("PostAsync")));
-
- context.RequestMessage.RequestUri = new Uri("http://www.webapi.com/");
- context.RequestMessage.Method = HttpMethod.Post;
-
- var attr = new MulitpartTextAttribute("name", "laojiu");
- await attr.BeforeRequestAsync(context);
- var body = await context.RequestMessage.Content.ReadAsStringAsync();
- Assert.Contains(get("name", "laojiu"), body);
-
-
- // IgnoreWhenNull Test
- var attr2 = new MulitpartTextAttribute("age", null)
- {
- IgnoreWhenNull = true
- };
- await attr2.BeforeRequestAsync(context);
- body = await context.RequestMessage.Content.ReadAsStringAsync();
- Assert.DoesNotContain("age", body);
- }
- }
-}
diff --git a/WebApiClient.Test/Attributes/ParameterAttributes/FormContentAttributeTest.cs b/WebApiClient.Test/Attributes/ParameterAttributes/FormContentAttributeTest.cs
deleted file mode 100644
index 4d32f741..00000000
--- a/WebApiClient.Test/Attributes/ParameterAttributes/FormContentAttributeTest.cs
+++ /dev/null
@@ -1,47 +0,0 @@
-using System;
-using System.Net.Http;
-using System.Text;
-using System.Threading.Tasks;
-using WebApiClient;
-using WebApiClient.Attributes;
-using WebApiClient.Contexts;
-using Xunit;
-
-
-namespace WebApiClient.Test.Attributes.HttpActionAttributes
-{
- public class FormContentAttributeTest
- {
- public interface IMyApi : IDisposable
- {
- ITask PostAsync(object content);
- }
-
- [Fact]
- public async Task BeforeRequestAsyncTest()
- {
- var context = new TestActionContext(
- httpApi: null,
- httpApiConfig: new HttpApiConfig(),
- apiActionDescriptor: new ApiActionDescriptor(typeof(IMyApi).GetMethod("PostAsync")));
-
- context.RequestMessage.RequestUri = new Uri("http://www.mywebapi.com");
- context.RequestMessage.Method = HttpMethod.Post;
-
-
- var parameter = context.ApiActionDescriptor.Parameters[0].Clone(new
- {
- name = "老 九",
- birthDay = DateTime.Parse("2010-10-10")
- });
-
- var attr = new FormContentAttribute();
- await ((IApiParameterAttribute)attr).BeforeRequestAsync(context, parameter);
-
- var body = await context.RequestMessage.Content.ReadAsStringAsync();
- var time = context.HttpApiConfig.FormatOptions.CloneChange(attr.DateTimeFormat).FormatDateTime(DateTime.Parse("2010-10-10"));
- var target = $"name={HttpUtility.UrlEncode("老 九", Encoding.UTF8)}&birthDay={HttpUtility.UrlEncode(time, Encoding.UTF8)}";
- Assert.True(body.ToUpper() == target.ToUpper());
- }
- }
-}
diff --git a/WebApiClient.Test/Attributes/ParameterAttributes/HeadersAttributeTest.cs b/WebApiClient.Test/Attributes/ParameterAttributes/HeadersAttributeTest.cs
deleted file mode 100644
index 7151fe8d..00000000
--- a/WebApiClient.Test/Attributes/ParameterAttributes/HeadersAttributeTest.cs
+++ /dev/null
@@ -1,45 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Net.Http;
-using System.Threading.Tasks;
-using WebApiClient;
-using WebApiClient.Attributes;
-using WebApiClient.Contexts;
-using Xunit;
-
-
-namespace WebApiClient.Test.Attributes.ParameterAttributes
-{
- public class HeadersAttributeTest
- {
- public interface IMyApi : IDisposable
- {
- ITask PostAsync(object headers);
- }
-
- [Fact]
- public async Task IApiParameterAttributeTest()
- {
- var context = new TestActionContext(
- httpApi: null,
- httpApiConfig: new HttpApiConfig(),
- apiActionDescriptor: new ApiActionDescriptor(typeof(IMyApi).GetMethod("PostAsync")));
-
- var parameter = context.ApiActionDescriptor.Parameters[0].Clone(new
- {
- @class = 123,
- User_Agent = "WebApiClient"
- });
-
- var attr = new HeadersAttribute();
- await attr.BeforeRequestAsync(context, parameter);
-
- context.RequestMessage.Headers.TryGetValues("User-Agent", out IEnumerable values);
- Assert.Equal("WebApiClient", values.FirstOrDefault());
-
- context.RequestMessage.Headers.TryGetValues("class", out IEnumerable cValues);
- Assert.Equal("123", cValues.FirstOrDefault());
- }
- }
-}
diff --git a/WebApiClient.Test/Attributes/ParameterAttributes/HttpContentAttributeTest.cs b/WebApiClient.Test/Attributes/ParameterAttributes/HttpContentAttributeTest.cs
deleted file mode 100644
index 677e99e2..00000000
--- a/WebApiClient.Test/Attributes/ParameterAttributes/HttpContentAttributeTest.cs
+++ /dev/null
@@ -1,37 +0,0 @@
-using System;
-using System.Net.Http;
-using System.Threading.Tasks;
-using WebApiClient;
-using WebApiClient.Attributes;
-using WebApiClient.Contexts;
-using Xunit;
-
-namespace WebApiClient.Test.Attributes.HttpActionAttributes
-{
- public class HttpContentAttributeTest
- {
- public interface IMyApi : IDisposable
- {
- ITask PostAsync(HttpContent content);
- }
-
- [Fact]
- public async Task BeforeRequestAsyncTest()
- {
- var context = new TestActionContext(
- httpApi: null,
- httpApiConfig: new HttpApiConfig(),
- apiActionDescriptor: new ApiActionDescriptor(typeof(IMyApi).GetMethod("PostAsync")));
-
- context.RequestMessage.RequestUri = new Uri("http://www.mywebapi.com");
- context.RequestMessage.Method = HttpMethod.Post;
-
- var parameter = context.ApiActionDescriptor.Parameters[0].Clone(new StringContent("laojiu"));
- var attr = new HttpContentAttribute();
- await ((IApiParameterAttribute)attr).BeforeRequestAsync(context, parameter);
-
- var body = await context.RequestMessage.Content.ReadAsStringAsync();
- Assert.True(body == "laojiu");
- }
- }
-}
\ No newline at end of file
diff --git a/WebApiClient.Test/Attributes/ParameterAttributes/JsonContentAttributeTest.cs b/WebApiClient.Test/Attributes/ParameterAttributes/JsonContentAttributeTest.cs
deleted file mode 100644
index 71c333dc..00000000
--- a/WebApiClient.Test/Attributes/ParameterAttributes/JsonContentAttributeTest.cs
+++ /dev/null
@@ -1,46 +0,0 @@
-using System;
-using System.Net.Http;
-using System.Threading.Tasks;
-using WebApiClient;
-using WebApiClient.Attributes;
-using WebApiClient.Contexts;
-using Xunit;
-
-namespace WebApiClient.Test.Attributes.HttpActionAttributes
-{
- public class JsonContentAttributeTest
- {
- public interface IMyApi : IDisposable
- {
- ITask PostAsync(object content);
- }
-
- [Fact]
- public async Task BeforeRequestAsyncTest()
- {
- var context = new TestActionContext(
- httpApi: null,
- httpApiConfig: new HttpApiConfig(),
- apiActionDescriptor: new ApiActionDescriptor(typeof(IMyApi).GetMethod("PostAsync")));
-
- context.RequestMessage.RequestUri = new Uri("http://www.webapi.com/");
- context.RequestMessage.Method = HttpMethod.Post;
-
-
- var parameter = context.ApiActionDescriptor.Parameters[0].Clone(new
- {
- name = "laojiu",
- birthDay = DateTime.Parse("2010-10-10")
- });
-
- var attr = new JsonContentAttribute();
- await ((IApiParameterAttribute)attr).BeforeRequestAsync(context, parameter);
-
- var body = await context.RequestMessage.Content.ReadAsStringAsync();
- var options = context.HttpApiConfig.FormatOptions.CloneChange(attr.DateTimeFormat);
- var target = context.HttpApiConfig.JsonFormatter.Serialize(parameter.Value, options);
- Assert.True(body == target);
- }
- }
-}
-
diff --git a/WebApiClient.Test/Attributes/ParameterAttributes/PathQueryAttributeTest.cs b/WebApiClient.Test/Attributes/ParameterAttributes/PathQueryAttributeTest.cs
deleted file mode 100644
index 7f2d63ca..00000000
--- a/WebApiClient.Test/Attributes/ParameterAttributes/PathQueryAttributeTest.cs
+++ /dev/null
@@ -1,45 +0,0 @@
-using System;
-using System.Net.Http;
-using System.Text;
-using System.Threading.Tasks;
-using WebApiClient;
-using WebApiClient.Attributes;
-using WebApiClient.Contexts;
-using Xunit;
-
-namespace WebApiClient.Test.Attributes.HttpActionAttributes
-{
- public class PathQueryAttributeTest
- {
- public interface IMyApi : IDisposable
- {
- ITask PostAsync(object content);
- }
-
- [Fact]
- public async Task BeforeRequestAsyncTest()
- {
- var context = new TestActionContext(
- httpApi: null,
- httpApiConfig: new HttpApiConfig(),
- apiActionDescriptor: new ApiActionDescriptor(typeof(IMyApi).GetMethod("PostAsync")));
-
- context.RequestMessage.RequestUri = new Uri("http://www.webapi.com/");
- context.RequestMessage.Method = HttpMethod.Post;
-
- var parameter = context.ApiActionDescriptor.Parameters[0].Clone(new
- {
- name = "laojiu",
- birthDay = DateTime.Parse("2010-10-10")
- });
-
- var attr = new PathQueryAttribute();
- await ((IApiParameterAttribute)attr).BeforeRequestAsync(context, parameter);
-
- var birthday = context.HttpApiConfig.FormatOptions.CloneChange(attr.DateTimeFormat).FormatDateTime(DateTime.Parse("2010-10-10"));
- var target = new Uri("http://www.webapi.com?name=laojiu&birthDay=" + HttpUtility.UrlEncode(birthday, Encoding.GetEncoding(attr.Encoding)));
- Assert.True(context.RequestMessage.RequestUri == target);
- }
- }
-}
-
diff --git a/WebApiClient.Test/Attributes/ParameterAttributes/UriAttributeTest.cs b/WebApiClient.Test/Attributes/ParameterAttributes/UriAttributeTest.cs
deleted file mode 100644
index 19c938c4..00000000
--- a/WebApiClient.Test/Attributes/ParameterAttributes/UriAttributeTest.cs
+++ /dev/null
@@ -1,37 +0,0 @@
-using System;
-using System.Net.Http;
-using System.Threading.Tasks;
-using WebApiClient;
-using WebApiClient.Attributes;
-using WebApiClient.Contexts;
-using Xunit;
-
-namespace WebApiClient.Test.Attributes.HttpActionAttributes
-{
- public class UriAttributeTest
- {
- public interface IMyApi : IDisposable
- {
- ITask PostAsync(string url);
- }
-
- [Fact]
- public async Task BeforeRequestAsyncTest()
- {
- var context = new TestActionContext(
- httpApi: null,
- httpApiConfig: new HttpApiConfig(),
- apiActionDescriptor: new ApiActionDescriptor(typeof(IMyApi).GetMethod("PostAsync")));
-
- var parameter = context.ApiActionDescriptor.Parameters[0].Clone("http://www.baidu.com");
-
- var attr = new UriAttribute();
- await ((IApiParameterAttribute)attr).BeforeRequestAsync(context, parameter);
- Assert.True(context.RequestMessage.RequestUri == new Uri("http://www.baidu.com"));
-
- parameter = parameter.Clone("/login");
- await ((IApiParameterAttribute)attr).BeforeRequestAsync(context, parameter);
- Assert.True(context.RequestMessage.RequestUri == new Uri("http://www.baidu.com/login"));
- }
- }
-}
diff --git a/WebApiClient.Test/Attributes/ParameterAttributes/XmlContentAttributeTest.cs b/WebApiClient.Test/Attributes/ParameterAttributes/XmlContentAttributeTest.cs
deleted file mode 100644
index 112fc755..00000000
--- a/WebApiClient.Test/Attributes/ParameterAttributes/XmlContentAttributeTest.cs
+++ /dev/null
@@ -1,52 +0,0 @@
-using System;
-using System.Net.Http;
-using System.Text;
-using System.Threading.Tasks;
-using WebApiClient;
-using WebApiClient.Attributes;
-using WebApiClient.Contexts;
-using Xunit;
-
-namespace WebApiClient.Test.Attributes.HttpActionAttributes
-{
- public class XmlContentAttributeTest
- {
- public interface IMyApi : IDisposable
- {
- ITask PostAsync(object content);
- }
-
- public class Model
- {
- public string name { get; set; }
-
- public DateTime birthDay { get; set; }
- }
-
- [Fact]
- public async Task BeforeRequestAsyncTest()
- {
- var context = new TestActionContext(
- httpApi: null,
- httpApiConfig: new HttpApiConfig(),
- apiActionDescriptor: new ApiActionDescriptor(typeof(IMyApi).GetMethod("PostAsync")));
-
- context.RequestMessage.RequestUri = new Uri("http://www.webapi.com/");
- context.RequestMessage.Method = HttpMethod.Post;
-
- var parameter = context.ApiActionDescriptor.Parameters[0].Clone(new Model
- {
- name = "laojiu",
- birthDay = DateTime.Parse("2010-10-10")
- });
-
- var attr = new XmlContentAttribute();
- await ((IApiParameterAttribute)attr).BeforeRequestAsync(context, parameter);
-
- var body = await context.RequestMessage.Content.ReadAsStringAsync();
- var target = context.HttpApiConfig.XmlFormatter.Serialize(parameter.Value, Encoding.UTF8);
- Assert.True(body == target);
- }
- }
-}
-
diff --git a/WebApiClient.Test/Attributes/ReturnAttributes/AutoReturnAttributeTest.cs b/WebApiClient.Test/Attributes/ReturnAttributes/AutoReturnAttributeTest.cs
deleted file mode 100644
index 141f09b3..00000000
--- a/WebApiClient.Test/Attributes/ReturnAttributes/AutoReturnAttributeTest.cs
+++ /dev/null
@@ -1,146 +0,0 @@
-using System;
-using System.Net.Http;
-using System.Text;
-using System.Threading.Tasks;
-using WebApiClient;
-using WebApiClient.Attributes;
-using WebApiClient.Contexts;
-using Xunit;
-
-namespace WebApiClient.Test.Attributes.HttpActionAttributes
-{
- public class AutoReturnAttributeTest
- {
- public interface IMyApi : IDisposable
- {
- ITask HttpResponseMessageAsync();
-
- ITask StringAsync();
-
- ITask ByteArrayAsync();
-
- ITask JsonXmlAsync();
- }
-
- public class Model
- {
- public string Name { get; set; } = "laojiu";
- public int Age { get; set; } = 18;
- }
-
- [Fact]
- public async Task HttpResponseMessageResultTest()
- {
- var context = new TestActionContext(
- httpApi: null,
- httpApiConfig: new HttpApiConfig(),
- apiActionDescriptor: new ApiActionDescriptor(typeof(IMyApi).GetMethod("HttpResponseMessageAsync")));
-
- context.RequestMessage.RequestUri = new Uri("http://www.mywebapi.com");
- context.RequestMessage.Method = HttpMethod.Post;
- context.ResponseMessage.Content = new StringContent("laojiu", Encoding.UTF8);
-
-
- var attr = new AutoReturnAttribute();
- var result = await ((IApiReturnAttribute)attr).GetTaskResult(context);
- Assert.True(result is HttpResponseMessage);
- }
-
- [Fact]
- public async Task StringResultTest()
- {
- var context = new TestActionContext(
- httpApi: null,
- httpApiConfig: new HttpApiConfig(),
- apiActionDescriptor: new ApiActionDescriptor(typeof(IMyApi).GetMethod("StringAsync")));
-
- context.RequestMessage.RequestUri = new Uri("http://www.mywebapi.com");
- context.RequestMessage.Method = HttpMethod.Post;
- context.ResponseMessage.Content = new StringContent("laojiu");
-
- var attr = new AutoReturnAttribute();
- var result = await ((IApiReturnAttribute)attr).GetTaskResult(context);
- Assert.True(result?.ToString() == "laojiu");
- }
-
- [Fact]
- public async Task ByteArrayResultTest()
- {
- var context = new TestActionContext(
- httpApi: null,
- httpApiConfig: new HttpApiConfig(),
- apiActionDescriptor: new ApiActionDescriptor(typeof(IMyApi).GetMethod("ByteArrayAsync")));
-
- context.RequestMessage.RequestUri = new Uri("http://www.mywebapi.com");
- context.RequestMessage.Method = HttpMethod.Post;
- context.ResponseMessage.Content = new StringContent("laojiu", Encoding.UTF8);
-
- var attr = new AutoReturnAttribute();
- var result = await ((IApiReturnAttribute)attr).GetTaskResult(context);
- var text = Encoding.UTF8.GetString((byte[])result);
- Assert.True(text == "laojiu");
- }
-
- [Fact]
- public async Task JsonResultTest()
- {
- var context = new TestActionContext(
- httpApi: null,
- httpApiConfig: new HttpApiConfig(),
- apiActionDescriptor: new ApiActionDescriptor(typeof(IMyApi).GetMethod("JsonXmlAsync")));
-
- context.RequestMessage.RequestUri = new Uri("http://www.mywebapi.com");
- context.RequestMessage.Method = HttpMethod.Post;
-
-
- var model = new Model();
- var json = context.HttpApiConfig.JsonFormatter.Serialize(model, context.HttpApiConfig.FormatOptions);
- context.ResponseMessage.Content = new StringContent(json, Encoding.UTF8, "application/json");
-
- var attr = new AutoReturnAttribute();
- var result = await ((IApiReturnAttribute)attr).GetTaskResult(context) as Model;
- Assert.True(model.Name == result.Name && model.Age == result.Age);
- }
-
- [Fact]
- public async Task XmlResultTest()
- {
- var context = new TestActionContext(
- httpApi: null,
- httpApiConfig: new HttpApiConfig(),
- apiActionDescriptor: new ApiActionDescriptor(typeof(IMyApi).GetMethod("JsonXmlAsync")));
-
- context.RequestMessage.RequestUri = new Uri("http://www.mywebapi.com");
- context.RequestMessage.Method = HttpMethod.Post;
-
-
- var model = new Model();
- var xml = context.HttpApiConfig.XmlFormatter.Serialize(model, Encoding.UTF8);
- context.ResponseMessage.Content = new StringContent(xml, Encoding.UTF8, "application/xml");
-
- var attr = new AutoReturnAttribute();
- var result = await ((IApiReturnAttribute)attr).GetTaskResult(context) as Model;
- Assert.True(model.Name == result.Name && model.Age == result.Age);
- }
-
- [Fact]
- public async Task EnsureSuccessStatusCodeTest()
- {
- var context = new TestActionContext(
- httpApi: null,
- httpApiConfig: new HttpApiConfig(),
- apiActionDescriptor: new ApiActionDescriptor(typeof(IMyApi).GetMethod("JsonXmlAsync")));
-
- context.RequestMessage.RequestUri = new Uri("http://www.mywebapi.com");
- context.RequestMessage.Method = HttpMethod.Post;
- context.ResponseMessage.StatusCode = System.Net.HttpStatusCode.InternalServerError;
-
- var model = new Model();
- var xml = context.HttpApiConfig.XmlFormatter.Serialize(model, Encoding.UTF8);
- context.ResponseMessage.Content = new StringContent(xml, Encoding.UTF8, "application/xml");
-
- var attr = new AutoReturnAttribute() { EnsureSuccessStatusCode = true };
- await Assert.ThrowsAsync(() => ((IApiReturnAttribute)attr).GetTaskResult(context));
- }
- }
-}
\ No newline at end of file
diff --git a/WebApiClient.Test/Attributes/TagsAttributeTest.cs b/WebApiClient.Test/Attributes/TagsAttributeTest.cs
deleted file mode 100644
index 18b175bd..00000000
--- a/WebApiClient.Test/Attributes/TagsAttributeTest.cs
+++ /dev/null
@@ -1,44 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Net.Http;
-using System.Threading.Tasks;
-using WebApiClient;
-using WebApiClient.Attributes;
-using WebApiClient.Contexts;
-using Xunit;
-
-namespace WebApiClient.Test.Attributes
-{
- public class TagsAttributeTest
- {
- [Fact]
- public async Task IApiParameterAttributeTest()
- {
- var context = new TestActionContext(
- httpApi: null,
- httpApiConfig: new HttpApiConfig { HttpHost = new Uri("http://www.mywebapi.com") },
- apiActionDescriptor: new ApiActionDescriptor(typeof(IMyApi).GetMethod("PostAsync")));
-
- var parameter = context.ApiActionDescriptor.Parameters[0].Clone("laojiu");
-
- IApiParameterAttribute attr = new TagsAttribute("key");
- await attr.BeforeRequestAsync(context, parameter);
- Assert.Equal("laojiu", context.Tags.Get("key").As());
- }
-
- [Fact]
- public async Task IApiActionAttributeTest()
- {
- var context = new TestActionContext(
- httpApi: null,
- httpApiConfig: new HttpApiConfig { HttpHost = new Uri("http://www.mywebapi.com") },
- apiActionDescriptor: new ApiActionDescriptor(typeof(IMyApi).GetMethod("PostAsync")));
-
- var attr = new TagsAttribute("key", "laojiu");
- await attr.BeforeRequestAsync(context);
-
- Assert.Equal("laojiu", context.Tags.Get("key").As());
- }
- }
-}
diff --git a/WebApiClient.Test/Attributes/TestActionContext.cs b/WebApiClient.Test/Attributes/TestActionContext.cs
deleted file mode 100644
index 504f429f..00000000
--- a/WebApiClient.Test/Attributes/TestActionContext.cs
+++ /dev/null
@@ -1,22 +0,0 @@
-using System;
-using System.Net.Http;
-using WebApiClient.Contexts;
-
-namespace WebApiClient.Test
-{
- class TestActionContext : ApiActionContext
- {
- ///
- /// 请求Api的上下文
- ///
- /// httpApi代理类实例
- /// 关联的HttpApiConfig
- /// 关联的ApiActionDescriptor
- ///
- public TestActionContext(IHttpApi httpApi, HttpApiConfig httpApiConfig, ApiActionDescriptor apiActionDescriptor)
- : base(httpApi, httpApiConfig, apiActionDescriptor)
- {
- this.ResponseMessage = new HttpResponseMessage(System.Net.HttpStatusCode.OK);
- }
- }
-}
diff --git a/WebApiClient.Test/AuthTokens/AuthTokenFilterTest.cs b/WebApiClient.Test/AuthTokens/AuthTokenFilterTest.cs
deleted file mode 100644
index 380ef9ac..00000000
--- a/WebApiClient.Test/AuthTokens/AuthTokenFilterTest.cs
+++ /dev/null
@@ -1,81 +0,0 @@
-using System;
-using System.Net.Http;
-using System.Threading.Tasks;
-using WebApiClient;
-using WebApiClient.AuthTokens;
-using WebApiClient.Contexts;
-using Xunit;
-
-namespace WebApiClient.Test.AuthTokens
-{
- public class AuthTokenFilterTest
- {
- class TokenFilter : AuthTokenFilter
- {
- public bool IsRequestTokenResult { get; set; } = false;
-
- public bool IsRequestRefreshToken { get; set; } = false;
-
- public TokenResult TokenResult { get; set; }
-
- protected override Task RequestTokenResultAsync()
- {
- this.IsRequestTokenResult = true;
- var token = new TokenResult
- {
- IdToken = "0",
- ExpiresIn = 1,
- AccessToken = "AccessToken",
- RefreshToken = "RefreshToken",
- TokenType = "client_credentials"
- };
- return Task.FromResult(token);
- }
-
- protected override Task RequestRefreshTokenAsync(string refresh_token)
- {
- this.IsRequestRefreshToken = true;
- var token = new TokenResult
- {
- IdToken = "1",
- ExpiresIn = 1,
- AccessToken = "AccessToken",
- RefreshToken = "RefreshToken",
- TokenType = "client_credentials"
- };
- return Task.FromResult(token);
- }
-
- protected override void AccessTokenResult(ApiActionContext context, TokenResult tokenResult)
- {
- this.TokenResult = tokenResult;
- }
- }
-
- [Fact]
- public async Task TokenTestAsync()
- {
- var tokenFilter = new TokenFilter();
- IApiActionFilter filter = tokenFilter;
-
- await filter.OnBeginRequestAsync(null);
- Assert.True(tokenFilter.IsRequestTokenResult && tokenFilter.IsRequestRefreshToken == false && tokenFilter.TokenResult.IdToken == "0");
- await filter.OnEndRequestAsync(null);
- tokenFilter.IsRequestTokenResult = false;
- tokenFilter.IsRequestRefreshToken = false;
-
- await Task.Delay(500);
- await filter.OnBeginRequestAsync(null);
- Assert.True(tokenFilter.IsRequestTokenResult == false && tokenFilter.IsRequestRefreshToken == false && tokenFilter.TokenResult.IdToken == "0");
- await filter.OnEndRequestAsync(null);
- tokenFilter.IsRequestTokenResult = false;
- tokenFilter.IsRequestRefreshToken = false;
-
-
- await Task.Delay(1100);
- await filter.OnBeginRequestAsync(null);
- Assert.True(tokenFilter.IsRequestTokenResult == false && tokenFilter.IsRequestRefreshToken && tokenFilter.TokenResult.IdToken == "1");
- await filter.OnEndRequestAsync(null);
- }
- }
-}
diff --git a/WebApiClient.Test/Contexts/ApiActionDescriptorTest.cs b/WebApiClient.Test/Contexts/ApiActionDescriptorTest.cs
deleted file mode 100644
index e892f011..00000000
--- a/WebApiClient.Test/Contexts/ApiActionDescriptorTest.cs
+++ /dev/null
@@ -1,38 +0,0 @@
-using WebApiClient.Contexts;
-using Xunit;
-
-namespace WebApiClient.Test.Contexts
-{
- public class ApiActionDescriptorTest
- {
- [Fact]
- public void NewTest()
- {
- var m = typeof(IUserApi).GetMethod("Get1");
- var d = new ApiActionDescriptor(m);
-
- Assert.True(d.Attributes.Count == 3);
- Assert.True(d.Filters.Count == 1);
- Assert.True(d.Parameters.Count == 2);
- Assert.True(d.Name == m.Name);
- Assert.True(d.Member == m);
- Assert.True(d.Return.ReturnType == m.ReturnType);
- }
-
- [Fact]
- public void CloneTest()
- {
- var m = typeof(IUserApi).GetMethod("Get1");
- var d = new ApiActionDescriptor(m);
- var d2 = d.Clone(new object[] { null, null });
-
- Assert.True(d.Attributes == d2.Attributes);
- Assert.True(d.Name == d2.Name);
- Assert.True(d.Filters == d2.Filters);
- Assert.True(d.Member == d2.Member);
- Assert.True(d.Parameters != d2.Parameters);
- Assert.True(d.Parameters.Count == d2.Parameters.Count);
- Assert.True(d.Return == d2.Return);
- }
- }
-}
diff --git a/WebApiClient.Test/Contexts/ApiParameterDescriptorTest.cs b/WebApiClient.Test/Contexts/ApiParameterDescriptorTest.cs
deleted file mode 100644
index e0a4f5ee..00000000
--- a/WebApiClient.Test/Contexts/ApiParameterDescriptorTest.cs
+++ /dev/null
@@ -1,50 +0,0 @@
-using WebApiClient.Contexts;
-using Xunit;
-
-namespace WebApiClient.Test.Contexts
-{
- public class ApiParameterDescriptorTest
- {
- [Fact]
- public void NewTest()
- {
- var p1 = typeof(IUserApi).GetMethod("Get2").GetParameters()[0];
- var m1 = new ApiParameterDescriptor(p1);
- Assert.True(m1.Attributes.Count == 1);
- Assert.True(m1.Index == 0);
- Assert.True(m1.Member == p1);
- Assert.True(m1.Name == p1.Name);
- Assert.True(m1.ParameterType == p1.ParameterType);
- Assert.True(m1.ValidationAttributes.Count == 1);
- Assert.True(m1.Value == null);
-
-
- var p2 = typeof(IUserApi).GetMethod("Get2").GetParameters()[1];
- var m2 = new ApiParameterDescriptor(p2);
- Assert.True(m2.Attributes.Count == 1);
- Assert.True(m2.Index == 1);
- Assert.True(m2.Member == p2);
- Assert.True(m2.Name == p2.Name);
- Assert.True(m2.ParameterType == p2.ParameterType);
- Assert.True(m2.ValidationAttributes.Count == 0);
- Assert.True(m2.Value == null);
- }
-
-
- [Fact]
- public void CloneTest()
- {
- var p1 = typeof(IUserApi).GetMethod("Get2").GetParameters()[0];
- var m1 = new ApiParameterDescriptor(p1);
- var m2 = m1.Clone(10);
-
- Assert.True(m1.Attributes == m2.Attributes);
- Assert.True(m1.Name == m2.Name);
- Assert.True(m1.Index == m2.Index);
- Assert.True(m1.ParameterType == m2.ParameterType);
- Assert.True(m1.Member == m2.Member);
- Assert.True(m1.ValidationAttributes == m2.ValidationAttributes);
- Assert.True((int)m2.Value == 10);
- }
- }
-}
diff --git a/WebApiClient.Test/Contexts/ApiReturnDescriptorTest.cs b/WebApiClient.Test/Contexts/ApiReturnDescriptorTest.cs
deleted file mode 100644
index c541c314..00000000
--- a/WebApiClient.Test/Contexts/ApiReturnDescriptorTest.cs
+++ /dev/null
@@ -1,22 +0,0 @@
-using WebApiClient.Attributes;
-using WebApiClient.Contexts;
-using Xunit;
-
-namespace WebApiClient.Test.Contexts
-{
- public class ApiReturnDescriptorTest
- {
- [Fact]
- public void NewTest()
- {
- var m1 = new ApiReturnDescriptor(typeof(IUserApi).GetMethod("Get1"));
- var m2 = new ApiReturnDescriptor(typeof(IUserApi).GetMethod("Get2"));
-
- Assert.True(m1.IsTaskDefinition);
- Assert.True(m1.Attribute.GetType() == typeof(AutoReturnAttribute));
-
- Assert.True(m2.IsITaskDefinition);
- Assert.True(m2.Attribute.GetType() == typeof(JsonReturnAttribute));
- }
- }
-}
diff --git a/WebApiClient.Test/Contexts/DataTypeDescriptorTest.cs b/WebApiClient.Test/Contexts/DataTypeDescriptorTest.cs
deleted file mode 100644
index 18e57941..00000000
--- a/WebApiClient.Test/Contexts/DataTypeDescriptorTest.cs
+++ /dev/null
@@ -1,34 +0,0 @@
-using System.Linq;
-using WebApiClient.Contexts;
-using Xunit;
-
-namespace WebApiClient.Test.Contexts
-{
- public class DataTypeDescriptorTest
- {
- private DataTypeDescriptor Create(string methodName)
- {
- var method = typeof(IUserApi).GetMethod(methodName);
- var dataType = method.ReturnType.GetGenericArguments().FirstOrDefault();
- return new DataTypeDescriptor(dataType);
- }
-
- [Fact]
- public void NewTest()
- {
- var m1 = Create("Get1");
- var m2 = Create("Get2");
- var m3 = Create("Get3");
- var m4 = Create("Get4");
- var m5 = Create("Get5");
- var m6 = Create("Get6");
-
- Assert.True(m1.IsString);
- Assert.True(m2.IsHttpResponseMessage);
- Assert.True(m3.IsStream);
- Assert.True(m4.IsHttpResponseWrapper);
- Assert.True(m6.IsByteArray);
- Assert.False(m5.IsString || m5.IsStream || m5.IsByteArray || m5.IsHttpResponseMessage || m5.IsHttpResponseWrapper);
- }
- }
-}
diff --git a/WebApiClient.Test/Contexts/IUserApi.cs b/WebApiClient.Test/Contexts/IUserApi.cs
deleted file mode 100644
index 5aaf85e8..00000000
--- a/WebApiClient.Test/Contexts/IUserApi.cs
+++ /dev/null
@@ -1,39 +0,0 @@
-using System.ComponentModel.DataAnnotations;
-using System.IO;
-using System.Net.Http;
-using System.Threading;
-using System.Threading.Tasks;
-using WebApiClient.Attributes;
-using WebApiClient.Parameterables;
-
-namespace WebApiClient.Test.Contexts
-{
- ///
- /// 用户操作接口
- ///
- [TraceFilter]
- [HttpHost("http://localhost")]
- public interface IUserApi : IHttpApi
- {
- [HttpGet]
- [Timeout(10 * 1000)]
- Task Get1([Uri] string url, string something);
-
- [HttpGet]
- [JsonReturn]
- ITask Get2([Required]string id, CancellationToken token);
-
- [HttpGet]
- ITask Get3([Required]string account, CancellationToken token);
-
- [HttpGet]
- ITask Get4([Uri, Required] string uri);
-
-
- [HttpGet]
- ITask