Skip to content

Commit

Permalink
change routing attribute classes and modify the version to preview
Browse files Browse the repository at this point in the history
  • Loading branch information
xuzhg committed Sep 29, 2020
1 parent c7660d6 commit 5a8bf48
Show file tree
Hide file tree
Showing 12 changed files with 177 additions and 64 deletions.
26 changes: 12 additions & 14 deletions src/Microsoft.AspNetCore.OData/Extensions/ActionModelExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ApplicationModels;
using Microsoft.AspNetCore.Mvc.Routing;
using Microsoft.AspNetCore.OData.Edm;
using Microsoft.AspNetCore.OData.Routing;
using Microsoft.AspNetCore.OData.Routing.Attributes;
using Microsoft.AspNetCore.OData.Routing.Template;
Expand Down Expand Up @@ -36,22 +35,22 @@ public static class ActionModelExtensions
};

/// <summary>
/// Test whether the action is not suitable for OData action.
/// Tests whether the action is not suitable for OData action.
/// </summary>
/// <param name="action">The given action model.</param>
/// <returns>True/False.</returns>
public static bool IsNonODataAction(this ActionModel action)
{
if (action == null)
{
throw new ArgumentNullException(nameof(action));
throw Error.ArgumentNull(nameof(action));
}

return action.Attributes.Any(a => a is NonODataActionAttribute);
}

