ASP.NET WebAPI 体験記 #clrh99 で話した割り切り WebAPI に対応したクライアント自動生成 T4
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* tslint:disable */ | |
namespace App.WebApi { | |
"use strict"; | |
//#region infrastructure | |
angular.module("webApiClient", []) | |
.provider("webApiClientHandler", [function () { | |
this.defaultRequestHeaders = true; | |
this.webApiClientHandlerFactory = function ($http: ng.IHttpService) { | |
let handler = new Core.WebApiClientHandler($http); | |
return handler; | |
}; | |
this.$get = ["$http", this.webApiClientHandlerFactory]; | |
}]) | |
.provider("webapi", [function () { | |
this.webApiClientFactory = | |
function ($q: ng.IQService, rootEndPoint: string, webApiClientHandler: Core.WebApiClientHandler) { | |
let client = new WebApiClient($q, rootEndPoint, webApiClientHandler); | |
client.timeout = this.timeout; | |
Object.keys(this.defaultRequestHeaders || {}).forEach( | |
(x: string) => { | |
client.defaultRequestHeaders[x] = this.defaultRequestHeaders[x]; | |
}); | |
return client; | |
}; | |
this.$get = [ | |
"$q", "webApiClientHandler", | |
function ($q: ng.IQService, webApiClientHandler: Core.WebApiClientHandler) { | |
return this.webApiClientFactory($q, this.rootEndPoint, webApiClientHandler); | |
}]; | |
}]); | |
export namespace Core { | |
/** | |
* Use the annotation of "webApiClientHandlerProvider". | |
*/ | |
export interface IWebApiClientHandlerProvider { | |
webApiClientHandlerFactory: ($http: ng.IHttpService) => WebApiClientHandler; | |
} | |
/** | |
* Use the annotation of "webapiProvider". | |
*/ | |
export interface IWebApiClientProvider { | |
rootEndPoint: string; | |
defaultRequestHeaders: { [key: string]: string }; | |
timeout: number; | |
webApiClientFactory: ($q: ng.IQService, rootEndPoint: string, $http: ng.IHttpService) => WebApiClientBase; | |
} | |
export interface IHttpResponseTransformer<T> { | |
(data: any, headersGetter: ng.IHttpHeadersGetter, status: number): T; | |
} | |
export class ArgumentError extends Error { | |
constructor(message: string = "Value does not fall within the expected range.", paramName?: string) { | |
super(); | |
this.name = "ArgumentError"; | |
this.message = message + (paramName ? ": " + paramName : ""); | |
} | |
} | |
export class ArgumentNullError extends ArgumentError { | |
constructor(paramName?: string, message: string = "Value cannot be null.") { | |
super(message, paramName); | |
this.name = "ArgumentNullError"; | |
} | |
} | |
export class ArgumentOutOfRangeError extends ArgumentError { | |
constructor(paramName?: string, message: string = "Specified argument was out of the range of valid values.") { | |
super(message, paramName); | |
this.name = "ArgumentOutOfRangeError"; | |
} | |
} | |
export class WebApiClientHandler { | |
constructor(private $http: ng.IHttpService) { | |
if (!$http) { | |
throw new ArgumentNullError("$http"); | |
} | |
} | |
protected serializeToFormData(data: any): string { | |
return Object.keys(data || {}) | |
.map((x: string) => { | |
let value = data[x]; | |
if (value === void 0 || value === null) { | |
value = ""; | |
} | |
return encodeURIComponent(x) + "=" + encodeURIComponent(value); | |
}) | |
.join("&"); | |
} | |
public post<T>( | |
url: string, | |
data?: any, | |
timeout?: number | ng.IPromise<void>, | |
requestHeaders?: ng.IHttpRequestConfigHeaders, | |
transformResponse?: IHttpResponseTransformer<T>, | |
responseType?: string): ng.IPromise<T> { | |
if (!url) { | |
throw new ArgumentNullError("url"); | |
} | |
let config = <ng.IRequestShortcutConfig>{ | |
timeout: timeout, | |
headers: requestHeaders, | |
responseType: responseType || "json", | |
transformResponse: transformResponse | |
}; | |
return this.$http.post(url, data, config); | |
} | |
} | |
export class CancellationTokenSource { | |
constructor(private deferred: ng.IDeferred<void>) { | |
if (!deferred) { | |
throw new ArgumentNullError("deferred"); | |
} | |
} | |
public get token(): ng.IPromise<void> { | |
return this.deferred.promise; | |
} | |
public cancel(): void { | |
this.deferred.resolve(); | |
} | |
} | |
export class WebApiClientBase { | |
constructor(private $q: ng.IQService, private rootEndPoint: string, private innerHandler: WebApiClientHandler) { | |
if (!$q) { | |
throw new ArgumentNullError("$q"); | |
} | |
if (!rootEndPoint) { | |
throw new ArgumentNullError("rootEndPoint"); | |
} | |
if (!innerHandler) { | |
throw new ArgumentNullError("innerHandler"); | |
} | |
this.$q = $q; | |
this.rootEndPoint = rootEndPoint.replace(/\/$/, ""); | |
if (!this.rootEndPoint) { | |
throw new ArgumentOutOfRangeError("rootEndPoint"); | |
} | |
this.innerHandler = innerHandler; | |
this._defaultRequestHeaders = { | |
"Content-Type": "application/json", | |
"X-Requested-With": "XMLHttpRequest" | |
}; | |
} | |
private _timeout: number; | |
public get timeout(): number { | |
return this._timeout; | |
} | |
public set timeout(value: number) { | |
if (value <= 0) { | |
throw new ArgumentOutOfRangeError(); | |
} | |
this._timeout = value; | |
} | |
private _defaultRequestHeaders: { [key: string]: string }; | |
public get defaultRequestHeaders(): { [key: string]: string } { | |
return this._defaultRequestHeaders; | |
}; | |
protected post<T>( | |
method: string, | |
data?: any, | |
cancellationToken?: ng.IPromise<void>, | |
transformResponse?: IHttpResponseTransformer<T>, | |
responseType?: string): ng.IPromise<T> { | |
if (!method) { | |
throw new ArgumentNullError("method"); | |
} | |
return this.innerHandler.post( | |
this.rootEndPoint + method, | |
data, | |
cancellationToken || this.timeout, | |
this.defaultRequestHeaders, | |
transformResponse, | |
responseType) | |
.then((x: any) => x.data); | |
} | |
public createCancellationTokenSource(): CancellationTokenSource { | |
return new CancellationTokenSource(this.$q.defer<void>()); | |
}; | |
protected validStatusCode(status: number): boolean { | |
return 200 <= status && status <= 299; | |
} | |
protected parseJSON(json: any): any { | |
return typeof json === "string" | |
? JSON.parse(json) | |
: json; | |
} | |
} | |
} | |
//#endregion | |
export class Person { | |
constructor(json?: any) { | |
this.id = json.Id; | |
this.name = json.Name; | |
} | |
public id: number; | |
public name: string; | |
} | |
export namespace Controllers { | |
export interface ISampleController { | |
getPiyo(cancellationToken?: ng.IPromise<void>): ng.IPromise<Person>; | |
} | |
} | |
/** | |
* Provides webapi methods. | |
* Use the annotation of "webapi". | |
*/ | |
export class WebApiClient extends Core.WebApiClientBase { | |
constructor($q: ng.IQService, rootEndPoint: string, innerHandler: Core.WebApiClientHandler) { | |
super($q, rootEndPoint, innerHandler); | |
} | |
private _sample: Controllers.ISampleController; | |
/** | |
* Provides methods of SampleController. | |
*/ | |
public get sample(): Controllers.ISampleController { | |
if (!this._sample) { | |
this._sample = { | |
getPiyo: this.sampleGetPiyo.bind(this) | |
}; | |
} | |
return this._sample; | |
} | |
protected sampleGetPiyo(cancellationToken?: ng.IPromise<void>): ng.IPromise<Person> { | |
let data = {}; | |
return this.post<Person>("/Sample/GetPiyo", data, cancellationToken, | |
(data: any, headersGetter: ng.IHttpHeadersGetter, status: number) => { | |
if (!this.validStatusCode(status)) { | |
return data; | |
} | |
let json = this.parseJSON(data); | |
return json ? new Person(json) : null; | |
}); | |
} | |
} | |
} | |
/* tslint:enable */ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<#@ template debug="false" hostspecific="true" language="C#" #> | |
<#@ assembly name="System.Core" #> | |
<#@ assembly name="EnvDTE" #> | |
<#@ import namespace="EnvDTE" #> | |
<#@ import namespace="System.Linq" #> | |
<#@ import namespace="System.Text" #> | |
<#@ import namespace="System.Text.RegularExpressions" #> | |
<#@ import namespace="System.Reflection" #> | |
<#@ import namespace="System.Threading" #> | |
<#@ import namespace="System.Threading.Tasks" #> | |
<#@ import namespace="System.Collections.Generic" #> | |
<#@ output extension="ts" encoding="utf-8" #> | |
<#@ assembly name="$(SolutionDir)\WebApplication4\bin\WebApplication4.dll" #> | |
<# | |
// ------------- T4 Configuration ------------- // | |
// 1. Set ApiController assemblies(and all dependency) path to above #@ assembly name # directive | |
// 2. Set Namespace | |
var namespaceName = "App.WebApi"; | |
// 3. Set ModuleName & ClientName(register to angular) | |
var moduleName = "webApiClient"; | |
var clientName = "webapi"; | |
// 3. Set allow upper camel case json(ex: true: json.PersonName, false: json.personName) | |
var allowUpperCamelJson = true; | |
// ----------End T4 Configuration ------------- // | |
var @typeMap = new Dictionary<string, string>() | |
{ | |
{"String", "string"}, | |
{"DateTime", "Date"}, | |
{"Int16", "number"}, | |
{"Int32", "number"}, | |
{"Int64", "number"}, | |
{"Single", "number"}, | |
{"Double", "number"}, | |
{"Decimal", "number"}, | |
{"Boolean", "boolean"}, | |
{"Void", "void"}, | |
{"HttpResponseMessage", "ArrayBuffer"}, | |
{"CancellationToken", "ng.IPromise<void>"} | |
}; | |
Type[] complexTypes = {}; | |
Type[] enumTypes = {}; | |
Type[] parameterTypes = {}; | |
Func<string, string> SwapType = (string name) => | |
{ | |
return typeMap.ContainsKey(name) ? typeMap[name] | |
: complexTypes.Any(x => x.Name == name) || enumTypes.Any(x => x.Name == name) ? name | |
: parameterTypes.Any(x => x.Name == name) ? "I" + name | |
: "any"; | |
}; | |
Func<Type, bool> IsEnumerableType = (Type t) => | |
{ | |
return t != typeof(string) | |
&& ((t.IsGenericType && t.GetGenericTypeDefinition() == typeof(IEnumerable<>)) | |
|| t.GetInterfaces().Any(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(IEnumerable<>))); | |
}; | |
Func<Type, string> BeautifyType = null; | |
BeautifyType = (Type t) => | |
{ | |
if (t.IsArray) return BeautifyType(t.GetElementType()) + "[]"; | |
if (!t.IsGenericType) return SwapType(t.Name); | |
if (t.GetGenericTypeDefinition() == typeof(Nullable<>)) return BeautifyType(t.GetGenericArguments()[0]); | |
if (IsEnumerableType(t)) return BeautifyType(t.GetGenericArguments()[0]) + "[]"; | |
var innerFormat = string.Join(", ", t.GetGenericArguments().Select(x => BeautifyType(x))); | |
return Regex.Replace(t.GetGenericTypeDefinition().Name, @"`.+$", "") + "<" + innerFormat + ">"; | |
}; | |
Func<Type, Type> UnWrapNullableOrSelf = (Type t) => | |
{ | |
return t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Nullable<>) ? t.GetGenericArguments()[0] : t; | |
}; | |
Func<Type, Type> UnWrapEnumerableOrSelf = (Type t) => | |
{ | |
return t.IsArray ? t.GetElementType() | |
: t.IsGenericType && IsEnumerableType(t) ? t.GetGenericArguments()[0] : t; | |
}; | |
Func<Type, bool> IsComplexType = (Type t) => | |
{ | |
var beautifyType = BeautifyType(t); | |
return typeMap.Values.All(x => x != beautifyType && x + "[]" != beautifyType); | |
}; | |
Func<Type, IEnumerable<Type>> FindComplexTypes = null; | |
FindComplexTypes = (Type t) => | |
{ | |
var complexTypeProperties = t.GetProperties() | |
.Where(x => x.CanRead) | |
.Select(x => x.PropertyType) | |
.Select(x => UnWrapEnumerableOrSelf(x)) | |
.Select(x => UnWrapNullableOrSelf(x)) | |
.Where(x => IsComplexType(x)) | |
.Where(x => x != t) | |
.Where(x => x != typeof(object)) | |
.SelectMany(x => FindComplexTypes(x)) | |
.ToArray(); | |
if(complexTypeProperties.Any()) | |
{ | |
return (new Type[]{t}).Concat(complexTypeProperties); | |
} | |
else | |
{ | |
return new Type[]{t}; | |
} | |
}; | |
Func<string, string> ToLowerCamelCase = (string value) => | |
{ | |
return Regex.Replace(value, "^(.)(.*)", x => x.Groups[1].Value.ToLower() + x.Groups[2].Value); | |
}; | |
Func<Type, bool> ShouldCreateInstance = (Type t) => | |
{ | |
var beautifyType = BeautifyType(t).TrimEnd('[', ']'); | |
return beautifyType == "Date" || complexTypes.Any(x => x.Name == beautifyType); | |
}; | |
var @typeFromAssemblies = System.AppDomain.CurrentDomain | |
.GetAssemblies() | |
.Where(x => !Regex.IsMatch(x.GetName().Name, "^(mscorlib|System|EnvDTE)$")) | |
.SelectMany(x => x.GetTypes()) | |
.Where(x => x != null && x.FullName != "System.Web.Http.ApiController"); | |
var @ignoreMethods = new HashSet<string> { "Equals", "GetHashCode", "GetType", "ToString" }; | |
var @controllerTypes = typeFromAssemblies | |
.Where(x => | |
{ | |
while (x != typeof(object) && x != null) | |
{ | |
if (x.FullName == "System.Web.Http.ApiController") return true; | |
x = x.BaseType; | |
} | |
return false; | |
}) | |
.Select(x => | |
{ | |
var actions = x.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly) | |
.Where(methodInfo => !(methodInfo.IsSpecialName && (methodInfo.Name.StartsWith("set_") || methodInfo.Name.StartsWith("get_")))) | |
.Where(methodInfo => !ignoreMethods.Contains(methodInfo.Name)) | |
.Select(methodInfo => | |
{ | |
var retType = methodInfo.ReturnType; | |
var returnType = | |
(retType == typeof(Task)) ? typeof(void) | |
: (retType.IsGenericType && retType.GetGenericTypeDefinition() == typeof(Task<>)) ? retType.GetGenericArguments()[0] | |
: retType; | |
return new | |
{ | |
Info = methodInfo, | |
ReturnType = returnType, | |
Parameters = methodInfo.GetParameters() | |
.Select(paramInfo => new | |
{ | |
Info = paramInfo, | |
ParameterType = paramInfo.ParameterType | |
}) | |
.ToArray() | |
}; | |
}) | |
.ToArray(); | |
return new | |
{ | |
Type = x, | |
Actions = actions | |
}; | |
}) | |
.ToArray(); | |
var @shouldDefineReturnTypes = controllerTypes | |
.SelectMany(x => x.Actions) | |
.Select(x => x.ReturnType) | |
.Select(x => UnWrapEnumerableOrSelf(x)) | |
.Select(x => UnWrapNullableOrSelf(x)) | |
.Where(x => IsComplexType(x)) | |
.SelectMany(x => FindComplexTypes(x)) | |
.ToArray(); | |
var @shouldDefineParameterTypes = controllerTypes | |
.SelectMany(x => x.Actions) | |
.SelectMany(x => x.Parameters) | |
.Select(x => x.ParameterType) | |
.Select(x => UnWrapEnumerableOrSelf(x)) | |
.Select(x => UnWrapNullableOrSelf(x)) | |
.Where(x => IsComplexType(x)) | |
.SelectMany(x => FindComplexTypes(x)) | |
.ToArray(); | |
complexTypes = shouldDefineReturnTypes | |
.Concat(shouldDefineReturnTypes) | |
.Where(x => x.IsClass) | |
.Distinct() | |
.ToArray(); | |
enumTypes = shouldDefineReturnTypes | |
.Concat(shouldDefineParameterTypes) | |
.Where(x => x.IsEnum) | |
.Distinct() | |
.ToArray(); | |
parameterTypes = shouldDefineParameterTypes | |
.Where(x => x.IsClass) | |
.Distinct() | |
.ToArray(); | |
var controllers = controllerTypes | |
.Select(x => | |
{ | |
var actions = x.Actions | |
.Select(action => | |
{ | |
var parameters = action.Parameters | |
.Select(p => new | |
{ | |
p.Info.Name, | |
p.Info.ParameterType, | |
p.Info.IsOptional, | |
p.Info.DefaultValue, | |
IsBoolean = p.Info.ParameterType == typeof(Boolean), | |
IsCancellationToken = false, | |
}) | |
.Concat(new[]{new | |
{ | |
Name = "cancellationToken", | |
ParameterType = typeof(CancellationToken), | |
IsOptional = true, | |
DefaultValue = (object)default(CancellationToken), | |
IsBoolean = false, | |
IsCancellationToken = true, | |
}}) | |
.ToArray(); | |
var parameterString = string.Join(", ", parameters.Select(p => | |
{ | |
return p.Name + (p.IsOptional ? "?" : "") + ": " + BeautifyType(p.ParameterType); | |
})); | |
var parameterStringWithOptional = string.Join(", ", parameters.Select(p => | |
{ | |
var withoutValue = p.Name + (p.IsOptional ? "?" : "") + ": " + BeautifyType(p.ParameterType); | |
if (p.IsOptional) | |
{ | |
var withValueBase = p.Name + ": " + BeautifyType(p.ParameterType); | |
return ( | |
(p.DefaultValue == null) ? withoutValue | |
: (p.DefaultValue is string) ? withValueBase + " = \"" + p.DefaultValue + "\"" | |
: (p.DefaultValue is CancellationToken) ? withoutValue | |
: (p.ParameterType.IsEnum) ? withValueBase + " = " + p.ParameterType.Name + "." + p.DefaultValue.ToString() | |
: withValueBase + " = " + p.DefaultValue.ToString().ToLower()); | |
} else { | |
return withoutValue; | |
} | |
})); | |
return new | |
{ | |
Name = action.Info.Name, | |
ReturnType = action.ReturnType, | |
IsReturnBinary = action.ReturnType.Name == "HttpResponseMessage", | |
IsComplexTypeParameter = parameterTypes.Any(p => p == parameters.First().ParameterType), | |
Parameters = parameters, | |
ParameterString = parameterString, | |
ParameterStringWithOptional = parameterStringWithOptional | |
}; | |
}) | |
.ToArray(); | |
return new | |
{ | |
Name = Regex.Replace(x.Type.Name, "^(.*)Controller$", "$1"), | |
InterfaceName = "I" + x.Type.Name, | |
Actions = actions | |
}; | |
}) | |
.ToArray(); | |
#> | |
/* tslint:disable */ | |
namespace <#= namespaceName #> { | |
"use strict"; | |
//#region infrastructure | |
angular.module("<#= moduleName #>", []) | |
.provider("webApiClientHandler", [function () { | |
this.defaultRequestHeaders = true; | |
this.webApiClientHandlerFactory = function ($http: ng.IHttpService) { | |
let handler = new Core.WebApiClientHandler($http); | |
return handler; | |
}; | |
this.$get = ["$http", this.webApiClientHandlerFactory]; | |
}]) | |
.provider("<#= clientName #>", [function () { | |
this.webApiClientFactory = | |
function ($q: ng.IQService, rootEndPoint: string, webApiClientHandler: Core.WebApiClientHandler) { | |
let client = new WebApiClient($q, rootEndPoint, webApiClientHandler); | |
client.timeout = this.timeout; | |
Object.keys(this.defaultRequestHeaders || {}).forEach( | |
(x: string) => { | |
client.defaultRequestHeaders[x] = this.defaultRequestHeaders[x]; | |
}); | |
return client; | |
}; | |
this.$get = [ | |
"$q", "webApiClientHandler", | |
function ($q: ng.IQService, webApiClientHandler: Core.WebApiClientHandler) { | |
return this.webApiClientFactory($q, this.rootEndPoint, webApiClientHandler); | |
}]; | |
}]); | |
export namespace Core { | |
/** | |
* Use the annotation of "webApiClientHandlerProvider". | |
*/ | |
export interface IWebApiClientHandlerProvider { | |
webApiClientHandlerFactory: ($http: ng.IHttpService) => WebApiClientHandler; | |
} | |
/** | |
* Use the annotation of "<#= clientName #>Provider". | |
*/ | |
export interface IWebApiClientProvider { | |
rootEndPoint: string; | |
defaultRequestHeaders: { [key: string]: string }; | |
timeout: number; | |
webApiClientFactory: ($q: ng.IQService, rootEndPoint: string, $http: ng.IHttpService) => WebApiClientBase; | |
} | |
export interface IHttpResponseTransformer<T> { | |
(data: any, headersGetter: ng.IHttpHeadersGetter, status: number): T; | |
} | |
export class ArgumentError extends Error { | |
constructor(message: string = "Value does not fall within the expected range.", paramName?: string) { | |
super(); | |
this.name = "ArgumentError"; | |
this.message = message + (paramName ? ": " + paramName : ""); | |
} | |
} | |
export class ArgumentNullError extends ArgumentError { | |
constructor(paramName?: string, message: string = "Value cannot be null.") { | |
super(message, paramName); | |
this.name = "ArgumentNullError"; | |
} | |
} | |
export class ArgumentOutOfRangeError extends ArgumentError { | |
constructor(paramName?: string, message: string = "Specified argument was out of the range of valid values.") { | |
super(message, paramName); | |
this.name = "ArgumentOutOfRangeError"; | |
} | |
} | |
export class WebApiClientHandler { | |
constructor(private $http: ng.IHttpService) { | |
if (!$http) { | |
throw new ArgumentNullError("$http"); | |
} | |
} | |
protected serializeToFormData(data: any): string { | |
return Object.keys(data || {}) | |
.map((x: string) => { | |
let value = data[x]; | |
if (value === void 0 || value === null) { | |
value = ""; | |
} | |
return encodeURIComponent(x) + "=" + encodeURIComponent(value); | |
}) | |
.join("&"); | |
} | |
public post<T>( | |
url: string, | |
data?: any, | |
timeout?: number | ng.IPromise<void>, | |
requestHeaders?: ng.IHttpRequestConfigHeaders, | |
transformResponse?: IHttpResponseTransformer<T>, | |
responseType?: string): ng.IPromise<T> { | |
if (!url) { | |
throw new ArgumentNullError("url"); | |
} | |
let config = <ng.IRequestShortcutConfig>{ | |
timeout: timeout, | |
headers: requestHeaders, | |
responseType: responseType || "json", | |
transformResponse: transformResponse | |
}; | |
return this.$http.post(url, data, config); | |
} | |
} | |
export class CancellationTokenSource { | |
constructor(private deferred: ng.IDeferred<void>) { | |
if (!deferred) { | |
throw new ArgumentNullError("deferred"); | |
} | |
} | |
public get token(): ng.IPromise<void> { | |
return this.deferred.promise; | |
} | |
public cancel(): void { | |
this.deferred.resolve(); | |
} | |
} | |
export class WebApiClientBase { | |
constructor(private $q: ng.IQService, private rootEndPoint: string, private innerHandler: WebApiClientHandler) { | |
if (!$q) { | |
throw new ArgumentNullError("$q"); | |
} | |
if (!rootEndPoint) { | |
throw new ArgumentNullError("rootEndPoint"); | |
} | |
if (!innerHandler) { | |
throw new ArgumentNullError("innerHandler"); | |
} | |
this.$q = $q; | |
this.rootEndPoint = rootEndPoint.replace(/\/$/, ""); | |
if (!this.rootEndPoint) { | |
throw new ArgumentOutOfRangeError("rootEndPoint"); | |
} | |
this.innerHandler = innerHandler; | |
this._defaultRequestHeaders = { | |
"Content-Type": "application/json", | |
"X-Requested-With": "XMLHttpRequest" | |
}; | |
} | |
private _timeout: number; | |
public get timeout(): number { | |
return this._timeout; | |
} | |
public set timeout(value: number) { | |
if (value <= 0) { | |
throw new ArgumentOutOfRangeError(); | |
} | |
this._timeout = value; | |
} | |
private _defaultRequestHeaders: { [key: string]: string }; | |
public get defaultRequestHeaders(): { [key: string]: string } { | |
return this._defaultRequestHeaders; | |
}; | |
protected post<T>( | |
method: string, | |
data?: any, | |
cancellationToken?: ng.IPromise<void>, | |
transformResponse?: IHttpResponseTransformer<T>, | |
responseType?: string): ng.IPromise<T> { | |
if (!method) { | |
throw new ArgumentNullError("method"); | |
} | |
return this.innerHandler.post( | |
this.rootEndPoint + method, | |
data, | |
cancellationToken || this.timeout, | |
this.defaultRequestHeaders, | |
transformResponse, | |
responseType) | |
.then((x: any) => x.data); | |
} | |
public createCancellationTokenSource(): CancellationTokenSource { | |
return new CancellationTokenSource(this.$q.defer<void>()); | |
}; | |
protected validStatusCode(status: number): boolean { | |
return 200 <= status && status <= 299; | |
} | |
protected parseJSON(json: any): any { | |
return typeof json === "string" | |
? JSON.parse(json) | |
: json; | |
} | |
} | |
} | |
//#endregion | |
<# foreach(var enumType in enumTypes) { #> | |
export enum <#= enumType.Name #> { | |
<# foreach(var enumMember in Enum.GetNames(enumType).Select(x => new { Name = x, Value = ((Enum)Enum.Parse(enumType, x)).ToString("d") }).ToArray()) { #> | |
<#= enumMember.Name #> = <#= enumMember.Value #><#= Enum.GetNames(enumType).Last() == enumMember.Name ? "" : "," #> | |
<# } #> | |
} | |
<# } #> | |
<# foreach(var parameterType in parameterTypes) { #> | |
export interface I<#= parameterType.Name #> { | |
<# foreach(var p in parameterType.GetProperties().Where(x => x.CanRead).ToArray()) { #> | |
<#= ToLowerCamelCase(p.Name) #>: <#= BeautifyType(p.PropertyType) #>; | |
<# } #> | |
} | |
<# } #> | |
<# foreach(var complexType in complexTypes) { #> | |
export class <#= complexType.Name #> { | |
constructor(json?: any) { | |
<# foreach(var p in complexType.GetProperties().Where(x => x.CanRead).ToArray()) { #> | |
<# if(ShouldCreateInstance(p.PropertyType)) { #> | |
<# if(IsEnumerableType(p.PropertyType)) { #> | |
this.<#= ToLowerCamelCase(p.Name) #> = (<any[]>json.<#= p.Name #> || []).map((x: any) => x ? new <#= BeautifyType(p.PropertyType).TrimEnd('[', ']') #>(x) : null); | |
<# } else { #> | |
this.<#= ToLowerCamelCase(p.Name) #> = json.<#= allowUpperCamelJson ? p.Name : ToLowerCamelCase(p.Name) #> ? new <#= BeautifyType(p.PropertyType) #>(json.<#= allowUpperCamelJson ? p.Name : ToLowerCamelCase(p.Name) #>) : null; | |
<# } #> | |
<# } else { #> | |
this.<#= ToLowerCamelCase(p.Name) #> = json.<#= allowUpperCamelJson ? p.Name : ToLowerCamelCase(p.Name) #><#= IsEnumerableType(p.PropertyType) ? " || []" : "" #>; | |
<# } #> | |
<# } #> | |
} | |
<# foreach(var p in complexType.GetProperties().Where(x => x.CanRead).ToArray()) { #> | |
public <#= ToLowerCamelCase(p.Name) #>: <#= BeautifyType(p.PropertyType) #>; | |
<# } #> | |
} | |
<# } #> | |
export namespace Controllers { | |
<# foreach(var controller in controllers) { #> | |
export interface <#= controller.InterfaceName #> { | |
<# foreach(var action in controller.Actions) { #> | |
<#= ToLowerCamelCase(action.Name) #>(<#= action.ParameterString #>): ng.IPromise<<#= BeautifyType(action.ReturnType) #>>; | |
<# } #> | |
} | |
<# } #> | |
} | |
/** | |
* Provides webapi methods. | |
* Use the annotation of "<#= clientName #>". | |
*/ | |
export class WebApiClient extends Core.WebApiClientBase { | |
constructor($q: ng.IQService, rootEndPoint: string, innerHandler: Core.WebApiClientHandler) { | |
super($q, rootEndPoint, innerHandler); | |
} | |
<# foreach(var controller in controllers) { #> | |
private _<#= ToLowerCamelCase(controller.Name) #>: Controllers.<#= controller.InterfaceName #>; | |
/** | |
* Provides methods of <#= controller.Name #>Controller. | |
*/ | |
public get <#= ToLowerCamelCase(controller.Name) #>(): Controllers.<#= controller.InterfaceName #> { | |
if (!this._<#= ToLowerCamelCase(controller.Name) #>) { | |
this._<#= ToLowerCamelCase(controller.Name) #> = { | |
<# for(var i = 0; i < controller.Actions.Length; i++) { #> | |
<#= ToLowerCamelCase(controller.Actions[i].Name) #>: this.<#= ToLowerCamelCase(controller.Name) #><#= controller.Actions[i].Name #>.bind(this)<#= i == controller.Actions.Length - 1 ? "" : "," #> | |
<# } #> | |
}; | |
} | |
return this._<#= ToLowerCamelCase(controller.Name) #>; | |
} | |
<# } #> | |
<# foreach(var controller in controllers) { #> | |
<# foreach(var action in controller.Actions) { #> | |
protected <#= ToLowerCamelCase(controller.Name) #><#= action.Name #>(<#= action.ParameterStringWithOptional #>): ng.IPromise<<#= BeautifyType(action.ReturnType) #>> { | |
<# if(action.IsComplexTypeParameter) { #> | |
let data = <#= ToLowerCamelCase(action.Parameters.First().Name) #> | |
<# } else if(action.Parameters.Any(x => !x.IsCancellationToken)) { #> | |
let data = { | |
<# foreach(var parameter in action.Parameters.Where(x => !x.IsCancellationToken)) { #> | |
"<#= ToLowerCamelCase(parameter.Name) #>": <#= parameter.IsBoolean ? "!!" : "" #><#= ToLowerCamelCase(parameter.Name) #><#= action.Parameters.Where(x => !x.IsCancellationToken).Last() == parameter ? "" : "," #> | |
<# } #> | |
}; | |
<# } else { #> | |
let data = {}; | |
<# } #> | |
<# if(action.ReturnType == typeof(void) ) { #> | |
return this.post<<#= BeautifyType(action.ReturnType) #>>("/<#= controller.Name #>/<#= action.Name #>", data, cancellationToken); | |
<# } else { #> | |
return this.post<<#= BeautifyType(action.ReturnType) #>>("/<#= controller.Name #>/<#= action.Name #>", data, cancellationToken, | |
(data: any, headersGetter: ng.IHttpHeadersGetter, status: number) => { | |
<# if(ShouldCreateInstance(action.ReturnType)) { #> | |
if (!this.validStatusCode(status)) { | |
return data; | |
} | |
<# if(IsEnumerableType(action.ReturnType)) { #> | |
return (<any[]>this.parseJSON(data) || []).map((x: any) => x ? new <#= BeautifyType(action.ReturnType).TrimEnd('[', ']') #>(x) : null); | |
<# } else { #> | |
let json = this.parseJSON(data); | |
return json ? new <#= BeautifyType(action.ReturnType) #>(json) : null; | |
<# } #> | |
<# } else { #> | |
return <#= action.IsReturnBinary ? "" : "this.parseJSON(" #>data<#= action.IsReturnBinary ? "" : ")" #>; | |
<# } #> | |
}<#= action.IsReturnBinary ? @", ""arraybuffer""" : "" #>); | |
<# } #> | |
} | |
<# } #> | |
<# } #> | |
} | |
} | |
/* tslint:enable */ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
LightNode を参考にしました。