/// <summary> /// Determines whether the route constraint matches the specified criteria. /// </summary> /// <param name="httpContext">The current <see cref="HttpContext">HTTP context</see>.</param> /// <param name="route">The current <see cref="IRouter">route</see>.</param> /// <param name="routeKey">The key of the route parameter to match.</param> /// <param name="values">The current <see cref="RouteValueDictionary">collection</see> of route values.</param> /// <param name="routeDirection">The <see cref="RouteDirection">route direction</see> to match.</param> /// <returns>True if the route constraint is matched; otherwise, false.</returns> public bool Match(HttpContext httpContext, IRouter route, string routeKey, RouteValueDictionary values, RouteDirection routeDirection) { if (IsNullOrEmpty(routeKey)) { return(false); } var properties = httpContext.ApiVersionProperties(); if (values.TryGetValue(routeKey, out string value)) { properties.RawApiVersion = value; } else { return(false); } if (routeDirection == UrlGeneration) { return(!IsNullOrEmpty(value)); } if (TryParse(value, out var requestedVersion)) { properties.ApiVersion = requestedVersion; return(true); } return(false); }
RequestHandler ClientError(HttpContext httpContext, ActionSelectionResult selectionResult) { Contract.Requires(httpContext != null); Contract.Requires(selectionResult != null); const RequestHandler NotFound = default(RequestHandler); var candidates = selectionResult.CandidateActions.OrderBy(kvp => kvp.Key).SelectMany(kvp => kvp.Value).Distinct().ToArray(); if (candidates.Length == 0) { return(NotFound); } var properties = httpContext.ApiVersionProperties(); var method = httpContext.Request.Method; var requestUrl = new Lazy <string>(httpContext.Request.GetDisplayUrl); var requestedVersion = properties.RawApiVersion; var parsedVersion = properties.ApiVersion; var actionNames = new Lazy <string>(() => Join(NewLine, candidates.Select(a => a.DisplayName))); var allowedMethods = new Lazy <HashSet <string> >(() => AllowedMethodsFromCandidates(candidates)); var apiVersions = new Lazy <ApiVersionModel>(selectionResult.CandidateActions.SelectMany(l => l.Value.Select(a => a.GetProperty <ApiVersionModel>()).Where(m => m != null)).Aggregate); var handlerContext = new RequestHandlerContext(ErrorResponseProvider, ApiVersionReporter, apiVersions); if (parsedVersion == null) { if (IsNullOrEmpty(requestedVersion)) { if (Options.AssumeDefaultVersionWhenUnspecified || candidates.Any(c => c.IsApiVersionNeutral())) { return(VersionNeutralUnmatched(handlerContext, requestUrl.Value, method, allowedMethods.Value, actionNames.Value)); } return(UnspecifiedApiVersion(handlerContext, actionNames.Value)); } else if (!TryParse(requestedVersion, out parsedVersion)) { return(MalformedApiVersion(handlerContext, requestUrl.Value, requestedVersion)); } } else if (IsNullOrEmpty(requestedVersion)) { return(VersionNeutralUnmatched(handlerContext, requestUrl.Value, method, allowedMethods.Value, actionNames.Value)); } else { requestedVersion = parsedVersion.ToString(); } return(Unmatched(handlerContext, requestUrl.Value, method, allowedMethods.Value, actionNames.Value, parsedVersion, requestedVersion)); }
/// <summary> /// Gets the current API version requested. /// </summary> /// <param name="context">The current <see cref="HttpContext">HTTP context</see> to get the API version for.</param> /// <returns>The requested <see cref="ApiVersion">API version</see> or <c>null</c>.</returns> /// <remarks>This method will return <c>null</c> no service API version was requested or the requested /// service API version is in an invalid format.</remarks> /// <exception cref="AmbiguousApiVersionException">Multiple, different API versions were requested.</exception> public static ApiVersion GetRequestedApiVersion(this HttpContext context) { Arg.NotNull(context, nameof(context)); return(context.ApiVersionProperties().ApiVersion); }
RequestHandler ClientError(HttpContext httpContext, ActionSelectionResult selectionResult) { Contract.Requires(httpContext != null); Contract.Requires(selectionResult != null); const RequestHandler NotFound = default(RequestHandler); var candidates = selectionResult.CandidateActions.OrderBy(kvp => kvp.Key).SelectMany(kvp => kvp.Value).Distinct().ToArray(); if (candidates.Length == 0) { return(NotFound); } var properties = httpContext.ApiVersionProperties(); var requestedVersion = default(string); var parsedVersion = properties.ApiVersion; var actionNames = new Lazy <string>(() => Join(NewLine, candidates.Select(a => a.DisplayName))); var allowedMethods = new Lazy <HashSet <string> >(() => AllowedMethodsFromCandidates(candidates)); var newRequestHandler = default(Func <RequestHandlerContext, RequestHandler>); var apiVersions = new Lazy <ApiVersionModel>(selectionResult.CandidateActions.SelectMany(l => l.Value.Select(a => a.GetProperty <ApiVersionModel>()).Where(m => m != null)).Aggregate); var handlerContext = new RequestHandlerContext(ErrorResponseProvider, ApiVersionReporter, apiVersions); if (parsedVersion == null) { var versionNeutral = new Lazy <bool>(() => candidates.Any(c => c.IsApiVersionNeutral())); requestedVersion = properties.RawApiVersion; if (IsNullOrEmpty(requestedVersion) && !versionNeutral.Value) { Logger.ApiVersionUnspecified(actionNames.Value); handlerContext.Code = ApiVersionUnspecified; handlerContext.Message = SR.ApiVersionUnspecified; return(new BadRequestHandler(handlerContext)); } else if (TryParse(requestedVersion, out parsedVersion)) { Logger.ApiVersionUnmatched(parsedVersion, actionNames.Value); handlerContext.Code = UnsupportedApiVersion; if (allowedMethods.Value.Contains(httpContext.Request.Method)) { newRequestHandler = c => new BadRequestHandler(c); } else { handlerContext.AllowedMethods = allowedMethods.Value.ToArray(); newRequestHandler = c => new MethodNotAllowedHandler(c); } } else if (versionNeutral.Value) { Logger.ApiVersionUnspecified(actionNames.Value); handlerContext.Code = UnsupportedApiVersion; handlerContext.Message = SR.VersionNeutralResourceNotSupported.FormatDefault(httpContext.Request.GetDisplayUrl()); if (allowedMethods.Value.Contains(httpContext.Request.Method)) { return(new BadRequestHandler(handlerContext)); } handlerContext.AllowedMethods = allowedMethods.Value.ToArray(); return(new MethodNotAllowedHandler(handlerContext)); } else { Logger.ApiVersionInvalid(requestedVersion); handlerContext.Code = InvalidApiVersion; newRequestHandler = c => new BadRequestHandler(c); } } else { Logger.ApiVersionUnmatched(parsedVersion, actionNames.Value); requestedVersion = parsedVersion.ToString(); handlerContext.Code = UnsupportedApiVersion; if (allowedMethods.Value.Contains(httpContext.Request.Method)) { newRequestHandler = c => new BadRequestHandler(c); } else { handlerContext.AllowedMethods = allowedMethods.Value.ToArray(); newRequestHandler = c => new MethodNotAllowedHandler(c); } } handlerContext.Message = SR.VersionedResourceNotSupported.FormatDefault(httpContext.Request.GetDisplayUrl(), requestedVersion); return(newRequestHandler(handlerContext)); }
public async Task Invoke(HttpContext context) { var reqestUrl = context.Request.GetDisplayUrl(); var response = context.Response; var currentBody = response.Body; using (var memoryStream = new MemoryStream()) { response.Body = memoryStream; await _next(context); var apiVersionProperties = context.ApiVersionProperties(); var apiVersion = apiVersionProperties.RawApiVersion; response.Body = currentBody; memoryStream.Seek(0, SeekOrigin.Begin); var readToEnd = new StreamReader(memoryStream).ReadToEnd(); var statusCode = response.StatusCode; object objResult = null; try { objResult = statusCode < 400 ? JsonConvert.DeserializeObject(readToEnd) : JsonConvert.DeserializeObject <ApiError>(readToEnd); } catch (Exception ex) { //TODO: Log Unknown Error Response Content objResult = JsonConvert.DeserializeObject(readToEnd); } string statusMessage; var sb = new StringBuilder(); sb.Append(Enum.GetName(typeof(HttpStatusCode), statusCode)); if (statusCode > 399) { if (statusCode == 401) { var authErrors = response.Headers["WWW-Authenticate"]; foreach (var authErr in authErrors) { sb.Append($" - {authErr.Replace("\"", "'")}"); } } } statusMessage = sb.ToString(); var result = new ApiResponse((HttpStatusCode)statusCode, statusMessage, reqestUrl, apiVersion, objResult); response.ContentType = response.ContentType ?? "application/json; charset=utf-8"; await response.WriteAsync(JsonConvert.SerializeObject(result)); } }