/// <summary>
/// Test whether the action has the given parameter with the given type.
/// Tests whether the action has the given parameter with the given type.
/// </summary>
/// <typeparam name="T">The parameter type.</typeparam>
/// <param name="action">The given action model.</param>
Expand All @@ -61,7 +60,7 @@ public static bool HasParameter<T>(this ActionModel action, string parameterName
{
if (action == null)
{
throw new ArgumentNullException(nameof(action));
throw Error.ArgumentNull(nameof(action));
}

// parameter name is unique?
Expand All @@ -84,7 +83,7 @@ public static T GetAttribute<T>(this ActionModel action)
{
if (action == null)
{
throw new ArgumentNullException(nameof(action));
throw Error.ArgumentNull(nameof(action));
}

return action.Attributes.OfType<T>().FirstOrDefault();
Expand All @@ -101,12 +100,12 @@ public static bool HasODataKeyParameter(this ActionModel action, IEdmEntityType
{
if (action == null)
{
throw new ArgumentNullException(nameof(action));
throw Error.ArgumentNull(nameof(action));
}

if (entityType == null)
{
throw new ArgumentNullException(nameof(entityType));
throw Error.ArgumentNull(nameof(entityType));
}

// TODO: shall we make sure the type is matching?
Expand Down Expand Up @@ -175,12 +174,12 @@ public static void AddSelector(this ActionModel action, string prefix, IEdmModel
{
if (action == null)
{
throw new ArgumentNullException(nameof(action));
throw Error.ArgumentNull(nameof(action));
}

if (model == null)
{
throw new ArgumentNullException(nameof(model));
throw Error.ArgumentNull(nameof(model));
}

if (path == null)
Expand Down Expand Up @@ -227,17 +226,17 @@ public static void AddSelector(this ActionModel action, string httpMethod, strin
{
if (action == null)
{
throw new ArgumentNullException(nameof(action));
throw Error.ArgumentNull(nameof(action));
}

if (model == null)
{
throw new ArgumentNullException(nameof(model));
throw Error.ArgumentNull(nameof(model));
}

if (path == null)
{
throw new ArgumentNullException(nameof(path));
throw Error.ArgumentNull(nameof(path));
}

foreach (var template in path.GetTemplates())
Expand Down Expand Up @@ -276,7 +275,6 @@ private static void AddHttpMethod(ODataRoutingMetadata metadata, string httpMeth
metadata.HttpMethods.Add(method);
}
}

}
}

36 changes: 25 additions & 11 deletions src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1979,14 +1979,14 @@
</member>
<member name="M:Microsoft.AspNetCore.OData.Extensions.ActionModelExtensions.IsNonODataAction(Microsoft.AspNetCore.Mvc.ApplicationModels.ActionModel)">
<summary>
Test whether the action is not suitable for OData action.
Tests whether the action is not suitable for OData action.
</summary>
<param name="action">The given action model.</param>
<returns>True/False.</returns>
</member>
<member name="M:Microsoft.AspNetCore.OData.Extensions.ActionModelExtensions.HasParameter``1(Microsoft.AspNetCore.Mvc.ApplicationModels.ActionModel,System.String)">
<summary>
Test whether the action has the given parameter with the given type.
Tests whether the action has the given parameter with the given type.
</summary>
<typeparam name="T">The parameter type.</typeparam>
<param name="action">The given action model.</param>
Expand Down Expand Up @@ -10308,38 +10308,52 @@
<summary>
Initializes a new instance of the <see cref="T:Microsoft.AspNetCore.OData.Routing.Attributes.ODataRouteAttribute"/> class.
</summary>
<param name="pathTemplate">The OData URL path template that this action handles.</param>
<param name="template">The OData URL path template that this action handles, it could be null.</param>
</member>
<member name="M:Microsoft.AspNetCore.OData.Routing.Attributes.ODataRouteAttribute.#ctor(System.String,System.String)">
<summary>
Initializes a new instance of the <see cref="T:Microsoft.AspNetCore.OData.Routing.Attributes.ODataRoutePrefixAttribute"/> class.
</summary>
<param name="template">The OData URL path template that this action handles. It could be null, For example: "customers({key})".</param>
<param name="prefix">The OData routing prefix setting. For example: "v{version}". It could be null which means this attribute be suitable for routes.</param>
</member>
<member name="P:Microsoft.AspNetCore.OData.Routing.Attributes.ODataRouteAttribute.PathTemplate">
<summary>
Gets the OData URL path template that this action handles.
</summary>
</member>
<member name="P:Microsoft.AspNetCore.OData.Routing.Attributes.ODataRouteAttribute.ModelName">
<member name="P:Microsoft.AspNetCore.OData.Routing.Attributes.ODataRouteAttribute.RoutePrefix">
<summary>
Gets or sets the OData route with which to associate the attribute.
</summary>
</member>
<member name="T:Microsoft.AspNetCore.OData.Routing.Attributes.ODataRoutePrefixAttribute">
<summary>
Represents an attribute that can be placed on an OData controller to specify
the prefix that will be used for all actions of that controller.
the route template prefix that will be used for all actions of that controller.
</summary>
</member>
<member name="M:Microsoft.AspNetCore.OData.Routing.Attributes.ODataRoutePrefixAttribute.#ctor(System.String)">
<summary>
Initializes a new instance of the <see cref="T:Microsoft.AspNetCore.OData.Routing.Attributes.ODataRoutePrefixAttribute"/> class.
</summary>
<param name="routePrefix">The OData URL path template that this action handles.</param>
<param name="template">The OData URL path template that this controller handles.For example: "customers({key})".</param>
</member>
<member name="P:Microsoft.AspNetCore.OData.Routing.Attributes.ODataRoutePrefixAttribute.RoutePrefix">
<member name="M:Microsoft.AspNetCore.OData.Routing.Attributes.ODataRoutePrefixAttribute.#ctor(System.String,System.String)">
<summary>
Gets the OData URL path template that this action handles.
Initializes a new instance of the <see cref="T:Microsoft.AspNetCore.OData.Routing.Attributes.ODataRoutePrefixAttribute"/> class.
</summary>
<param name="template">The OData URL path template that this controller handles. For example: "customers({key})".</param>
<param name="prefix">The OData routing prefix setting. For example: "v{version}". It could be null which means this attribute be suitable for routes.</param>
</member>
<member name="P:Microsoft.AspNetCore.OData.Routing.Attributes.ODataRoutePrefixAttribute.ModelName">
<member name="P:Microsoft.AspNetCore.OData.Routing.Attributes.ODataRoutePrefixAttribute.PathPrefixTemplate">
<summary>
Gets the OData URL path prefix template that this controller handles.
</summary>
</member>
<member name="P:Microsoft.AspNetCore.OData.Routing.Attributes.ODataRoutePrefixAttribute.RoutePrefix">
<summary>
Gets or sets the model name with which to associate the attribute.
Gets or sets the route prefix with which to associate the attribute.
</summary>
</member>
<member name="T:Microsoft.AspNetCore.OData.Routing.Controllers.MetadataController">
Expand Down Expand Up @@ -10426,7 +10440,7 @@
<summary>
Gets the route prefix on the controller.
</summary>
<param name="modelName">The model name.</param>
<param name="routePrefix">The route prefix.</param>
<param name="controller">The controller.</param>
<returns>The prefix string list.</returns>
</member>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,30 @@ public sealed class ODataRouteAttribute : Attribute
/// Initializes a new instance of the <see cref="ODataRouteAttribute"/> class.
/// </summary>
public ODataRouteAttribute()
: this(pathTemplate: null)
: this(template: null)
{
}

/// <summary>
/// Initializes a new instance of the <see cref="ODataRouteAttribute"/> class.
/// </summary>
/// <param name="pathTemplate">The OData URL path template that this action handles.</param>
public ODataRouteAttribute(string pathTemplate)
/// <param name="template">The OData URL path template that this action handles, it could be null.</param>
public ODataRouteAttribute(string template)
: this(template, null)
{
PathTemplate = pathTemplate ?? String.Empty;
}

/// <summary>
/// Initializes a new instance of the <see cref="ODataRoutePrefixAttribute"/> class.
/// </summary>
/// <param name="template">The OData URL path template that this action handles. It could be null, For example: "customers({key})".</param>
/// <param name="prefix">The OData routing prefix setting. For example: "v{version}". It could be null which means this attribute be suitable for routes.</param>
public ODataRouteAttribute(string template, string prefix)
{
PathTemplate = template ?? string.Empty;

// It could be null which means this attribute can apply for all routes.
RoutePrefix = prefix;
}

/// <summary>
Expand All @@ -37,6 +50,6 @@ public ODataRouteAttribute(string pathTemplate)
/// <summary>
/// Gets or sets the OData route with which to associate the attribute.
/// </summary>
public string ModelName { get; set; }
public string RoutePrefix { get; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,28 +7,46 @@ namespace Microsoft.AspNetCore.OData.Routing.Attributes
{
/// <summary>
/// Represents an attribute that can be placed on an OData controller to specify
/// the prefix that will be used for all actions of that controller.
/// the route template prefix that will be used for all actions of that controller.
/// </summary>
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
public sealed class ODataRoutePrefixAttribute : Attribute
{
/// <summary>
/// Initializes a new instance of the <see cref="ODataRoutePrefixAttribute"/> class.
/// </summary>
/// <param name="routePrefix">The OData URL path template that this action handles.</param>
public ODataRoutePrefixAttribute(string routePrefix)
/// <param name="template">The OData URL path template that this controller handles.For example: "customers({key})".</param>
public ODataRoutePrefixAttribute(string template)
: this (template, null)
{
RoutePrefix = routePrefix ?? throw new ArgumentNullException(nameof(routePrefix));
}

/// <summary>
/// Gets the OData URL path template that this action handles.
/// Initializes a new instance of the <see cref="ODataRoutePrefixAttribute"/> class.
/// </summary>
public string RoutePrefix { get; }
/// <param name="template">The OData URL path template that this controller handles. For example: "customers({key})".</param>
/// <param name="prefix">The OData routing prefix setting. For example: "v{version}". It could be null which means this attribute be suitable for routes.</param>
public ODataRoutePrefixAttribute(string template, string prefix)
{
if (string.IsNullOrEmpty(template))
{
throw Error.ArgumentNullOrEmpty(nameof(template));
}

PathPrefixTemplate = template;

// It could be null which means this attribute can apply for all routes.
RoutePrefix = prefix;
}

/// <summary>
/// Gets or sets the model name with which to associate the attribute.
/// Gets the OData URL path prefix template that this controller handles.
/// </summary>
public string ModelName { get; set; }
public string PathPrefixTemplate { get; }

/// <summary>
/// Gets or sets the route prefix with which to associate the attribute.
/// </summary>
public string RoutePrefix { get; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
using Microsoft.AspNetCore.OData.Routing.Template;
using Microsoft.Extensions.Logging;
using Microsoft.OData;
using Microsoft.OData.UriParser;

namespace Microsoft.AspNetCore.OData.Routing.Conventions
{
Expand Down Expand Up @@ -46,7 +45,7 @@ public virtual bool AppliesToController(ODataControllerActionContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
throw Error.ArgumentNull(nameof(context));
}

// It allows to use attribute routing without ODataRoutePrefixAttribute.
Expand All @@ -60,8 +59,9 @@ public virtual bool AppliesToController(ODataControllerActionContext context)

foreach (ODataRouteAttribute routeAttribute in routeAttributes)
{
// If we have the model name setting, make sure we only let the attribute with the same model name to pass.
if (!string.IsNullOrEmpty(routeAttribute.ModelName) && routeAttribute.ModelName != context.Prefix)
// If we have the route prefix name setting, make sure we only let the attribute with the same route prefx to pass.
if (!string.IsNullOrEmpty(routeAttribute.RoutePrefix) &&
!string.Equals(routeAttribute.RoutePrefix, routePrefix, StringComparison.OrdinalIgnoreCase))
{
continue;
}
Expand Down Expand Up @@ -103,10 +103,10 @@ public virtual bool AppliesToAction(ODataControllerActionContext context)
/// <summary>
/// Gets the route prefix on the controller.
/// </summary>
/// <param name="modelName">The model name.</param>
/// <param name="routePrefix">The route prefix.</param>
/// <param name="controller">The controller.</param>
/// <returns>The prefix string list.</returns>
private IEnumerable<string> GetODataRoutePrefixes(string modelName, ControllerModel controller)
private IEnumerable<string> GetODataRoutePrefixes(string routePrefix, ControllerModel controller)
{
Contract.Assert(controller != null);

Expand All @@ -118,27 +118,28 @@ private IEnumerable<string> GetODataRoutePrefixes(string modelName, ControllerMo

foreach (ODataRoutePrefixAttribute prefixAttribute in prefixAttributes)
{
// If we have the model name setting, make sure we only let the attribute with the same model name to pass.
if (!string.IsNullOrEmpty(prefixAttribute.ModelName) && prefixAttribute.ModelName != modelName)
// If we have the route prefix setting, make sure we only let the attribute with the same route prefix (ignore case) to pass.
if (!string.IsNullOrEmpty(prefixAttribute.RoutePrefix) &&
!string.Equals(prefixAttribute.RoutePrefix, routePrefix, StringComparison.OrdinalIgnoreCase))
{
continue;
}

string prefix = prefixAttribute.RoutePrefix;
if (prefix != null && prefix.StartsWith("/", StringComparison.Ordinal))
string template = prefixAttribute.PathPrefixTemplate;
if (template != null && template.StartsWith("/", StringComparison.Ordinal))
{
// So skip it? or let's remove the "/" and let it go?
_logger.LogWarning($"The OData route prefix '{prefix}' on the controller '{controller.ControllerName}' starts with a '/'. Route prefixes cannot start with a '/'.");
_logger.LogWarning($"The OData route prefix '{template}' on the controller '{controller.ControllerName}' starts with a '/'. Route prefixes cannot start with a '/'.");

prefix = prefix.TrimStart('/');
template = template.TrimStart('/');
}

if (prefix != null && prefix.EndsWith("/", StringComparison.Ordinal))
if (template != null && template.EndsWith("/", StringComparison.Ordinal))
{
prefix = prefix.TrimEnd('/');
template = template.TrimEnd('/');
}

yield return prefix;
yield return template;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
using System.Diagnostics.Contracts;
using System.Globalization;
using System.Linq;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.OData.Formatter;
using Microsoft.AspNetCore.Routing;
using Microsoft.OData;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,7 @@ public static ArgumentNullException ThrowsArgumentNull(Action testCode, string p
/// <exception cref="ThrowsException">Thrown when an exception was not thrown, or when an exception of the incorrect type is thrown</exception>
public static ArgumentException ThrowsArgumentNullOrEmpty(Action testCode, string paramName)
{
return Throws<ArgumentException>(testCode, "Value cannot be null or empty.\r\nParameter name: " + paramName, allowDerivedExceptions: false);
return Throws<ArgumentException>(testCode, $"The argument '{paramName}' is null or empty. (Parameter '{paramName}')", allowDerivedExceptions: false);
}

/// <summary>
Expand Down
Loading

0 comments on commit 5a8bf48

Please sign in to comment.