private async Task ProcessPutCommand(RestProcessParameters param, CancellationToken ct) { var restQuery = new RestQueryString(param); var entityDefinition = param.Model.EntitiesByPluralName.SafeGet(restQuery.Root); if (entityDefinition == null) { throw new BadRequestException($"Unable to find an entity definition named {restQuery.Root}."); } if (restQuery.AdditionalQualifier.IsNullOrEmpty()) { throw new BadRequestException($"A put request must apply to a specific id."); } var propertyBag = param.RequestSerializer.Deserialize(param.Context.Request.Body); propertyBag[MetaConstants.IdProperty] = restQuery.AdditionalQualifier; var entity = await EntityFactory.Hydrate(entityDefinition, propertyBag, ct); entity.Etag = param.Headers.IfNoneMatch(); var result = await EntityService.Update(entity, ct); Respond(param, result.ToPropertyBag(), StatusCodes.Status200OK, new Dictionary <string, string> { { WebConstants.ETagHeader, entity.Etag }, { WebConstants.LastModifiedHeader, result.LastModified.SelectOrDefault(x => x.ToUtcIso8601()) } }); }
private async Task ProcessDeleteCommand(RestProcessParameters param, CancellationToken ct) { var restQuery = new RestQueryString(param); var entityDefinition = param.Model.EntitiesByPluralName.SafeGet(restQuery.Root); if (entityDefinition == null) { throw new BadRequestException($"Unable to find an entity definition named {restQuery.Root}."); } if (restQuery.AdditionalQualifier.IsNullOrEmpty()) { throw new BadRequestException($"A delete request must include an id."); } var result = await EntityService.Delete(entityDefinition, restQuery.AdditionalQualifier, ct); if (!result) { Respond(param, new MessageResponse($"Unable to find a {entityDefinition.SingleName} with id {restQuery.AdditionalQualifier}."), StatusCodes.Status404NotFound); return; } Respond(param, null, StatusCodes.Status204NoContent); }
private async Task ProcessPostCommand(RestProcessParameters param, CancellationToken ct) { var restQuery = new RestQueryString(param); var entityDefinition = param.Model.EntitiesByPluralName.SafeGet(restQuery.Root); if (entityDefinition == null) { throw new BadRequestException($"Unable to find an entity definition named {restQuery.Root}."); } if (!restQuery.AdditionalQualifier.IsNullOrEmpty()) { throw new BadRequestException($"A post request should not have an additional qualifier."); } var propertyBag = param.RequestSerializer.Deserialize(param.Context.Request.Body); var entity = await EntityFactory.Create(entityDefinition, propertyBag, ct); var result = await EntityService.Create(entity, ct); Respond(param, result.ToPropertyBag(), StatusCodes.Status201Created, new Dictionary <string, string> { { WebConstants.ETagHeader, result.Etag }, { WebConstants.LastModifiedHeader, result.LastModified.SelectOrDefault(x => x.ToUtcIso8601()) }, { "Location", param.Context.Request.AbsoluteUri( $"{_options.Value.MountPoint}/{entityDefinition.Model.Name}/{entityDefinition.PluralName}/{WebUtility.UrlEncode(result.Id.ToString())}") } }); }
public RestQueryString(RestProcessParameters param) { Root = param.PathSegments.Length.IsOdd() ? param.PathSegments.Last() : param.PathSegments.ElementAtOrDefault(param.PathSegments.Length - 2); AdditionalQualifier = param.PathSegments.Length.IsEven() ? param.PathSegments.Last() : null; var pathValues = new Dictionary <string, string>(); var remainingPathSegments = param.PathSegments.Length.IsOdd() ? param.PathSegments.Take(param.PathSegments.Length - 1) : param.PathSegments.Take(param.PathSegments.Length - 2); string currentKey = null; foreach (var remainingPathSegment in remainingPathSegments) { if (currentKey == null) { currentKey = remainingPathSegment; } else { pathValues.Add(currentKey, remainingPathSegment); currentKey = null; } } PathValues = pathValues.ToImmutableDictionary(); QueryStringValues = param.Context.Request.Query.ToImmutableDictionary(StringComparer.OrdinalIgnoreCase); }
private async Task ProcessPatchCommand(RestProcessParameters param, CancellationToken ct) { var restQuery = new RestQueryString(param); var entityDefinition = param.Model.EntitiesByPluralName.SafeGet(restQuery.Root); if (entityDefinition == null) { throw new BadRequestException($"Unable to find an entity definition named {restQuery.Root}."); } if (restQuery.AdditionalQualifier.IsNullOrEmpty()) { throw new BadRequestException($"A patch request must apply to a specific id."); } if (!(param.RequestSerializer is JsonRestSerializer)) { throw new BadRequestException("Only JSON Patch is supported for patching resources."); } JsonPatchDocument jsonPatchDocument; try { jsonPatchDocument = HiveJsonSerializer.Instance.Deserialize <JsonPatchDocument>(param.Context.Request.Body); } catch (Exception ex) { throw new BadRequestException("Malformed JSON Patch.", ex); } var entity = await EntityService.GetById(entityDefinition, restQuery.AdditionalQualifier, ct); if (entity == null) { Respond(param, new MessageResponse($"Unable to find a {entityDefinition.SingleName} with id {restQuery.AdditionalQualifier}"), StatusCodes.Status404NotFound); return; } jsonPatchDocument.ApplyTo(entity, new EntityPatchObjectAdapter()); entity.Etag = param.Headers.IfNoneMatch(); var result = await EntityService.Update(entity, ct); Respond(param, result.ToPropertyBag(), StatusCodes.Status200OK, new Dictionary <string, string> { { WebConstants.ETagHeader, result.Etag }, { WebConstants.LastModifiedHeader, result.LastModified.SelectOrDefault(x => x.ToUtcIso8601()) } }); }
private void Respond(RestProcessParameters param, object message, int statusCode, IDictionary <string, string> responseHeaders = null) { param.Context.Response.StatusCode = statusCode; foreach (var responseHeader in responseHeaders.Safe()) { param.Context.Response.Headers.Add(responseHeader.Key, new StringValues(responseHeader.Value)); } if (message != null) { param.Context.Response.Headers["Content-Type"] = param.ResponseSerializer.MediaTypes.First(); param.ResponseSerializer.Serialize(message, param.Context.Response.Body); } }
private static void FillOperators(IQuery query, RestQueryString restQuery, RestProcessParameters param) { if (restQuery.QueryStringValues.ContainsKey(RestConstants.LimitOperator)) { var maxResults = restQuery.QueryStringValues[RestConstants.LimitOperator].First().IntSafeInvariantParse(); query.SetMaxResults(maxResults); } if (restQuery.QueryStringValues.ContainsKey(RestConstants.OrderOperator)) { var orderby = restQuery.QueryStringValues[RestConstants.OrderOperator].FirstOrDefault(); var orderbyMatch = OrderByRegex.Match(orderby); if (orderbyMatch.Success) { switch (orderbyMatch.Groups["asc"].Value.IsNullOrEmpty() ? "asc" : orderbyMatch.Groups["asc"].Value) { case "asc": query.AddOrder(Order.Asc(orderbyMatch.Groups["prop"].Value)); break; case "desc": query.AddOrder(Order.Desc(orderbyMatch.Groups["prop"].Value)); break; } } } if (restQuery.QueryStringValues.ContainsKey(RestConstants.IncludeOperator)) { foreach (var value in restQuery.QueryStringValues[RestConstants.IncludeOperator]) { query.Include(value); } } if (restQuery.QueryStringValues.ContainsKey(RestConstants.SelectOperator)) { query.SetProjection(Projection.Properties(restQuery.QueryStringValues[RestConstants.SelectOperator])); } if (param.Context.Request.Headers.ContainsKey(RestConstants.ContinuationTokenHeader)) { var continuationToken = param.Context.Request.Headers[RestConstants.ContinuationTokenHeader].FirstOrDefault(); query.SetContinuationToken(continuationToken); } }
protected override async Task <bool> ProcessInternal(HttpContext context, CancellationToken ct) { var request = context.Request; PathString partialPath; if (!request.Path.StartsWithSegments(_options.Value.MountPoint, out partialPath)) { return(false); } var pathSegments = partialPath.Value?.Split('/').Where(x => !x.Trim().IsNullOrEmpty()).ToArray(); if (pathSegments == null || pathSegments.Length < 2) { return(false); } var model = await MetaService.GetModel(pathSegments[0], ct); pathSegments = pathSegments.Skip(1).ToArray(); var headers = context.Request.GetTypedHeaders(); var requestSerializer = _serializerFactory.GetByMediaType(headers.ContentType?.MediaType); var responseSerializer = headers.Accept.IsNullOrEmpty() ? requestSerializer : _serializerFactory.GetByMediaType(headers.Accept.Safe().Select(x => x.MediaType)); var processParams = new RestProcessParameters(context, headers, pathSegments, model, requestSerializer, responseSerializer); if (HttpMethods.IsGet(request.Method)) { await ProcessQuery(processParams, ct); return(true); } if (HttpMethods.IsPost(request.Method)) { await ProcessPostCommand(processParams, ct); return(true); } if (HttpMethods.IsPut(request.Method)) { await ProcessPutCommand(processParams, ct); return(true); } if (HttpMethods.IsPatch(request.Method)) { await ProcessPatchCommand(processParams, ct); return(true); } if (HttpMethods.IsDelete(request.Method)) { await ProcessDeleteCommand(processParams, ct); return(true); } throw new NotSupportedException(); }
private IQuery CreateQuery(IEntityDefinition entityDefinition, RestQueryString restQuery, RestProcessParameters param) { var query = EntityService.CreateQuery(entityDefinition); foreach ( var queryStringValue in restQuery.QueryStringValues.Where(x => !x.Key.StartsWith(RestConstants.ReservedOperatorsPrefix))) { RecursiveFillQuery(query, null, queryStringValue.Key, queryStringValue.Value); } FillOperators(query, restQuery, param); if (restQuery.PathValues.Count == 0) { return(query); } if (restQuery.PathValues.Count > 1) { throw new BadRequestException("Multi-level query expressions in path is not supported."); } var pathValue = restQuery.PathValues.First(); var targetEntityDef = entityDefinition.Model.EntitiesByPluralName.SafeGet(pathValue.Key); if (targetEntityDef == null) { throw new NotFoundException($"Unable to find an entity definition named {pathValue.Key}"); } var targetPropertyDefinition = entityDefinition.Properties.SafeGet(targetEntityDef.SingleName); if (targetPropertyDefinition != null) { var subQuery = query.GetOrCreateSubQuery(targetPropertyDefinition.Name); RecursiveFillQuery(subQuery, null, MetaConstants.IdProperty, pathValue.Value); } return(query); }
private async Task ProcessQuery(RestProcessParameters param, CancellationToken ct) { var restQuery = new RestQueryString(param); var entityDefinition = param.Model.EntitiesByPluralName.SafeGet(restQuery.Root); if (entityDefinition == null) { throw new NotFoundException($"Unable to find an entity definition named {restQuery.Root}."); } if (!restQuery.AdditionalQualifier.IsNullOrEmpty()) { var result = await EntityService.GetById(entityDefinition, restQuery.AdditionalQualifier, ct); if (result == null) { Respond(param, new MessageResponse($"Unable to find a {entityDefinition.SingleName} with id {restQuery.AdditionalQualifier}"), StatusCodes.Status404NotFound); return; } var ifNoneMatch = param.Headers.IfNoneMatch(); if (string.Equals(ifNoneMatch, result.Etag, StringComparison.Ordinal)) { Respond(param, null, StatusCodes.Status304NotModified, new Dictionary <string, string> { { WebConstants.ETagHeader, result.Etag }, { WebConstants.LastModifiedHeader, result.LastModified.SelectOrDefault(x => x.ToUtcIso8601()) } }); } else { Respond(param, result.ToPropertyBag(), StatusCodes.Status200OK, new Dictionary <string, string> { { WebConstants.ETagHeader, result.Etag }, { WebConstants.LastModifiedHeader, result.LastModified.SelectOrDefault(x => x.ToUtcIso8601()) } }); } } else { var query = CreateQuery(entityDefinition, restQuery, param); if (query.MaxResults.HasValue) { var result = await query.ToContinuationEnumerable <IEntity>(ct); Respond(param, result.Select(x => x.ToPropertyBag()).ToArray(), StatusCodes.Status200OK, new Dictionary <string, string> { { RestConstants.ContinuationTokenHeader, result.ContinuationToken } }); } else { var result = await query.ToEnumerable <IEntity>(ct); Respond(param, result.Select(x => x.ToPropertyBag()).ToArray(), StatusCodes.Status200OK); } } }