localization fixes
This commit is contained in:
parent
a6e7438987
commit
0a03d7ad9f
|
@ -215,6 +215,13 @@
|
||||||
<Compile Include="Security\RegRecord.cs" />
|
<Compile Include="Security\RegRecord.cs" />
|
||||||
<Compile Include="ServerManager\ServerManager.cs" />
|
<Compile Include="ServerManager\ServerManager.cs" />
|
||||||
<Compile Include="ServerManager\WebSocketConnection.cs" />
|
<Compile Include="ServerManager\WebSocketConnection.cs" />
|
||||||
|
<Compile Include="Services\ServiceMethod.cs" />
|
||||||
|
<Compile Include="Services\ResponseHelper.cs" />
|
||||||
|
<Compile Include="Services\HttpResult.cs" />
|
||||||
|
<Compile Include="Services\RequestHelper.cs" />
|
||||||
|
<Compile Include="Services\ServiceHandler.cs" />
|
||||||
|
<Compile Include="Services\ServiceController.cs" />
|
||||||
|
<Compile Include="Services\ServiceExec.cs" />
|
||||||
<Compile Include="Session\HttpSessionController.cs" />
|
<Compile Include="Session\HttpSessionController.cs" />
|
||||||
<Compile Include="Session\SessionManager.cs" />
|
<Compile Include="Session\SessionManager.cs" />
|
||||||
<Compile Include="Session\SessionWebSocketListener.cs" />
|
<Compile Include="Session\SessionWebSocketListener.cs" />
|
||||||
|
|
|
@ -3,7 +3,6 @@ using MediaBrowser.Controller.Configuration;
|
||||||
using MediaBrowser.Controller.Net;
|
using MediaBrowser.Controller.Net;
|
||||||
using MediaBrowser.Model.Logging;
|
using MediaBrowser.Model.Logging;
|
||||||
using ServiceStack;
|
using ServiceStack;
|
||||||
using ServiceStack.Host;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
@ -13,6 +12,7 @@ using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Emby.Server.Implementations.HttpServer;
|
using Emby.Server.Implementations.HttpServer;
|
||||||
using Emby.Server.Implementations.HttpServer.SocketSharp;
|
using Emby.Server.Implementations.HttpServer.SocketSharp;
|
||||||
|
using Emby.Server.Implementations.Services;
|
||||||
using MediaBrowser.Common.Net;
|
using MediaBrowser.Common.Net;
|
||||||
using MediaBrowser.Common.Security;
|
using MediaBrowser.Common.Security;
|
||||||
using MediaBrowser.Controller;
|
using MediaBrowser.Controller;
|
||||||
|
@ -61,12 +61,15 @@ namespace Emby.Server.Implementations.HttpServer
|
||||||
private readonly Func<Type, Func<string, object>> _funcParseFn;
|
private readonly Func<Type, Func<string, object>> _funcParseFn;
|
||||||
private readonly bool _enableDualModeSockets;
|
private readonly bool _enableDualModeSockets;
|
||||||
|
|
||||||
|
public List<Action<IRequest, IResponse, object>> RequestFilters { get; set; }
|
||||||
|
private Dictionary<Type, Type> ServiceOperationsMap = new Dictionary<Type, Type>();
|
||||||
|
|
||||||
public HttpListenerHost(IServerApplicationHost applicationHost,
|
public HttpListenerHost(IServerApplicationHost applicationHost,
|
||||||
ILogger logger,
|
ILogger logger,
|
||||||
IServerConfigurationManager config,
|
IServerConfigurationManager config,
|
||||||
string serviceName,
|
string serviceName,
|
||||||
string defaultRedirectPath, INetworkManager networkManager, IMemoryStreamFactory memoryStreamProvider, ITextEncoding textEncoding, ISocketFactory socketFactory, ICryptoProvider cryptoProvider, IJsonSerializer jsonSerializer, IXmlSerializer xmlSerializer, IEnvironmentInfo environment, ICertificate certificate, IStreamFactory streamFactory, Func<Type, Func<string, object>> funcParseFn, bool enableDualModeSockets)
|
string defaultRedirectPath, INetworkManager networkManager, IMemoryStreamFactory memoryStreamProvider, ITextEncoding textEncoding, ISocketFactory socketFactory, ICryptoProvider cryptoProvider, IJsonSerializer jsonSerializer, IXmlSerializer xmlSerializer, IEnvironmentInfo environment, ICertificate certificate, IStreamFactory streamFactory, Func<Type, Func<string, object>> funcParseFn, bool enableDualModeSockets)
|
||||||
: base(serviceName)
|
: base()
|
||||||
{
|
{
|
||||||
_appHost = applicationHost;
|
_appHost = applicationHost;
|
||||||
DefaultRedirectPath = defaultRedirectPath;
|
DefaultRedirectPath = defaultRedirectPath;
|
||||||
|
@ -85,6 +88,8 @@ namespace Emby.Server.Implementations.HttpServer
|
||||||
_config = config;
|
_config = config;
|
||||||
|
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
|
|
||||||
|
RequestFilters = new List<Action<IRequest, IResponse, object>>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public string GlobalResponse { get; set; }
|
public string GlobalResponse { get; set; }
|
||||||
|
@ -99,18 +104,7 @@ namespace Emby.Server.Implementations.HttpServer
|
||||||
{typeof (ArgumentException), 400}
|
{typeof (ArgumentException), 400}
|
||||||
};
|
};
|
||||||
|
|
||||||
public override void Configure()
|
protected ILogger Logger
|
||||||
{
|
|
||||||
var requestFilters = _appHost.GetExports<IRequestFilter>().ToList();
|
|
||||||
foreach (var filter in requestFilters)
|
|
||||||
{
|
|
||||||
GlobalRequestFilters.Add(filter.Filter);
|
|
||||||
}
|
|
||||||
|
|
||||||
GlobalResponseFilters.Add(new ResponseFilter(_logger).FilterResponse);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override ILogger Logger
|
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
|
@ -118,32 +112,73 @@ namespace Emby.Server.Implementations.HttpServer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override T Resolve<T>()
|
|
||||||
{
|
|
||||||
return _appHost.Resolve<T>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override T TryResolve<T>()
|
|
||||||
{
|
|
||||||
return _appHost.TryResolve<T>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override object CreateInstance(Type type)
|
public override object CreateInstance(Type type)
|
||||||
{
|
{
|
||||||
return _appHost.CreateInstance(type);
|
return _appHost.CreateInstance(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override ServiceController CreateServiceController()
|
private ServiceController CreateServiceController()
|
||||||
{
|
{
|
||||||
var types = _restServices.Select(r => r.GetType()).ToArray();
|
var types = _restServices.Select(r => r.GetType()).ToArray();
|
||||||
|
|
||||||
return new ServiceController(() => types);
|
return new ServiceController(() => types);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override ServiceStackHost Start(string listeningAtUrlBase)
|
/// <summary>
|
||||||
|
/// Applies the request filters. Returns whether or not the request has been handled
|
||||||
|
/// and no more processing should be done.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public void ApplyRequestFilters(IRequest req, IResponse res, object requestDto)
|
||||||
{
|
{
|
||||||
StartListener();
|
//Exec all RequestFilter attributes with Priority < 0
|
||||||
return this;
|
var attributes = GetRequestFilterAttributes(requestDto.GetType());
|
||||||
|
var i = 0;
|
||||||
|
for (; i < attributes.Length && attributes[i].Priority < 0; i++)
|
||||||
|
{
|
||||||
|
var attribute = attributes[i];
|
||||||
|
attribute.RequestFilter(req, res, requestDto);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Exec global filters
|
||||||
|
foreach (var requestFilter in RequestFilters)
|
||||||
|
{
|
||||||
|
requestFilter(req, res, requestDto);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Exec remaining RequestFilter attributes with Priority >= 0
|
||||||
|
for (; i < attributes.Length && attributes[i].Priority >= 0; i++)
|
||||||
|
{
|
||||||
|
var attribute = attributes[i];
|
||||||
|
attribute.RequestFilter(req, res, requestDto);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Type GetServiceTypeByRequest(Type requestType)
|
||||||
|
{
|
||||||
|
Type serviceType;
|
||||||
|
ServiceOperationsMap.TryGetValue(requestType, out serviceType);
|
||||||
|
return serviceType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddServiceInfo(Type serviceType, Type requestType, Type responseType)
|
||||||
|
{
|
||||||
|
ServiceOperationsMap[requestType] = serviceType;
|
||||||
|
}
|
||||||
|
|
||||||
|
private IHasRequestFilter[] GetRequestFilterAttributes(Type requestDtoType)
|
||||||
|
{
|
||||||
|
var attributes = requestDtoType.AllAttributes().OfType<IHasRequestFilter>().ToList();
|
||||||
|
|
||||||
|
var serviceType = GetServiceTypeByRequest(requestDtoType);
|
||||||
|
if (serviceType != null)
|
||||||
|
{
|
||||||
|
attributes.AddRange(serviceType.AllAttributes().OfType<IHasRequestFilter>());
|
||||||
|
}
|
||||||
|
|
||||||
|
attributes.Sort((x, y) => x.Priority - y.Priority);
|
||||||
|
|
||||||
|
return attributes.ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -531,11 +566,11 @@ namespace Emby.Server.Implementations.HttpServer
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var handler = HttpHandlerFactory.GetHandler(httpReq, _logger);
|
var handler = GetServiceHandler(httpReq);
|
||||||
|
|
||||||
if (handler != null)
|
if (handler != null)
|
||||||
{
|
{
|
||||||
await handler.ProcessRequestAsync(httpReq, httpRes, operationName).ConfigureAwait(false);
|
await handler.ProcessRequestAsync(this, httpReq, httpRes, Logger, operationName).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -565,6 +600,35 @@ namespace Emby.Server.Implementations.HttpServer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Entry point for HttpListener
|
||||||
|
public ServiceHandler GetServiceHandler(IHttpRequest httpReq)
|
||||||
|
{
|
||||||
|
var pathInfo = httpReq.PathInfo;
|
||||||
|
|
||||||
|
var pathParts = pathInfo.TrimStart('/').Split('/');
|
||||||
|
if (pathParts.Length == 0)
|
||||||
|
{
|
||||||
|
_logger.Error("Path parts empty for PathInfo: {0}, Url: {1}", pathInfo, httpReq.RawUrl);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
string contentType;
|
||||||
|
var restPath = ServiceHandler.FindMatchingRestPath(httpReq.HttpMethod, pathInfo, _logger, out contentType);
|
||||||
|
|
||||||
|
if (restPath != null)
|
||||||
|
{
|
||||||
|
return new ServiceHandler
|
||||||
|
{
|
||||||
|
RestPath = restPath,
|
||||||
|
ResponseContentType = contentType
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
_logger.Error("Could not find handler for {0}", pathInfo);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private void Write(IResponse response, string text)
|
private void Write(IResponse response, string text)
|
||||||
{
|
{
|
||||||
var bOutput = Encoding.UTF8.GetBytes(text);
|
var bOutput = Encoding.UTF8.GetBytes(text);
|
||||||
|
@ -580,6 +644,7 @@ namespace Emby.Server.Implementations.HttpServer
|
||||||
httpRes.AddHeader("Location", url);
|
httpRes.AddHeader("Location", url);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ServiceController ServiceController { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Adds the rest handlers.
|
/// Adds the rest handlers.
|
||||||
|
@ -593,12 +658,22 @@ namespace Emby.Server.Implementations.HttpServer
|
||||||
|
|
||||||
_logger.Info("Calling ServiceStack AppHost.Init");
|
_logger.Info("Calling ServiceStack AppHost.Init");
|
||||||
|
|
||||||
base.Init();
|
Instance = this;
|
||||||
|
|
||||||
|
ServiceController.Init(this);
|
||||||
|
|
||||||
|
var requestFilters = _appHost.GetExports<IRequestFilter>().ToList();
|
||||||
|
foreach (var filter in requestFilters)
|
||||||
|
{
|
||||||
|
RequestFilters.Add(filter.Filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
GlobalResponseFilters.Add(new ResponseFilter(_logger).FilterResponse);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override RouteAttribute[] GetRouteAttributes(Type requestType)
|
public override RouteAttribute[] GetRouteAttributes(Type requestType)
|
||||||
{
|
{
|
||||||
var routes = base.GetRouteAttributes(requestType).ToList();
|
var routes = requestType.AllAttributes<RouteAttribute>();
|
||||||
var clone = routes.ToList();
|
var clone = routes.ToList();
|
||||||
|
|
||||||
foreach (var route in clone)
|
foreach (var route in clone)
|
||||||
|
@ -628,33 +703,6 @@ namespace Emby.Server.Implementations.HttpServer
|
||||||
return routes.ToArray();
|
return routes.ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override object GetTaskResult(Task task, string requestName)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var taskObject = task as Task<object>;
|
|
||||||
if (taskObject != null)
|
|
||||||
{
|
|
||||||
return taskObject.Result;
|
|
||||||
}
|
|
||||||
|
|
||||||
task.Wait();
|
|
||||||
|
|
||||||
var type = task.GetType().GetTypeInfo();
|
|
||||||
if (!type.IsGenericType)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
Logger.Warn("Getting task result from " + requestName + " using reflection. For better performance have your api return Task<object>");
|
|
||||||
return type.GetDeclaredProperty("Result").GetValue(task);
|
|
||||||
}
|
|
||||||
catch (TypeAccessException)
|
|
||||||
{
|
|
||||||
return null; //return null for void Task's
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override Func<string, object> GetParseFn(Type propertyType)
|
public override Func<string, object> GetParseFn(Type propertyType)
|
||||||
{
|
{
|
||||||
return _funcParseFn(propertyType);
|
return _funcParseFn(propertyType);
|
||||||
|
@ -740,7 +788,7 @@ namespace Emby.Server.Implementations.HttpServer
|
||||||
public void StartServer(IEnumerable<string> urlPrefixes)
|
public void StartServer(IEnumerable<string> urlPrefixes)
|
||||||
{
|
{
|
||||||
UrlPrefixes = urlPrefixes.ToList();
|
UrlPrefixes = urlPrefixes.ToList();
|
||||||
Start(UrlPrefixes.First());
|
StartListener();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -13,10 +13,10 @@ using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Xml;
|
using System.Xml;
|
||||||
using Emby.Server.Implementations.HttpServer;
|
using Emby.Server.Implementations.HttpServer;
|
||||||
|
using Emby.Server.Implementations.Services;
|
||||||
using MediaBrowser.Model.IO;
|
using MediaBrowser.Model.IO;
|
||||||
using MediaBrowser.Model.Services;
|
using MediaBrowser.Model.Services;
|
||||||
using ServiceStack;
|
using ServiceStack;
|
||||||
using ServiceStack.Host;
|
|
||||||
using IRequest = MediaBrowser.Model.Services.IRequest;
|
using IRequest = MediaBrowser.Model.Services.IRequest;
|
||||||
using MimeTypes = MediaBrowser.Model.Net.MimeTypes;
|
using MimeTypes = MediaBrowser.Model.Net.MimeTypes;
|
||||||
using StreamWriter = Emby.Server.Implementations.HttpServer.StreamWriter;
|
using StreamWriter = Emby.Server.Implementations.HttpServer.StreamWriter;
|
||||||
|
@ -203,7 +203,11 @@ namespace Emby.Server.Implementations.HttpServer
|
||||||
// Do not use the memoryStreamFactory here, they don't place nice with compression
|
// Do not use the memoryStreamFactory here, they don't place nice with compression
|
||||||
using (var ms = new MemoryStream())
|
using (var ms = new MemoryStream())
|
||||||
{
|
{
|
||||||
ContentTypes.Instance.SerializeToStream(request, dto, ms);
|
var contentType = request.ResponseContentType;
|
||||||
|
var writerFn = RequestHelper.GetResponseWriter(contentType);
|
||||||
|
|
||||||
|
writerFn(dto, ms);
|
||||||
|
|
||||||
ms.Position = 0;
|
ms.Position = 0;
|
||||||
|
|
||||||
var responseHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
var responseHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
|
@ -1,14 +1,12 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Text;
|
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using MediaBrowser.Model.Services;
|
using MediaBrowser.Model.Services;
|
||||||
using ServiceStack.Host;
|
using ServiceStack;
|
||||||
|
|
||||||
namespace ServiceStack
|
namespace Emby.Server.Implementations.Services
|
||||||
{
|
{
|
||||||
public class HttpResult
|
public class HttpResult
|
||||||
: IHttpResult, IAsyncStreamWriter
|
: IHttpResult, IAsyncStreamWriter
|
||||||
|
@ -55,7 +53,7 @@ namespace ServiceStack
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await HttpResponseExtensionsInternal.WriteObject(this.RequestContext, this.Response, response).ConfigureAwait(false);
|
await ResponseHelper.WriteObject(this.RequestContext, this.Response, response).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,43 +1,14 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Text;
|
using ServiceStack;
|
||||||
using MediaBrowser.Model.Services;
|
|
||||||
|
|
||||||
namespace ServiceStack.Host
|
namespace Emby.Server.Implementations.Services
|
||||||
{
|
{
|
||||||
public class ContentTypes
|
public class RequestHelper
|
||||||
{
|
{
|
||||||
public static ContentTypes Instance = new ContentTypes();
|
public static Func<Type, Stream, object> GetRequestReader(string contentType)
|
||||||
|
|
||||||
public void SerializeToStream(IRequest req, object response, Stream responseStream)
|
|
||||||
{
|
{
|
||||||
var contentType = req.ResponseContentType;
|
switch (GetContentTypeWithoutEncoding(contentType))
|
||||||
var serializer = GetStreamSerializer(contentType);
|
|
||||||
|
|
||||||
serializer(response, responseStream);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Action<object, Stream> GetStreamSerializer(string contentType)
|
|
||||||
{
|
|
||||||
switch (GetRealContentType(contentType))
|
|
||||||
{
|
|
||||||
case "application/xml":
|
|
||||||
case "text/xml":
|
|
||||||
case "text/xml; charset=utf-8": //"text/xml; charset=utf-8" also matches xml
|
|
||||||
return (o, s) => ServiceStackHost.Instance.SerializeToXml(o, s);
|
|
||||||
|
|
||||||
case "application/json":
|
|
||||||
case "text/json":
|
|
||||||
return (o, s) => ServiceStackHost.Instance.SerializeToJson(o, s);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Func<Type, Stream, object> GetStreamDeserializer(string contentType)
|
|
||||||
{
|
|
||||||
switch (GetRealContentType(contentType))
|
|
||||||
{
|
{
|
||||||
case "application/xml":
|
case "application/xml":
|
||||||
case "text/xml":
|
case "text/xml":
|
||||||
|
@ -52,7 +23,24 @@ namespace ServiceStack.Host
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string GetRealContentType(string contentType)
|
public static Action<object, Stream> GetResponseWriter(string contentType)
|
||||||
|
{
|
||||||
|
switch (GetContentTypeWithoutEncoding(contentType))
|
||||||
|
{
|
||||||
|
case "application/xml":
|
||||||
|
case "text/xml":
|
||||||
|
case "text/xml; charset=utf-8": //"text/xml; charset=utf-8" also matches xml
|
||||||
|
return (o, s) => ServiceStackHost.Instance.SerializeToXml(o, s);
|
||||||
|
|
||||||
|
case "application/json":
|
||||||
|
case "text/json":
|
||||||
|
return (o, s) => ServiceStackHost.Instance.SerializeToJson(o, s);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetContentTypeWithoutEncoding(string contentType)
|
||||||
{
|
{
|
||||||
return contentType == null
|
return contentType == null
|
||||||
? null
|
? null
|
|
@ -1,21 +1,17 @@
|
||||||
//Copyright (c) Service Stack LLC. All Rights Reserved.
|
|
||||||
//License: https://raw.github.com/ServiceStack/ServiceStack/master/license.txt
|
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Threading.Tasks;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using MediaBrowser.Model.Services;
|
using MediaBrowser.Model.Services;
|
||||||
using ServiceStack.Host;
|
|
||||||
|
|
||||||
namespace ServiceStack
|
namespace Emby.Server.Implementations.Services
|
||||||
{
|
{
|
||||||
public static class HttpResponseExtensionsInternal
|
public static class ResponseHelper
|
||||||
{
|
{
|
||||||
public static async Task<bool> WriteToOutputStream(IResponse response, object result)
|
private static async Task<bool> WriteToOutputStream(IResponse response, object result)
|
||||||
{
|
{
|
||||||
var asyncStreamWriter = result as IAsyncStreamWriter;
|
var asyncStreamWriter = result as IAsyncStreamWriter;
|
||||||
if (asyncStreamWriter != null)
|
if (asyncStreamWriter != null)
|
||||||
|
@ -54,24 +50,16 @@ namespace ServiceStack
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
public static Task WriteToResponse(IResponse httpRes, IRequest httpReq, object result)
|
||||||
/// End a ServiceStack Request with no content
|
|
||||||
/// </summary>
|
|
||||||
public static void EndRequestWithNoContent(this IResponse httpRes)
|
|
||||||
{
|
|
||||||
if (httpRes.StatusCode == (int)HttpStatusCode.OK)
|
|
||||||
{
|
|
||||||
httpRes.StatusCode = (int)HttpStatusCode.NoContent;
|
|
||||||
}
|
|
||||||
|
|
||||||
httpRes.SetContentLength(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Task WriteToResponse(this IResponse httpRes, IRequest httpReq, object result)
|
|
||||||
{
|
{
|
||||||
if (result == null)
|
if (result == null)
|
||||||
{
|
{
|
||||||
httpRes.EndRequestWithNoContent();
|
if (httpRes.StatusCode == (int)HttpStatusCode.OK)
|
||||||
|
{
|
||||||
|
httpRes.StatusCode = (int)HttpStatusCode.NoContent;
|
||||||
|
}
|
||||||
|
|
||||||
|
httpRes.SetContentLength(0);
|
||||||
return Task.FromResult(true);
|
return Task.FromResult(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -80,10 +68,10 @@ namespace ServiceStack
|
||||||
{
|
{
|
||||||
httpResult.RequestContext = httpReq;
|
httpResult.RequestContext = httpReq;
|
||||||
httpReq.ResponseContentType = httpResult.ContentType ?? httpReq.ResponseContentType;
|
httpReq.ResponseContentType = httpResult.ContentType ?? httpReq.ResponseContentType;
|
||||||
return httpRes.WriteToResponseInternal(httpResult, httpReq);
|
return WriteToResponseInternal(httpRes, httpResult, httpReq);
|
||||||
}
|
}
|
||||||
|
|
||||||
return httpRes.WriteToResponseInternal(result, httpReq);
|
return WriteToResponseInternal(httpRes, result, httpReq);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -94,7 +82,7 @@ namespace ServiceStack
|
||||||
/// <param name="result">Whether or not it was implicity handled by ServiceStack's built-in handlers.</param>
|
/// <param name="result">Whether or not it was implicity handled by ServiceStack's built-in handlers.</param>
|
||||||
/// <param name="request">The serialization context.</param>
|
/// <param name="request">The serialization context.</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
private static async Task WriteToResponseInternal(this IResponse response, object result, IRequest request)
|
private static async Task WriteToResponseInternal(IResponse response, object result, IRequest request)
|
||||||
{
|
{
|
||||||
var defaultContentType = request.ResponseContentType;
|
var defaultContentType = request.ResponseContentType;
|
||||||
|
|
||||||
|
@ -173,7 +161,7 @@ namespace ServiceStack
|
||||||
public static async Task WriteObject(IRequest request, object result, IResponse response)
|
public static async Task WriteObject(IRequest request, object result, IResponse response)
|
||||||
{
|
{
|
||||||
var contentType = request.ResponseContentType;
|
var contentType = request.ResponseContentType;
|
||||||
var serializer = ContentTypes.GetStreamSerializer(contentType);
|
var serializer = RequestHelper.GetResponseWriter(contentType);
|
||||||
|
|
||||||
using (var ms = new MemoryStream())
|
using (var ms = new MemoryStream())
|
||||||
{
|
{
|
|
@ -1,14 +1,13 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Emby.Server.Implementations.HttpServer;
|
||||||
using MediaBrowser.Model.Logging;
|
using MediaBrowser.Model.Logging;
|
||||||
using MediaBrowser.Model.Services;
|
using MediaBrowser.Model.Services;
|
||||||
|
using ServiceStack;
|
||||||
|
|
||||||
namespace ServiceStack.Host
|
namespace Emby.Server.Implementations.Services
|
||||||
{
|
{
|
||||||
public delegate Task<object> InstanceExecFn(IRequest requestContext, object intance, object request);
|
public delegate Task<object> InstanceExecFn(IRequest requestContext, object intance, object request);
|
||||||
public delegate object ActionInvokerFn(object intance, object request);
|
public delegate object ActionInvokerFn(object intance, object request);
|
||||||
|
@ -16,21 +15,20 @@ namespace ServiceStack.Host
|
||||||
|
|
||||||
public class ServiceController
|
public class ServiceController
|
||||||
{
|
{
|
||||||
|
public static ServiceController Instance;
|
||||||
private readonly Func<IEnumerable<Type>> _resolveServicesFn;
|
private readonly Func<IEnumerable<Type>> _resolveServicesFn;
|
||||||
|
|
||||||
public ServiceController(Func<IEnumerable<Type>> resolveServicesFn)
|
public ServiceController(Func<IEnumerable<Type>> resolveServicesFn)
|
||||||
{
|
{
|
||||||
|
Instance = this;
|
||||||
_resolveServicesFn = resolveServicesFn;
|
_resolveServicesFn = resolveServicesFn;
|
||||||
this.RequestTypeFactoryMap = new Dictionary<Type, Func<IRequest, object>>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Dictionary<Type, Func<IRequest, object>> RequestTypeFactoryMap { get; set; }
|
public void Init(HttpListenerHost appHost)
|
||||||
|
|
||||||
public void Init()
|
|
||||||
{
|
{
|
||||||
foreach (var serviceType in _resolveServicesFn())
|
foreach (var serviceType in _resolveServicesFn())
|
||||||
{
|
{
|
||||||
RegisterService(serviceType);
|
RegisterService(appHost, serviceType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,15 +39,12 @@ namespace ServiceStack.Host
|
||||||
: type.GetTypeInfo().GenericTypeArguments;
|
: type.GetTypeInfo().GenericTypeArguments;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RegisterService(Type serviceType)
|
public void RegisterService(HttpListenerHost appHost, Type serviceType)
|
||||||
{
|
{
|
||||||
var processedReqs = new HashSet<Type>();
|
var processedReqs = new HashSet<Type>();
|
||||||
|
|
||||||
var actions = ServiceExecGeneral.Reset(serviceType);
|
var actions = ServiceExecGeneral.Reset(serviceType);
|
||||||
|
|
||||||
var requiresRequestStreamTypeInfo = typeof(IRequiresRequestStream).GetTypeInfo();
|
|
||||||
|
|
||||||
var appHost = ServiceStackHost.Instance;
|
|
||||||
foreach (var mi in serviceType.GetActions())
|
foreach (var mi in serviceType.GetActions())
|
||||||
{
|
{
|
||||||
var requestType = mi.GetParameters()[0].ParameterType;
|
var requestType = mi.GetParameters()[0].ParameterType;
|
||||||
|
@ -67,20 +62,7 @@ namespace ServiceStack.Host
|
||||||
|
|
||||||
RegisterRestPaths(requestType);
|
RegisterRestPaths(requestType);
|
||||||
|
|
||||||
appHost.Metadata.Add(serviceType, requestType, responseType);
|
appHost.AddServiceInfo(serviceType, requestType, responseType);
|
||||||
|
|
||||||
if (requiresRequestStreamTypeInfo.IsAssignableFrom(requestType.GetTypeInfo()))
|
|
||||||
{
|
|
||||||
this.RequestTypeFactoryMap[requestType] = req =>
|
|
||||||
{
|
|
||||||
var restPath = req.GetRoute();
|
|
||||||
var request = RestHandler.CreateRequest(req, restPath, req.GetRequestParams(), ServiceStackHost.Instance.CreateInstance(requestType));
|
|
||||||
|
|
||||||
var rawReq = (IRequiresRequestStream)request;
|
|
||||||
rawReq.RequestStream = req.InputStream;
|
|
||||||
return rawReq;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -121,21 +103,6 @@ namespace ServiceStack.Host
|
||||||
pathsAtFirstMatch.Add(restPath);
|
pathsAtFirstMatch.Add(restPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AfterInit()
|
|
||||||
{
|
|
||||||
var appHost = ServiceStackHost.Instance;
|
|
||||||
|
|
||||||
//Register any routes configured on Metadata.Routes
|
|
||||||
foreach (var restPath in appHost.RestPaths)
|
|
||||||
{
|
|
||||||
RegisterRestPath(restPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
//Sync the RestPaths collections
|
|
||||||
appHost.RestPaths.Clear();
|
|
||||||
appHost.RestPaths.AddRange(RestPathMap.Values.SelectMany(x => x));
|
|
||||||
}
|
|
||||||
|
|
||||||
public RestPath GetRestPathForRequest(string httpMethod, string pathInfo, ILogger logger)
|
public RestPath GetRestPathForRequest(string httpMethod, string pathInfo, ILogger logger)
|
||||||
{
|
{
|
||||||
var matchUsingPathParts = RestPath.GetPathPartsForMatching(pathInfo);
|
var matchUsingPathParts = RestPath.GetPathPartsForMatching(pathInfo);
|
||||||
|
@ -191,15 +158,15 @@ namespace ServiceStack.Host
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<object> Execute(object requestDto, IRequest req)
|
public async Task<object> Execute(HttpListenerHost appHost, object requestDto, IRequest req)
|
||||||
{
|
{
|
||||||
req.Dto = requestDto;
|
req.Dto = requestDto;
|
||||||
var requestType = requestDto.GetType();
|
var requestType = requestDto.GetType();
|
||||||
req.OperationName = requestType.Name;
|
req.OperationName = requestType.Name;
|
||||||
|
|
||||||
var serviceType = ServiceStackHost.Instance.Metadata.GetServiceTypeByRequest(requestType);
|
var serviceType = appHost.GetServiceTypeByRequest(requestType);
|
||||||
|
|
||||||
var service = ServiceStackHost.Instance.CreateInstance(serviceType);
|
var service = appHost.CreateInstance(serviceType);
|
||||||
|
|
||||||
//var service = typeFactory.CreateInstance(serviceType);
|
//var service = typeFactory.CreateInstance(serviceType);
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
//Copyright (c) Service Stack LLC. All Rights Reserved.
|
|
||||||
//License: https://raw.github.com/ServiceStack/ServiceStack/master/license.txt
|
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
@ -8,11 +5,25 @@ using System.Linq.Expressions;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using MediaBrowser.Model.Services;
|
using MediaBrowser.Model.Services;
|
||||||
|
using ServiceStack;
|
||||||
|
|
||||||
namespace ServiceStack.Host
|
namespace Emby.Server.Implementations.Services
|
||||||
{
|
{
|
||||||
public static class ServiceExecExtensions
|
public static class ServiceExecExtensions
|
||||||
{
|
{
|
||||||
|
public static HashSet<string> AllVerbs = new HashSet<string>(new[] {
|
||||||
|
"OPTIONS", "GET", "HEAD", "POST", "PUT", "DELETE", "TRACE", "CONNECT", // RFC 2616
|
||||||
|
"PROPFIND", "PROPPATCH", "MKCOL", "COPY", "MOVE", "LOCK", "UNLOCK", // RFC 2518
|
||||||
|
"VERSION-CONTROL", "REPORT", "CHECKOUT", "CHECKIN", "UNCHECKOUT",
|
||||||
|
"MKWORKSPACE", "UPDATE", "LABEL", "MERGE", "BASELINE-CONTROL", "MKACTIVITY", // RFC 3253
|
||||||
|
"ORDERPATCH", // RFC 3648
|
||||||
|
"ACL", // RFC 3744
|
||||||
|
"PATCH", // https://datatracker.ietf.org/doc/draft-dusseault-http-patch/
|
||||||
|
"SEARCH", // https://datatracker.ietf.org/doc/draft-reschke-webdav-search/
|
||||||
|
"BCOPY", "BDELETE", "BMOVE", "BPROPFIND", "BPROPPATCH", "NOTIFY",
|
||||||
|
"POLL", "SUBSCRIBE", "UNSUBSCRIBE"
|
||||||
|
});
|
||||||
|
|
||||||
public static IEnumerable<MethodInfo> GetActions(this Type serviceType)
|
public static IEnumerable<MethodInfo> GetActions(this Type serviceType)
|
||||||
{
|
{
|
||||||
foreach (var mi in serviceType.GetRuntimeMethods().Where(i => i.IsPublic && !i.IsStatic))
|
foreach (var mi in serviceType.GetRuntimeMethods().Where(i => i.IsPublic && !i.IsStatic))
|
||||||
|
@ -20,8 +31,8 @@ namespace ServiceStack.Host
|
||||||
if (mi.GetParameters().Length != 1)
|
if (mi.GetParameters().Length != 1)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
var actionName = mi.Name.ToUpper();
|
var actionName = mi.Name;
|
||||||
if (!HttpMethods.AllVerbs.Contains(actionName) && actionName != ActionContext.AnyAction)
|
if (!AllVerbs.Contains(actionName, StringComparer.OrdinalIgnoreCase) && !string.Equals(actionName, ServiceMethod.AnyAction, StringComparison.OrdinalIgnoreCase))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
yield return mi;
|
yield return mi;
|
||||||
|
@ -31,9 +42,9 @@ namespace ServiceStack.Host
|
||||||
|
|
||||||
internal static class ServiceExecGeneral
|
internal static class ServiceExecGeneral
|
||||||
{
|
{
|
||||||
public static Dictionary<string, ActionContext> execMap = new Dictionary<string, ActionContext>();
|
public static Dictionary<string, ServiceMethod> execMap = new Dictionary<string, ServiceMethod>();
|
||||||
|
|
||||||
public static void CreateServiceRunnersFor(Type requestType, List<ActionContext> actions)
|
public static void CreateServiceRunnersFor(Type requestType, List<ServiceMethod> actions)
|
||||||
{
|
{
|
||||||
foreach (var actionCtx in actions)
|
foreach (var actionCtx in actions)
|
||||||
{
|
{
|
||||||
|
@ -45,12 +56,11 @@ namespace ServiceStack.Host
|
||||||
|
|
||||||
public static async Task<object> Execute(Type serviceType, IRequest request, object instance, object requestDto, string requestName)
|
public static async Task<object> Execute(Type serviceType, IRequest request, object instance, object requestDto, string requestName)
|
||||||
{
|
{
|
||||||
var actionName = request.Verb
|
var actionName = request.Verb ?? "POST";
|
||||||
?? HttpMethods.Post; //MQ Services
|
|
||||||
|
|
||||||
ActionContext actionContext;
|
ServiceMethod actionContext;
|
||||||
if (ServiceExecGeneral.execMap.TryGetValue(ActionContext.Key(serviceType, actionName, requestName), out actionContext)
|
if (ServiceExecGeneral.execMap.TryGetValue(ServiceMethod.Key(serviceType, actionName, requestName), out actionContext)
|
||||||
|| ServiceExecGeneral.execMap.TryGetValue(ActionContext.AnyKey(serviceType, requestName), out actionContext))
|
|| ServiceExecGeneral.execMap.TryGetValue(ServiceMethod.AnyKey(serviceType, requestName), out actionContext))
|
||||||
{
|
{
|
||||||
if (actionContext.RequestFilters != null)
|
if (actionContext.RequestFilters != null)
|
||||||
{
|
{
|
||||||
|
@ -67,7 +77,7 @@ namespace ServiceStack.Host
|
||||||
if (taskResponse != null)
|
if (taskResponse != null)
|
||||||
{
|
{
|
||||||
await taskResponse.ConfigureAwait(false);
|
await taskResponse.ConfigureAwait(false);
|
||||||
response = ServiceStackHost.Instance.GetTaskResult(taskResponse, requestName);
|
response = ServiceHandler.GetTaskResult(taskResponse);
|
||||||
}
|
}
|
||||||
|
|
||||||
return response;
|
return response;
|
||||||
|
@ -77,19 +87,19 @@ namespace ServiceStack.Host
|
||||||
throw new NotImplementedException(string.Format("Could not find method named {1}({0}) or Any({0}) on Service {2}", requestDto.GetType().GetOperationName(), expectedMethodName, serviceType.GetOperationName()));
|
throw new NotImplementedException(string.Format("Could not find method named {1}({0}) or Any({0}) on Service {2}", requestDto.GetType().GetOperationName(), expectedMethodName, serviceType.GetOperationName()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<ActionContext> Reset(Type serviceType)
|
public static List<ServiceMethod> Reset(Type serviceType)
|
||||||
{
|
{
|
||||||
var actions = new List<ActionContext>();
|
var actions = new List<ServiceMethod>();
|
||||||
|
|
||||||
foreach (var mi in serviceType.GetActions())
|
foreach (var mi in serviceType.GetActions())
|
||||||
{
|
{
|
||||||
var actionName = mi.Name.ToUpper();
|
var actionName = mi.Name;
|
||||||
var args = mi.GetParameters();
|
var args = mi.GetParameters();
|
||||||
|
|
||||||
var requestType = args[0].ParameterType;
|
var requestType = args[0].ParameterType;
|
||||||
var actionCtx = new ActionContext
|
var actionCtx = new ServiceMethod
|
||||||
{
|
{
|
||||||
Id = ActionContext.Key(serviceType, actionName, requestType.GetOperationName())
|
Id = ServiceMethod.Key(serviceType, actionName, requestType.GetOperationName())
|
||||||
};
|
};
|
||||||
|
|
||||||
try
|
try
|
297
Emby.Server.Implementations/Services/ServiceHandler.cs
Normal file
297
Emby.Server.Implementations/Services/ServiceHandler.cs
Normal file
|
@ -0,0 +1,297 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Emby.Server.Implementations.HttpServer;
|
||||||
|
using MediaBrowser.Model.Logging;
|
||||||
|
using MediaBrowser.Model.Services;
|
||||||
|
using ServiceStack;
|
||||||
|
|
||||||
|
namespace Emby.Server.Implementations.Services
|
||||||
|
{
|
||||||
|
public class ServiceHandler
|
||||||
|
{
|
||||||
|
public async Task<object> HandleResponseAsync(object response)
|
||||||
|
{
|
||||||
|
var taskResponse = response as Task;
|
||||||
|
|
||||||
|
if (taskResponse == null)
|
||||||
|
{
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
await taskResponse.ConfigureAwait(false);
|
||||||
|
|
||||||
|
var taskResult = GetTaskResult(taskResponse);
|
||||||
|
|
||||||
|
var subTask = taskResult as Task;
|
||||||
|
if (subTask != null)
|
||||||
|
{
|
||||||
|
taskResult = GetTaskResult(subTask);
|
||||||
|
}
|
||||||
|
|
||||||
|
return taskResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static object GetTaskResult(Task task)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var taskObject = task as Task<object>;
|
||||||
|
if (taskObject != null)
|
||||||
|
{
|
||||||
|
return taskObject.Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
task.Wait();
|
||||||
|
|
||||||
|
var type = task.GetType().GetTypeInfo();
|
||||||
|
if (!type.IsGenericType)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return type.GetDeclaredProperty("Result").GetValue(task);
|
||||||
|
}
|
||||||
|
catch (TypeAccessException)
|
||||||
|
{
|
||||||
|
return null; //return null for void Task's
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static object CreateContentTypeRequest(IRequest httpReq, Type requestType, string contentType)
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(contentType) && httpReq.ContentLength > 0)
|
||||||
|
{
|
||||||
|
var deserializer = RequestHelper.GetRequestReader(contentType);
|
||||||
|
if (deserializer != null)
|
||||||
|
{
|
||||||
|
return deserializer(requestType, httpReq.InputStream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ServiceStackHost.Instance.CreateInstance(requestType); //Return an empty DTO, even for empty request bodies
|
||||||
|
}
|
||||||
|
|
||||||
|
public static RestPath FindMatchingRestPath(string httpMethod, string pathInfo, ILogger logger, out string contentType)
|
||||||
|
{
|
||||||
|
pathInfo = GetSanitizedPathInfo(pathInfo, out contentType);
|
||||||
|
|
||||||
|
return ServiceController.Instance.GetRestPathForRequest(httpMethod, pathInfo, logger);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string GetSanitizedPathInfo(string pathInfo, out string contentType)
|
||||||
|
{
|
||||||
|
contentType = null;
|
||||||
|
var pos = pathInfo.LastIndexOf('.');
|
||||||
|
if (pos >= 0)
|
||||||
|
{
|
||||||
|
var format = pathInfo.Substring(pos + 1);
|
||||||
|
contentType = GetFormatContentType(format);
|
||||||
|
if (contentType != null)
|
||||||
|
{
|
||||||
|
pathInfo = pathInfo.Substring(0, pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return pathInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetFormatContentType(string format)
|
||||||
|
{
|
||||||
|
//built-in formats
|
||||||
|
if (format == "json")
|
||||||
|
return "application/json";
|
||||||
|
if (format == "xml")
|
||||||
|
return "application/xml";
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RestPath GetRestPath(string httpMethod, string pathInfo)
|
||||||
|
{
|
||||||
|
if (this.RestPath == null)
|
||||||
|
{
|
||||||
|
string contentType;
|
||||||
|
this.RestPath = FindMatchingRestPath(httpMethod, pathInfo, new NullLogger(), out contentType);
|
||||||
|
|
||||||
|
if (contentType != null)
|
||||||
|
ResponseContentType = contentType;
|
||||||
|
}
|
||||||
|
return this.RestPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RestPath RestPath { get; set; }
|
||||||
|
|
||||||
|
// Set from SSHHF.GetHandlerForPathInfo()
|
||||||
|
public string ResponseContentType { get; set; }
|
||||||
|
|
||||||
|
public async Task ProcessRequestAsync(HttpListenerHost appHost, IRequest httpReq, IResponse httpRes, ILogger logger, string operationName)
|
||||||
|
{
|
||||||
|
var restPath = GetRestPath(httpReq.Verb, httpReq.PathInfo);
|
||||||
|
if (restPath == null)
|
||||||
|
{
|
||||||
|
throw new NotSupportedException("No RestPath found for: " + httpReq.Verb + " " + httpReq.PathInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
SetRoute(httpReq, restPath);
|
||||||
|
|
||||||
|
if (ResponseContentType != null)
|
||||||
|
httpReq.ResponseContentType = ResponseContentType;
|
||||||
|
|
||||||
|
var request = httpReq.Dto = CreateRequest(httpReq, restPath, logger);
|
||||||
|
|
||||||
|
appHost.ApplyRequestFilters(httpReq, httpRes, request);
|
||||||
|
|
||||||
|
var rawResponse = await appHost.ServiceController.Execute(appHost, request, httpReq).ConfigureAwait(false);
|
||||||
|
|
||||||
|
var response = await HandleResponseAsync(rawResponse).ConfigureAwait(false);
|
||||||
|
|
||||||
|
// Apply response filters
|
||||||
|
foreach (var responseFilter in appHost.GlobalResponseFilters)
|
||||||
|
{
|
||||||
|
responseFilter(httpReq, httpRes, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
await ResponseHelper.WriteToResponse(httpRes, httpReq, response).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static object CreateRequest(IRequest httpReq, RestPath restPath, ILogger logger)
|
||||||
|
{
|
||||||
|
var requestType = restPath.RequestType;
|
||||||
|
|
||||||
|
if (RequireqRequestStream(requestType))
|
||||||
|
{
|
||||||
|
// Used by IRequiresRequestStream
|
||||||
|
return CreateRequiresRequestStreamRequest(httpReq, requestType);
|
||||||
|
}
|
||||||
|
|
||||||
|
var requestParams = GetFlattenedRequestParams(httpReq);
|
||||||
|
return CreateRequest(httpReq, restPath, requestParams);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool RequireqRequestStream(Type requestType)
|
||||||
|
{
|
||||||
|
var requiresRequestStreamTypeInfo = typeof(IRequiresRequestStream).GetTypeInfo();
|
||||||
|
|
||||||
|
return requiresRequestStreamTypeInfo.IsAssignableFrom(requestType.GetTypeInfo());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IRequiresRequestStream CreateRequiresRequestStreamRequest(IRequest req, Type requestType)
|
||||||
|
{
|
||||||
|
var restPath = GetRoute(req);
|
||||||
|
var request = ServiceHandler.CreateRequest(req, restPath, GetRequestParams(req), ServiceStackHost.Instance.CreateInstance(requestType));
|
||||||
|
|
||||||
|
var rawReq = (IRequiresRequestStream)request;
|
||||||
|
rawReq.RequestStream = req.InputStream;
|
||||||
|
return rawReq;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static object CreateRequest(IRequest httpReq, RestPath restPath, Dictionary<string, string> requestParams)
|
||||||
|
{
|
||||||
|
var requestDto = CreateContentTypeRequest(httpReq, restPath.RequestType, httpReq.ContentType);
|
||||||
|
|
||||||
|
return CreateRequest(httpReq, restPath, requestParams, requestDto);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static object CreateRequest(IRequest httpReq, RestPath restPath, Dictionary<string, string> requestParams, object requestDto)
|
||||||
|
{
|
||||||
|
string contentType;
|
||||||
|
var pathInfo = !restPath.IsWildCardPath
|
||||||
|
? GetSanitizedPathInfo(httpReq.PathInfo, out contentType)
|
||||||
|
: httpReq.PathInfo;
|
||||||
|
|
||||||
|
return restPath.CreateRequest(pathInfo, requestParams, requestDto);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Duplicate Params are given a unique key by appending a #1 suffix
|
||||||
|
/// </summary>
|
||||||
|
private static Dictionary<string, string> GetRequestParams(IRequest request)
|
||||||
|
{
|
||||||
|
var map = new Dictionary<string, string>();
|
||||||
|
|
||||||
|
foreach (var name in request.QueryString.Keys)
|
||||||
|
{
|
||||||
|
if (name == null) continue; //thank you ASP.NET
|
||||||
|
|
||||||
|
var values = request.QueryString.GetValues(name);
|
||||||
|
if (values.Length == 1)
|
||||||
|
{
|
||||||
|
map[name] = values[0];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (var i = 0; i < values.Length; i++)
|
||||||
|
{
|
||||||
|
map[name + (i == 0 ? "" : "#" + i)] = values[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((IsMethod(request.Verb, "POST") || IsMethod(request.Verb, "PUT")) && request.FormData != null)
|
||||||
|
{
|
||||||
|
foreach (var name in request.FormData.Keys)
|
||||||
|
{
|
||||||
|
if (name == null) continue; //thank you ASP.NET
|
||||||
|
|
||||||
|
var values = request.FormData.GetValues(name);
|
||||||
|
if (values.Length == 1)
|
||||||
|
{
|
||||||
|
map[name] = values[0];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (var i = 0; i < values.Length; i++)
|
||||||
|
{
|
||||||
|
map[name + (i == 0 ? "" : "#" + i)] = values[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsMethod(string method, string expected)
|
||||||
|
{
|
||||||
|
return string.Equals(method, expected, StringComparison.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Duplicate params have their values joined together in a comma-delimited string
|
||||||
|
/// </summary>
|
||||||
|
private static Dictionary<string, string> GetFlattenedRequestParams(IRequest request)
|
||||||
|
{
|
||||||
|
var map = new Dictionary<string, string>();
|
||||||
|
|
||||||
|
foreach (var name in request.QueryString.Keys)
|
||||||
|
{
|
||||||
|
if (name == null) continue; //thank you ASP.NET
|
||||||
|
map[name] = request.QueryString[name];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((IsMethod(request.Verb, "POST") || IsMethod(request.Verb, "PUT")) && request.FormData != null)
|
||||||
|
{
|
||||||
|
foreach (var name in request.FormData.Keys)
|
||||||
|
{
|
||||||
|
if (name == null) continue; //thank you ASP.NET
|
||||||
|
map[name] = request.FormData[name];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void SetRoute(IRequest req, RestPath route)
|
||||||
|
{
|
||||||
|
req.Items["__route"] = route;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static RestPath GetRoute(IRequest req)
|
||||||
|
{
|
||||||
|
object route;
|
||||||
|
req.Items.TryGetValue("__route", out route);
|
||||||
|
return route as RestPath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,11 +1,8 @@
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace ServiceStack.Host
|
namespace Emby.Server.Implementations.Services
|
||||||
{
|
{
|
||||||
/// <summary>
|
public class ServiceMethod
|
||||||
/// Context to capture IService action
|
|
||||||
/// </summary>
|
|
||||||
public class ActionContext
|
|
||||||
{
|
{
|
||||||
public const string AnyAction = "ANY";
|
public const string AnyAction = "ANY";
|
||||||
|
|
|
@ -1,27 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Reflection;
|
|
||||||
using System.Threading;
|
|
||||||
using ServiceStack;
|
|
||||||
|
|
||||||
namespace ServiceStack.Support.WebHost
|
|
||||||
{
|
|
||||||
public static class FilterAttributeCache
|
|
||||||
{
|
|
||||||
public static MediaBrowser.Model.Services.IHasRequestFilter[] GetRequestFilterAttributes(Type requestDtoType)
|
|
||||||
{
|
|
||||||
var attributes = requestDtoType.AllAttributes().OfType<MediaBrowser.Model.Services.IHasRequestFilter>().ToList();
|
|
||||||
|
|
||||||
var serviceType = ServiceStackHost.Instance.Metadata.GetServiceTypeByRequest(requestDtoType);
|
|
||||||
if (serviceType != null)
|
|
||||||
{
|
|
||||||
attributes.AddRange(serviceType.AllAttributes().OfType<MediaBrowser.Model.Services.IHasRequestFilter>());
|
|
||||||
}
|
|
||||||
|
|
||||||
attributes.Sort((x,y) => x.Priority - y.Priority);
|
|
||||||
|
|
||||||
return attributes.ToArray();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,177 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Runtime.Serialization;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using MediaBrowser.Model.Logging;
|
|
||||||
using MediaBrowser.Model.Services;
|
|
||||||
|
|
||||||
namespace ServiceStack.Host
|
|
||||||
{
|
|
||||||
public class RestHandler
|
|
||||||
{
|
|
||||||
public string RequestName { get; set; }
|
|
||||||
|
|
||||||
public async Task<object> HandleResponseAsync(object response)
|
|
||||||
{
|
|
||||||
var taskResponse = response as Task;
|
|
||||||
|
|
||||||
if (taskResponse == null)
|
|
||||||
{
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
|
|
||||||
await taskResponse.ConfigureAwait(false);
|
|
||||||
|
|
||||||
var taskResult = ServiceStackHost.Instance.GetTaskResult(taskResponse, RequestName);
|
|
||||||
|
|
||||||
var subTask = taskResult as Task;
|
|
||||||
if (subTask != null)
|
|
||||||
{
|
|
||||||
taskResult = ServiceStackHost.Instance.GetTaskResult(subTask, RequestName);
|
|
||||||
}
|
|
||||||
|
|
||||||
return taskResult;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static object CreateContentTypeRequest(IRequest httpReq, Type requestType, string contentType)
|
|
||||||
{
|
|
||||||
if (!string.IsNullOrEmpty(contentType) && httpReq.ContentLength > 0)
|
|
||||||
{
|
|
||||||
var deserializer = ContentTypes.Instance.GetStreamDeserializer(contentType);
|
|
||||||
if (deserializer != null)
|
|
||||||
{
|
|
||||||
return deserializer(requestType, httpReq.InputStream);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ServiceStackHost.Instance.CreateInstance(requestType); //Return an empty DTO, even for empty request bodies
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static object GetCustomRequestFromBinder(IRequest httpReq, Type requestType)
|
|
||||||
{
|
|
||||||
Func<IRequest, object> requestFactoryFn;
|
|
||||||
ServiceStackHost.Instance.ServiceController.RequestTypeFactoryMap.TryGetValue(
|
|
||||||
requestType, out requestFactoryFn);
|
|
||||||
|
|
||||||
return requestFactoryFn != null ? requestFactoryFn(httpReq) : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static RestPath FindMatchingRestPath(string httpMethod, string pathInfo, ILogger logger, out string contentType)
|
|
||||||
{
|
|
||||||
pathInfo = GetSanitizedPathInfo(pathInfo, out contentType);
|
|
||||||
|
|
||||||
return ServiceStackHost.Instance.ServiceController.GetRestPathForRequest(httpMethod, pathInfo, logger);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string GetSanitizedPathInfo(string pathInfo, out string contentType)
|
|
||||||
{
|
|
||||||
contentType = null;
|
|
||||||
var pos = pathInfo.LastIndexOf('.');
|
|
||||||
if (pos >= 0)
|
|
||||||
{
|
|
||||||
var format = pathInfo.Substring(pos + 1);
|
|
||||||
contentType = GetFormatContentType(format);
|
|
||||||
if (contentType != null)
|
|
||||||
{
|
|
||||||
pathInfo = pathInfo.Substring(0, pos);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return pathInfo;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string GetFormatContentType(string format)
|
|
||||||
{
|
|
||||||
//built-in formats
|
|
||||||
if (format == "json")
|
|
||||||
return "application/json";
|
|
||||||
if (format == "xml")
|
|
||||||
return "application/xml";
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public RestPath GetRestPath(string httpMethod, string pathInfo)
|
|
||||||
{
|
|
||||||
if (this.RestPath == null)
|
|
||||||
{
|
|
||||||
string contentType;
|
|
||||||
this.RestPath = FindMatchingRestPath(httpMethod, pathInfo, new NullLogger(), out contentType);
|
|
||||||
|
|
||||||
if (contentType != null)
|
|
||||||
ResponseContentType = contentType;
|
|
||||||
}
|
|
||||||
return this.RestPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
public RestPath RestPath { get; set; }
|
|
||||||
|
|
||||||
// Set from SSHHF.GetHandlerForPathInfo()
|
|
||||||
public string ResponseContentType { get; set; }
|
|
||||||
|
|
||||||
public async Task ProcessRequestAsync(IRequest httpReq, IResponse httpRes, string operationName)
|
|
||||||
{
|
|
||||||
var appHost = ServiceStackHost.Instance;
|
|
||||||
|
|
||||||
var restPath = GetRestPath(httpReq.Verb, httpReq.PathInfo);
|
|
||||||
if (restPath == null)
|
|
||||||
{
|
|
||||||
throw new NotSupportedException("No RestPath found for: " + httpReq.Verb + " " + httpReq.PathInfo);
|
|
||||||
}
|
|
||||||
httpReq.SetRoute(restPath);
|
|
||||||
|
|
||||||
if (ResponseContentType != null)
|
|
||||||
httpReq.ResponseContentType = ResponseContentType;
|
|
||||||
|
|
||||||
var request = httpReq.Dto = CreateRequest(httpReq, restPath);
|
|
||||||
|
|
||||||
appHost.ApplyRequestFilters(httpReq, httpRes, request);
|
|
||||||
|
|
||||||
var rawResponse = await ServiceStackHost.Instance.ServiceController.Execute(request, httpReq).ConfigureAwait(false);
|
|
||||||
|
|
||||||
var response = await HandleResponseAsync(rawResponse).ConfigureAwait(false);
|
|
||||||
|
|
||||||
appHost.ApplyResponseFilters(httpReq, httpRes, response);
|
|
||||||
|
|
||||||
await httpRes.WriteToResponse(httpReq, response).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static object CreateRequest(IRequest httpReq, RestPath restPath)
|
|
||||||
{
|
|
||||||
var dtoFromBinder = GetCustomRequestFromBinder(httpReq, restPath.RequestType);
|
|
||||||
if (dtoFromBinder != null)
|
|
||||||
return dtoFromBinder;
|
|
||||||
|
|
||||||
var requestParams = httpReq.GetFlattenedRequestParams();
|
|
||||||
return CreateRequest(httpReq, restPath, requestParams);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static object CreateRequest(IRequest httpReq, RestPath restPath, Dictionary<string, string> requestParams)
|
|
||||||
{
|
|
||||||
var requestDto = CreateContentTypeRequest(httpReq, restPath.RequestType, httpReq.ContentType);
|
|
||||||
|
|
||||||
return CreateRequest(httpReq, restPath, requestParams, requestDto);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static object CreateRequest(IRequest httpReq, RestPath restPath, Dictionary<string, string> requestParams, object requestDto)
|
|
||||||
{
|
|
||||||
string contentType;
|
|
||||||
var pathInfo = !restPath.IsWildCardPath
|
|
||||||
? GetSanitizedPathInfo(httpReq.PathInfo, out contentType)
|
|
||||||
: httpReq.PathInfo;
|
|
||||||
|
|
||||||
return restPath.CreateRequest(pathInfo, requestParams, requestDto);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Used in Unit tests
|
|
||||||
/// </summary>
|
|
||||||
/// <returns></returns>
|
|
||||||
public object CreateRequest(IRequest httpReq, string operationName)
|
|
||||||
{
|
|
||||||
if (this.RestPath == null)
|
|
||||||
throw new ArgumentNullException("No RestPath found");
|
|
||||||
|
|
||||||
return CreateRequest(httpReq, this.RestPath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,27 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace ServiceStack.Host
|
|
||||||
{
|
|
||||||
public class ServiceMetadata
|
|
||||||
{
|
|
||||||
public ServiceMetadata()
|
|
||||||
{
|
|
||||||
this.OperationsMap = new Dictionary<Type, Type>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Dictionary<Type, Type> OperationsMap { get; protected set; }
|
|
||||||
|
|
||||||
public void Add(Type serviceType, Type requestType, Type responseType)
|
|
||||||
{
|
|
||||||
this.OperationsMap[requestType] = serviceType;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Type GetServiceTypeByRequest(Type requestType)
|
|
||||||
{
|
|
||||||
Type serviceType;
|
|
||||||
OperationsMap.TryGetValue(requestType, out serviceType);
|
|
||||||
return serviceType;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,41 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using MediaBrowser.Model.Logging;
|
|
||||||
using MediaBrowser.Model.Services;
|
|
||||||
using ServiceStack.Host;
|
|
||||||
|
|
||||||
namespace ServiceStack
|
|
||||||
{
|
|
||||||
public class HttpHandlerFactory
|
|
||||||
{
|
|
||||||
// Entry point for HttpListener
|
|
||||||
public static RestHandler GetHandler(IHttpRequest httpReq, ILogger logger)
|
|
||||||
{
|
|
||||||
var pathInfo = httpReq.PathInfo;
|
|
||||||
|
|
||||||
var pathParts = pathInfo.TrimStart('/').Split('/');
|
|
||||||
if (pathParts.Length == 0)
|
|
||||||
{
|
|
||||||
logger.Error("Path parts empty for PathInfo: {0}, Url: {1}", pathInfo, httpReq.RawUrl);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
string contentType;
|
|
||||||
var restPath = RestHandler.FindMatchingRestPath(httpReq.HttpMethod, pathInfo, logger, out contentType);
|
|
||||||
|
|
||||||
if (restPath != null)
|
|
||||||
{
|
|
||||||
return new RestHandler
|
|
||||||
{
|
|
||||||
RestPath = restPath,
|
|
||||||
RequestName = restPath.RequestType.GetOperationName(),
|
|
||||||
ResponseContentType = contentType
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.Error("Could not find handler for {0}", pathInfo);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,127 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using MediaBrowser.Model.Services;
|
|
||||||
using ServiceStack.Host;
|
|
||||||
|
|
||||||
namespace ServiceStack
|
|
||||||
{
|
|
||||||
public static class HttpRequestExtensions
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
Input: http://localhost:96/Cambia3/Temp/Test.aspx/path/info?q=item#fragment
|
|
||||||
|
|
||||||
Some HttpRequest path and URL properties:
|
|
||||||
Request.ApplicationPath: /Cambia3
|
|
||||||
Request.CurrentExecutionFilePath: /Cambia3/Temp/Test.aspx
|
|
||||||
Request.FilePath: /Cambia3/Temp/Test.aspx
|
|
||||||
Request.Path: /Cambia3/Temp/Test.aspx/path/info
|
|
||||||
Request.PathInfo: /path/info
|
|
||||||
Request.PhysicalApplicationPath: D:\Inetpub\wwwroot\CambiaWeb\Cambia3\
|
|
||||||
Request.QueryString: /Cambia3/Temp/Test.aspx/path/info?query=arg
|
|
||||||
Request.Url.AbsolutePath: /Cambia3/Temp/Test.aspx/path/info
|
|
||||||
Request.Url.AbsoluteUri: http://localhost:96/Cambia3/Temp/Test.aspx/path/info?query=arg
|
|
||||||
Request.Url.Fragment:
|
|
||||||
Request.Url.Host: localhost
|
|
||||||
Request.Url.LocalPath: /Cambia3/Temp/Test.aspx/path/info
|
|
||||||
Request.Url.PathAndQuery: /Cambia3/Temp/Test.aspx/path/info?query=arg
|
|
||||||
Request.Url.Port: 96
|
|
||||||
Request.Url.Query: ?query=arg
|
|
||||||
Request.Url.Scheme: http
|
|
||||||
Request.Url.Segments: /
|
|
||||||
Cambia3/
|
|
||||||
Temp/
|
|
||||||
Test.aspx/
|
|
||||||
path/
|
|
||||||
info
|
|
||||||
* */
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Duplicate Params are given a unique key by appending a #1 suffix
|
|
||||||
/// </summary>
|
|
||||||
public static Dictionary<string, string> GetRequestParams(this IRequest request)
|
|
||||||
{
|
|
||||||
var map = new Dictionary<string, string>();
|
|
||||||
|
|
||||||
foreach (var name in request.QueryString.Keys)
|
|
||||||
{
|
|
||||||
if (name == null) continue; //thank you ASP.NET
|
|
||||||
|
|
||||||
var values = request.QueryString.GetValues(name);
|
|
||||||
if (values.Length == 1)
|
|
||||||
{
|
|
||||||
map[name] = values[0];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
for (var i = 0; i < values.Length; i++)
|
|
||||||
{
|
|
||||||
map[name + (i == 0 ? "" : "#" + i)] = values[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((request.Verb == HttpMethods.Post || request.Verb == HttpMethods.Put)
|
|
||||||
&& request.FormData != null)
|
|
||||||
{
|
|
||||||
foreach (var name in request.FormData.Keys)
|
|
||||||
{
|
|
||||||
if (name == null) continue; //thank you ASP.NET
|
|
||||||
|
|
||||||
var values = request.FormData.GetValues(name);
|
|
||||||
if (values.Length == 1)
|
|
||||||
{
|
|
||||||
map[name] = values[0];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
for (var i = 0; i < values.Length; i++)
|
|
||||||
{
|
|
||||||
map[name + (i == 0 ? "" : "#" + i)] = values[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return map;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Duplicate params have their values joined together in a comma-delimited string
|
|
||||||
/// </summary>
|
|
||||||
public static Dictionary<string, string> GetFlattenedRequestParams(this IRequest request)
|
|
||||||
{
|
|
||||||
var map = new Dictionary<string, string>();
|
|
||||||
|
|
||||||
foreach (var name in request.QueryString.Keys)
|
|
||||||
{
|
|
||||||
if (name == null) continue; //thank you ASP.NET
|
|
||||||
map[name] = request.QueryString[name];
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((request.Verb == HttpMethods.Post || request.Verb == HttpMethods.Put)
|
|
||||||
&& request.FormData != null)
|
|
||||||
{
|
|
||||||
foreach (var name in request.FormData.Keys)
|
|
||||||
{
|
|
||||||
if (name == null) continue; //thank you ASP.NET
|
|
||||||
map[name] = request.FormData[name];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return map;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void SetRoute(this IRequest req, RestPath route)
|
|
||||||
{
|
|
||||||
req.Items["__route"] = route;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static RestPath GetRoute(this IRequest req)
|
|
||||||
{
|
|
||||||
object route;
|
|
||||||
req.Items.TryGetValue("__route", out route);
|
|
||||||
return route as RestPath;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,34 +0,0 @@
|
||||||
//Copyright (c) Service Stack LLC. All Rights Reserved.
|
|
||||||
//License: https://raw.github.com/ServiceStack/ServiceStack/master/license.txt
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace ServiceStack
|
|
||||||
{
|
|
||||||
internal static class HttpMethods
|
|
||||||
{
|
|
||||||
static readonly string[] allVerbs = new[] {
|
|
||||||
"OPTIONS", "GET", "HEAD", "POST", "PUT", "DELETE", "TRACE", "CONNECT", // RFC 2616
|
|
||||||
"PROPFIND", "PROPPATCH", "MKCOL", "COPY", "MOVE", "LOCK", "UNLOCK", // RFC 2518
|
|
||||||
"VERSION-CONTROL", "REPORT", "CHECKOUT", "CHECKIN", "UNCHECKOUT",
|
|
||||||
"MKWORKSPACE", "UPDATE", "LABEL", "MERGE", "BASELINE-CONTROL", "MKACTIVITY", // RFC 3253
|
|
||||||
"ORDERPATCH", // RFC 3648
|
|
||||||
"ACL", // RFC 3744
|
|
||||||
"PATCH", // https://datatracker.ietf.org/doc/draft-dusseault-http-patch/
|
|
||||||
"SEARCH", // https://datatracker.ietf.org/doc/draft-reschke-webdav-search/
|
|
||||||
"BCOPY", "BDELETE", "BMOVE", "BPROPFIND", "BPROPPATCH", "NOTIFY",
|
|
||||||
"POLL", "SUBSCRIBE", "UNSUBSCRIBE" //MS Exchange WebDav: http://msdn.microsoft.com/en-us/library/aa142917.aspx
|
|
||||||
};
|
|
||||||
|
|
||||||
public static HashSet<string> AllVerbs = new HashSet<string>(allVerbs);
|
|
||||||
|
|
||||||
public const string Get = "GET";
|
|
||||||
public const string Put = "PUT";
|
|
||||||
public const string Post = "POST";
|
|
||||||
public const string Delete = "DELETE";
|
|
||||||
public const string Options = "OPTIONS";
|
|
||||||
public const string Head = "HEAD";
|
|
||||||
public const string Patch = "PATCH";
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -3,26 +3,11 @@ using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Runtime.Serialization;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace ServiceStack
|
namespace ServiceStack
|
||||||
{
|
{
|
||||||
public static class ReflectionExtensions
|
public static class ReflectionExtensions
|
||||||
{
|
{
|
||||||
public static bool IsInstanceOf(this Type type, Type thisOrBaseType)
|
|
||||||
{
|
|
||||||
while (type != null)
|
|
||||||
{
|
|
||||||
if (type == thisOrBaseType)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
type = type.BaseType();
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Type FirstGenericType(this Type type)
|
public static Type FirstGenericType(this Type type)
|
||||||
{
|
{
|
||||||
while (type != null)
|
while (type != null)
|
||||||
|
@ -54,44 +39,6 @@ namespace ServiceStack
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static PropertyInfo[] GetAllProperties(this Type type)
|
|
||||||
{
|
|
||||||
if (type.IsInterface())
|
|
||||||
{
|
|
||||||
var propertyInfos = new List<PropertyInfo>();
|
|
||||||
|
|
||||||
var considered = new List<Type>();
|
|
||||||
var queue = new Queue<Type>();
|
|
||||||
considered.Add(type);
|
|
||||||
queue.Enqueue(type);
|
|
||||||
|
|
||||||
while (queue.Count > 0)
|
|
||||||
{
|
|
||||||
var subType = queue.Dequeue();
|
|
||||||
foreach (var subInterface in subType.GetTypeInterfaces())
|
|
||||||
{
|
|
||||||
if (considered.Contains(subInterface)) continue;
|
|
||||||
|
|
||||||
considered.Add(subInterface);
|
|
||||||
queue.Enqueue(subInterface);
|
|
||||||
}
|
|
||||||
|
|
||||||
var typeProperties = subType.GetTypesProperties();
|
|
||||||
|
|
||||||
var newPropertyInfos = typeProperties
|
|
||||||
.Where(x => !propertyInfos.Contains(x));
|
|
||||||
|
|
||||||
propertyInfos.InsertRange(0, newPropertyInfos);
|
|
||||||
}
|
|
||||||
|
|
||||||
return propertyInfos.ToArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
return type.GetTypesProperties()
|
|
||||||
.Where(t => t.GetIndexParameters().Length == 0) // ignore indexed properties
|
|
||||||
.ToArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static PropertyInfo[] GetPublicProperties(this Type type)
|
public static PropertyInfo[] GetPublicProperties(this Type type)
|
||||||
{
|
{
|
||||||
if (type.IsInterface())
|
if (type.IsInterface())
|
||||||
|
@ -139,9 +86,7 @@ namespace ServiceStack
|
||||||
|
|
||||||
public static PropertyInfo[] GetSerializableProperties(this Type type)
|
public static PropertyInfo[] GetSerializableProperties(this Type type)
|
||||||
{
|
{
|
||||||
var properties = type.IsDto()
|
var properties = type.GetPublicProperties();
|
||||||
? type.GetAllProperties()
|
|
||||||
: type.GetPublicProperties();
|
|
||||||
return properties.OnlySerializableProperties(type);
|
return properties.OnlySerializableProperties(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -150,14 +95,7 @@ namespace ServiceStack
|
||||||
|
|
||||||
public static PropertyInfo[] OnlySerializableProperties(this PropertyInfo[] properties, Type type = null)
|
public static PropertyInfo[] OnlySerializableProperties(this PropertyInfo[] properties, Type type = null)
|
||||||
{
|
{
|
||||||
var isDto = type.IsDto();
|
var readableProperties = properties.Where(x => x.PropertyGetMethod(nonPublic: false) != null);
|
||||||
var readableProperties = properties.Where(x => x.PropertyGetMethod(nonPublic: isDto) != null);
|
|
||||||
|
|
||||||
if (isDto)
|
|
||||||
{
|
|
||||||
return readableProperties.Where(attr =>
|
|
||||||
attr.HasAttribute<DataMemberAttribute>()).ToArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
// else return those properties that are not decorated with IgnoreDataMember
|
// else return those properties that are not decorated with IgnoreDataMember
|
||||||
return readableProperties
|
return readableProperties
|
||||||
|
@ -206,36 +144,6 @@ namespace ServiceStack
|
||||||
return pis.ToArray();
|
return pis.ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static PropertyInfo[] GetTypesProperties(this Type subType)
|
|
||||||
{
|
|
||||||
var pis = new List<PropertyInfo>();
|
|
||||||
foreach (var pi in subType.GetRuntimeProperties())
|
|
||||||
{
|
|
||||||
var mi = pi.GetMethod ?? pi.SetMethod;
|
|
||||||
if (mi != null && mi.IsStatic) continue;
|
|
||||||
pis.Add(pi);
|
|
||||||
}
|
|
||||||
return pis.ToArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool HasAttribute<T>(this Type type)
|
|
||||||
{
|
|
||||||
return type.AllAttributes().Any(x => x.GetType() == typeof(T));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool HasAttribute<T>(this PropertyInfo pi)
|
|
||||||
{
|
|
||||||
return pi.AllAttributes().Any(x => x.GetType() == typeof(T));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool IsDto(this Type type)
|
|
||||||
{
|
|
||||||
if (type == null)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return type.HasAttribute<DataContractAttribute>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static MethodInfo PropertyGetMethod(this PropertyInfo pi, bool nonPublic = false)
|
public static MethodInfo PropertyGetMethod(this PropertyInfo pi, bool nonPublic = false)
|
||||||
{
|
{
|
||||||
return pi.GetMethod;
|
return pi.GetMethod;
|
||||||
|
@ -246,25 +154,15 @@ namespace ServiceStack
|
||||||
return propertyInfo.GetCustomAttributes(true).ToArray();
|
return propertyInfo.GetCustomAttributes(true).ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static object[] AllAttributes(this PropertyInfo propertyInfo, Type attrType)
|
|
||||||
{
|
|
||||||
return propertyInfo.GetCustomAttributes(true).Where(x => attrType.IsInstanceOf(x.GetType())).ToArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static object[] AllAttributes(this Type type)
|
public static object[] AllAttributes(this Type type)
|
||||||
{
|
{
|
||||||
return type.GetTypeInfo().GetCustomAttributes(true).ToArray();
|
return type.GetTypeInfo().GetCustomAttributes(true).ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static TAttr[] AllAttributes<TAttr>(this PropertyInfo pi)
|
public static List<TAttr> AllAttributes<TAttr>(this Type type)
|
||||||
{
|
|
||||||
return pi.AllAttributes(typeof(TAttr)).Cast<TAttr>().ToArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static TAttr[] AllAttributes<TAttr>(this Type type)
|
|
||||||
where TAttr : Attribute
|
where TAttr : Attribute
|
||||||
{
|
{
|
||||||
return type.GetTypeInfo().GetCustomAttributes<TAttr>(true).ToArray();
|
return type.GetTypeInfo().GetCustomAttributes<TAttr>(true).ToList();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using MediaBrowser.Model.Logging;
|
using MediaBrowser.Model.Logging;
|
||||||
using ServiceStack.Serialization;
|
using ServiceStack.Serialization;
|
||||||
|
|
||||||
namespace ServiceStack.Host
|
namespace ServiceStack
|
||||||
{
|
{
|
||||||
public class RestPath
|
public class RestPath
|
||||||
{
|
{
|
||||||
|
@ -50,7 +49,7 @@ namespace ServiceStack.Host
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return allowsAllVerbs
|
return allowsAllVerbs
|
||||||
? new[] { ActionContext.AnyAction }
|
? new[] { "ANY" }
|
||||||
: AllowedVerbs.Split(new[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries);
|
: AllowedVerbs.Split(new[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -69,24 +69,11 @@
|
||||||
<Prefer32Bit>false</Prefer32Bit>
|
<Prefer32Bit>false</Prefer32Bit>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="HttpUtils.cs" />
|
|
||||||
<Compile Include="Host\ContentTypes.cs" />
|
|
||||||
<Compile Include="ReflectionExtensions.cs" />
|
<Compile Include="ReflectionExtensions.cs" />
|
||||||
<Compile Include="StringMapTypeDeserializer.cs" />
|
<Compile Include="StringMapTypeDeserializer.cs" />
|
||||||
<Compile Include="HttpResult.cs" />
|
|
||||||
<Compile Include="ServiceStackHost.cs" />
|
<Compile Include="ServiceStackHost.cs" />
|
||||||
<Compile Include="ServiceStackHost.Runtime.cs" />
|
|
||||||
<Compile Include="Host\ServiceExec.cs" />
|
|
||||||
<Compile Include="UrlExtensions.cs" />
|
<Compile Include="UrlExtensions.cs" />
|
||||||
<Compile Include="Host\ActionContext.cs" />
|
<Compile Include="RestPath.cs" />
|
||||||
<Compile Include="HttpRequestExtensions.cs" />
|
|
||||||
<Compile Include="Host\RestPath.cs" />
|
|
||||||
<Compile Include="Host\ServiceController.cs" />
|
|
||||||
<Compile Include="Host\ServiceMetadata.cs" />
|
|
||||||
<Compile Include="Host\RestHandler.cs" />
|
|
||||||
<Compile Include="HttpResponseExtensionsInternal.cs" />
|
|
||||||
<Compile Include="HttpHandlerFactory.cs" />
|
|
||||||
<Compile Include="FilterAttributeCache.cs" />
|
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
|
@ -1,57 +0,0 @@
|
||||||
// Copyright (c) Service Stack LLC. All Rights Reserved.
|
|
||||||
// License: https://raw.github.com/ServiceStack/ServiceStack/master/license.txt
|
|
||||||
|
|
||||||
|
|
||||||
using MediaBrowser.Model.Services;
|
|
||||||
using ServiceStack.Support.WebHost;
|
|
||||||
|
|
||||||
namespace ServiceStack
|
|
||||||
{
|
|
||||||
public abstract partial class ServiceStackHost
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Applies the request filters. Returns whether or not the request has been handled
|
|
||||||
/// and no more processing should be done.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns></returns>
|
|
||||||
public virtual void ApplyRequestFilters(IRequest req, IResponse res, object requestDto)
|
|
||||||
{
|
|
||||||
//Exec all RequestFilter attributes with Priority < 0
|
|
||||||
var attributes = FilterAttributeCache.GetRequestFilterAttributes(requestDto.GetType());
|
|
||||||
var i = 0;
|
|
||||||
for (; i < attributes.Length && attributes[i].Priority < 0; i++)
|
|
||||||
{
|
|
||||||
var attribute = attributes[i];
|
|
||||||
attribute.RequestFilter(req, res, requestDto);
|
|
||||||
}
|
|
||||||
|
|
||||||
//Exec global filters
|
|
||||||
foreach (var requestFilter in GlobalRequestFilters)
|
|
||||||
{
|
|
||||||
requestFilter(req, res, requestDto);
|
|
||||||
}
|
|
||||||
|
|
||||||
//Exec remaining RequestFilter attributes with Priority >= 0
|
|
||||||
for (; i < attributes.Length && attributes[i].Priority >= 0; i++)
|
|
||||||
{
|
|
||||||
var attribute = attributes[i];
|
|
||||||
attribute.RequestFilter(req, res, requestDto);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Applies the response filters. Returns whether or not the request has been handled
|
|
||||||
/// and no more processing should be done.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns></returns>
|
|
||||||
public virtual void ApplyResponseFilters(IRequest req, IResponse res, object response)
|
|
||||||
{
|
|
||||||
//Exec global filters
|
|
||||||
foreach (var responseFilter in GlobalResponseFilters)
|
|
||||||
{
|
|
||||||
responseFilter(req, res, response);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -6,71 +6,24 @@ using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using MediaBrowser.Model.Logging;
|
|
||||||
using MediaBrowser.Model.Services;
|
using MediaBrowser.Model.Services;
|
||||||
using ServiceStack.Host;
|
|
||||||
|
|
||||||
namespace ServiceStack
|
namespace ServiceStack
|
||||||
{
|
{
|
||||||
public abstract partial class ServiceStackHost : IDisposable
|
public abstract class ServiceStackHost : IDisposable
|
||||||
{
|
{
|
||||||
public static ServiceStackHost Instance { get; protected set; }
|
public static ServiceStackHost Instance { get; protected set; }
|
||||||
|
|
||||||
protected ServiceStackHost(string serviceName)
|
protected ServiceStackHost()
|
||||||
{
|
{
|
||||||
ServiceName = serviceName;
|
|
||||||
ServiceController = CreateServiceController();
|
|
||||||
|
|
||||||
RestPaths = new List<RestPath>();
|
|
||||||
Metadata = new ServiceMetadata();
|
|
||||||
GlobalRequestFilters = new List<Action<IRequest, IResponse, object>>();
|
|
||||||
GlobalResponseFilters = new List<Action<IRequest, IResponse, object>>();
|
GlobalResponseFilters = new List<Action<IRequest, IResponse, object>>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract void Configure();
|
|
||||||
|
|
||||||
public abstract object CreateInstance(Type type);
|
public abstract object CreateInstance(Type type);
|
||||||
|
|
||||||
protected abstract ServiceController CreateServiceController();
|
|
||||||
|
|
||||||
public virtual ServiceStackHost Init()
|
|
||||||
{
|
|
||||||
Instance = this;
|
|
||||||
|
|
||||||
ServiceController.Init();
|
|
||||||
Configure();
|
|
||||||
|
|
||||||
ServiceController.AfterInit();
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public virtual ServiceStackHost Start(string urlBase)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException("Start(listeningAtUrlBase) is not supported by this AppHost");
|
|
||||||
}
|
|
||||||
|
|
||||||
public string ServiceName { get; set; }
|
|
||||||
|
|
||||||
public ServiceMetadata Metadata { get; set; }
|
|
||||||
|
|
||||||
public ServiceController ServiceController { get; set; }
|
|
||||||
|
|
||||||
public List<RestPath> RestPaths = new List<RestPath>();
|
|
||||||
|
|
||||||
public List<Action<IRequest, IResponse, object>> GlobalRequestFilters { get; set; }
|
|
||||||
|
|
||||||
public List<Action<IRequest, IResponse, object>> GlobalResponseFilters { get; set; }
|
public List<Action<IRequest, IResponse, object>> GlobalResponseFilters { get; set; }
|
||||||
|
|
||||||
public abstract T TryResolve<T>();
|
public abstract RouteAttribute[] GetRouteAttributes(Type requestType);
|
||||||
public abstract T Resolve<T>();
|
|
||||||
|
|
||||||
public virtual MediaBrowser.Model.Services.RouteAttribute[] GetRouteAttributes(Type requestType)
|
|
||||||
{
|
|
||||||
return requestType.AllAttributes<MediaBrowser.Model.Services.RouteAttribute>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract object GetTaskResult(Task task, string requestName);
|
|
||||||
|
|
||||||
public abstract Func<string, object> GetParseFn(Type propertyType);
|
public abstract Func<string, object> GetParseFn(Type propertyType);
|
||||||
|
|
||||||
|
@ -85,20 +38,5 @@ namespace ServiceStack
|
||||||
|
|
||||||
Instance = null;
|
Instance = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract ILogger Logger
|
|
||||||
{
|
|
||||||
get;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void OnLogError(Type type, string message)
|
|
||||||
{
|
|
||||||
Logger.Error(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void OnLogError(Type type, string message, Exception ex)
|
|
||||||
{
|
|
||||||
Logger.ErrorException(message, ex);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,11 +48,6 @@ namespace ServiceStack.Serialization
|
||||||
var propertyParseStringFn = GetParseFn(propertyType);
|
var propertyParseStringFn = GetParseFn(propertyType);
|
||||||
var propertySerializer = new PropertySerializerEntry(propertySetFn, propertyParseStringFn) { PropertyType = propertyType };
|
var propertySerializer = new PropertySerializerEntry(propertySetFn, propertyParseStringFn) { PropertyType = propertyType };
|
||||||
|
|
||||||
var attr = propertyInfo.AllAttributes<DataMemberAttribute>().FirstOrDefault();
|
|
||||||
if (attr != null && attr.Name != null)
|
|
||||||
{
|
|
||||||
propertySetterMap[attr.Name] = propertySerializer;
|
|
||||||
}
|
|
||||||
propertySetterMap[propertyInfo.Name] = propertySerializer;
|
propertySetterMap[propertyInfo.Name] = propertySerializer;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user