internal static IODataResponseMessage PrepareResponseMessage(IWebApiRequestMessage internalRequest, IWebApiHeaders internalRequestHeaders, Func <IServiceProvider, ODataMessageWrapper> getODataMessageWrapper) { string preferHeader = RequestPreferenceHelpers.GetRequestPreferHeader(internalRequestHeaders); string annotationFilter = null; string omitValues = null; int? maxPageSize = null; if (!String.IsNullOrEmpty(preferHeader)) { ODataMessageWrapper messageWrapper = getODataMessageWrapper(null); messageWrapper.SetHeader(RequestPreferenceHelpers.PreferHeaderName, preferHeader); annotationFilter = messageWrapper.PreferHeader().AnnotationFilter; omitValues = messageWrapper.PreferHeader().OmitValues; maxPageSize = messageWrapper.PreferHeader().MaxPageSize; } IODataResponseMessage responseMessage = getODataMessageWrapper(internalRequest.RequestContainer); if (annotationFilter != null) { responseMessage.PreferenceAppliedHeader().AnnotationFilter = annotationFilter; } if (omitValues != null) { responseMessage.PreferenceAppliedHeader().OmitValues = omitValues; } if (maxPageSize != null) { responseMessage.PreferenceAppliedHeader().MaxPageSize = maxPageSize; } return(responseMessage); }
internal static bool CanWriteType( Type type, IEnumerable <ODataPayloadKind> payloadKinds, bool isGenericSingleResult, IWebApiRequestMessage internalRequest, Func <Type, ODataSerializer> getODataPayloadSerializer) { if (type == null) { throw Error.ArgumentNull("type"); } ODataPayloadKind?payloadKind; Type elementType; if (typeof(IEdmObject).IsAssignableFrom(type) || (TypeHelper.IsCollection(type, out elementType) && typeof(IEdmObject).IsAssignableFrom(elementType))) { payloadKind = GetEdmObjectPayloadKind(type, internalRequest); } else { payloadKind = GetClrObjectResponsePayloadKind(type, isGenericSingleResult, getODataPayloadSerializer); } return(payloadKind == null ? false : payloadKinds.Contains(payloadKind.Value)); }
/// <summary> /// Execute the query. /// </summary> /// <param name="responseValue">The response value.</param> /// <param name="singleResultCollection">The content as SingleResult.Queryable.</param> /// <param name="actionDescriptor">The action context, i.e. action and controller name.</param> /// <param name="modelFunction">A function to get the model.</param> /// <param name="request">The internal request.</param> /// <param name="createQueryOptionFunction">A function used to create and validate query options.</param> /// <returns></returns> private object ExecuteQuery( object responseValue, IQueryable singleResultCollection, IWebApiActionDescriptor actionDescriptor, Func <Type, IEdmModel> modelFunction, IWebApiRequestMessage request, Func <ODataQueryContext, IODataQueryOptions> createQueryOptionFunction) { ODataQueryContext queryContext = GetODataQueryContext(responseValue, singleResultCollection, actionDescriptor, modelFunction, request.Context.Path); // Create and validate the query options. IODataQueryOptions queryOptions = createQueryOptionFunction(queryContext); // apply the query IEnumerable enumerable = responseValue as IEnumerable; if (enumerable == null || responseValue is string || responseValue is byte[]) { // response is not a collection; we only support $select and $expand on single entities. ValidateSelectExpandOnly(queryOptions); if (singleResultCollection == null) { // response is a single entity. return(ApplyQuery(entity: responseValue, queryOptions: queryOptions)); } else { IQueryable queryable = singleResultCollection as IQueryable; queryable = ApplyQuery(queryable, queryOptions); return(SingleOrDefault(queryable, actionDescriptor)); } } else { // response is a collection. IQueryable queryable = (enumerable as IQueryable) ?? enumerable.AsQueryable(); queryable = ApplyQuery(queryable, queryOptions); if (request.IsCountRequest()) { long?count = request.Context.TotalCount; if (count.HasValue) { // Return the count value if it is a $count request. return(count.Value); } } return(queryable); } }
internal static object ReadFromStream( Type type, object defaultValue, IEdmModel model, Uri baseAddress, IWebApiRequestMessage internalRequest, Func <IODataRequestMessage> getODataRequestMessage, Func <IEdmTypeReference, ODataDeserializer> getEdmTypeDeserializer, Func <Type, ODataDeserializer> getODataPayloadDeserializer, Func <ODataDeserializerContext> getODataDeserializerContext, Action <IDisposable> registerForDisposeAction, Action <Exception> logErrorAction) { object result; IEdmTypeReference expectedPayloadType; ODataDeserializer deserializer = GetDeserializer(type, internalRequest.Context.Path, model, getEdmTypeDeserializer, getODataPayloadDeserializer, out expectedPayloadType); if (deserializer == null) { throw Error.Argument("type", SRResources.FormatterReadIsNotSupportedForType, type.FullName, typeof(ODataInputFormatterHelper).FullName); } try { ODataMessageReaderSettings oDataReaderSettings = internalRequest.ReaderSettings; oDataReaderSettings.BaseUri = baseAddress; oDataReaderSettings.Validations = oDataReaderSettings.Validations & ~ValidationKinds.ThrowOnUndeclaredPropertyForNonOpenType; IODataRequestMessage oDataRequestMessage = getODataRequestMessage(); ODataMessageReader oDataMessageReader = new ODataMessageReader(oDataRequestMessage, oDataReaderSettings, model); registerForDisposeAction(oDataMessageReader); ODataPath path = internalRequest.Context.Path; ODataDeserializerContext readContext = getODataDeserializerContext(); readContext.Path = path; readContext.Model = model; readContext.ResourceType = type; readContext.ResourceEdmType = expectedPayloadType; result = deserializer.Read(oDataMessageReader, type, readContext); } catch (Exception e) { logErrorAction(e); result = defaultValue; } return(result); }
private static object CovertResourceId(object source, ODataResource resource, IEdmEntityTypeReference entityTypeReference, ODataDeserializerContext readContext) { Contract.Assert(resource != null); Contract.Assert(source != null); if (resource.Id == null || resource.Properties.Any()) { return(source); } IWebApiRequestMessage request = readContext.InternalRequest; IWebApiUrlHelper urlHelper = readContext.InternalUrlHelper; DefaultODataPathHandler pathHandler = new DefaultODataPathHandler(); string serviceRoot = urlHelper.CreateODataLink( request.Context.RouteName, request.PathHandler, new List <ODataPathSegment>()); IEnumerable <KeyValuePair <string, object> > keyValues = GetKeys(pathHandler, serviceRoot, resource.Id, request.RequestContainer); IList <IEdmStructuralProperty> keys = entityTypeReference.Key().ToList(); if (keys.Count == 1 && keyValues.Count() == 1) { // TODO: make sure the enum key works object propertyValue = keyValues.First().Value; DeserializationHelpers.SetDeclaredProperty(source, EdmTypeKind.Primitive, keys[0].Name, propertyValue, keys[0], readContext); return(source); } IDictionary <string, object> keyValuesDic = keyValues.ToDictionary(e => e.Key, e => e.Value); foreach (IEdmStructuralProperty key in keys) { object value; if (keyValuesDic.TryGetValue(key.Name, out value)) { // TODO: make sure the enum key works DeserializationHelpers.SetDeclaredProperty(source, EdmTypeKind.Primitive, key.Name, value, key, readContext); } } return(source); }
/// <summary> /// Returns the request method and in the case of Options request it returns the Access-Control-Request-Method present in the /// preflight request for the request method that will be used for the actual request /// </summary> /// <param name="request"></param> /// <returns></returns> internal static ODataRequestMethod GetRequestMethodOrPreflightMethod(this IWebApiRequestMessage request) { if (request.Method != ODataRequestMethod.Options) { return(request.Method); } IEnumerable <string> values; if (!request.Headers.TryGetValues("Access-Control-Request-Method", out values)) { return(ODataRequestMethod.Unknown); } ODataRequestMethod preflightMethod; return(Enum.TryParse <ODataRequestMethod>(values.FirstOrDefault(), true, out preflightMethod) ? preflightMethod : ODataRequestMethod.Unknown); }
private static ODataSerializer GetSerializer(Type type, object value, IWebApiRequestMessage internalRequest, Func <IEdmTypeReference, ODataSerializer> getEdmTypeSerializer, Func <Type, ODataSerializer> getODataPayloadSerializer) { ODataSerializer serializer; IEdmObject edmObject = value as IEdmObject; if (edmObject != null) { IEdmTypeReference edmType = edmObject.GetEdmType(); if (edmType == null) { throw new SerializationException(Error.Format(SRResources.EdmTypeCannotBeNull, edmObject.GetType().FullName, typeof(IEdmObject).Name)); } serializer = getEdmTypeSerializer(edmType); if (serializer == null) { string message = Error.Format(SRResources.TypeCannotBeSerialized, edmType.ToTraceString()); throw new SerializationException(message); } } else { var applyClause = internalRequest.Context.ApplyClause; // get the most appropriate serializer given that we support inheritance. if (applyClause == null) { type = value == null ? type : value.GetType(); } serializer = getODataPayloadSerializer(type); if (serializer == null) { string message = Error.Format(SRResources.TypeCannotBeSerialized, type.Name); throw new SerializationException(message); } } return(serializer); }
private static ODataPayloadKind?GetEdmObjectPayloadKind(Type type, IWebApiRequestMessage internalRequest) { if (internalRequest.IsCountRequest()) { return(ODataPayloadKind.Value); } Type elementType; if (TypeHelper.IsCollection(type, out elementType)) { if (typeof(IEdmComplexObject).IsAssignableFrom(elementType) || typeof(IEdmEnumObject).IsAssignableFrom(elementType)) { return(ODataPayloadKind.Collection); } else if (typeof(IEdmEntityObject).IsAssignableFrom(elementType)) { return(ODataPayloadKind.ResourceSet); } else if (typeof(IEdmChangedObject).IsAssignableFrom(elementType)) { return(ODataPayloadKind.Delta); } } else { if (typeof(IEdmComplexObject).IsAssignableFrom(elementType) || typeof(IEdmEnumObject).IsAssignableFrom(elementType)) { return(ODataPayloadKind.Property); } else if (typeof(IEdmEntityObject).IsAssignableFrom(elementType)) { return(ODataPayloadKind.Resource); } } return(null); }
private static object ConvertResourceOrResourceSet(object oDataValue, IEdmTypeReference edmTypeReference, ODataDeserializerContext readContext) { string valueString = oDataValue as string; Contract.Assert(valueString != null); if (edmTypeReference.IsNullable && String.Equals(valueString, "null", StringComparison.Ordinal)) { return(null); } IWebApiRequestMessage request = readContext.InternalRequest; ODataMessageReaderSettings oDataReaderSettings = request.ReaderSettings; using (MemoryStream stream = new MemoryStream(Encoding.UTF8.GetBytes(valueString))) { stream.Seek(0, SeekOrigin.Begin); IODataRequestMessage oDataRequestMessage = new ODataMessageWrapper(stream, null, request.ODataContentIdMapping); using ( ODataMessageReader oDataMessageReader = new ODataMessageReader(oDataRequestMessage, oDataReaderSettings, readContext.Model)) { if (edmTypeReference.IsCollection()) { return(ConvertResourceSet(oDataMessageReader, edmTypeReference, readContext)); } else { return(ConvertResource(oDataMessageReader, edmTypeReference, readContext)); } } } }
internal static void WriteToStream( Type type, object value, IEdmModel model, ODataVersion version, Uri baseAddress, MediaTypeHeaderValue contentType, IWebApiUrlHelper internaUrlHelper, IWebApiRequestMessage internalRequest, IWebApiHeaders internalRequestHeaders, Func <IServiceProvider, ODataMessageWrapper> getODataMessageWrapper, Func <IEdmTypeReference, ODataSerializer> getEdmTypeSerializer, Func <Type, ODataSerializer> getODataPayloadSerializer, Func <ODataSerializerContext> getODataSerializerContext) { if (model == null) { throw Error.InvalidOperation(SRResources.RequestMustHaveModel); } ODataSerializer serializer = GetSerializer(type, value, internalRequest, getEdmTypeSerializer, getODataPayloadSerializer); ODataPath path = internalRequest.Context.Path; IEdmNavigationSource targetNavigationSource = path == null ? null : path.NavigationSource; // serialize a response string preferHeader = RequestPreferenceHelpers.GetRequestPreferHeader(internalRequestHeaders); string annotationFilter = null; if (!String.IsNullOrEmpty(preferHeader)) { ODataMessageWrapper messageWrapper = getODataMessageWrapper(null); messageWrapper.SetHeader(RequestPreferenceHelpers.PreferHeaderName, preferHeader); annotationFilter = messageWrapper.PreferHeader().AnnotationFilter; } IODataResponseMessage responseMessage = getODataMessageWrapper(internalRequest.RequestContainer); if (annotationFilter != null) { responseMessage.PreferenceAppliedHeader().AnnotationFilter = annotationFilter; } ODataMessageWriterSettings writerSettings = internalRequest.WriterSettings; writerSettings.BaseUri = baseAddress; writerSettings.Version = version; writerSettings.Validations = writerSettings.Validations & ~ValidationKinds.ThrowOnUndeclaredPropertyForNonOpenType; string metadataLink = internaUrlHelper.CreateODataLink(MetadataSegment.Instance); if (metadataLink == null) { throw new SerializationException(SRResources.UnableToDetermineMetadataUrl); } //Set this variable if the SelectExpandClause is different from the processed clause on the Query options SelectExpandClause selectExpandDifferentFromQueryOptions = null; if (internalRequest.Context.QueryOptions != null && internalRequest.Context.QueryOptions.SelectExpand != null) { if (internalRequest.Context.QueryOptions.SelectExpand.ProcessedSelectExpandClause != internalRequest.Context.ProcessedSelectExpandClause) { selectExpandDifferentFromQueryOptions = internalRequest.Context.ProcessedSelectExpandClause; } } else if (internalRequest.Context.ProcessedSelectExpandClause != null) { selectExpandDifferentFromQueryOptions = internalRequest.Context.ProcessedSelectExpandClause; } writerSettings.ODataUri = new ODataUri { ServiceRoot = baseAddress, SelectAndExpand = internalRequest.Context.ProcessedSelectExpandClause, Apply = internalRequest.Context.ApplyClause, Path = ConvertPath(path), }; ODataMetadataLevel metadataLevel = ODataMetadataLevel.MinimalMetadata; if (contentType != null) { IEnumerable <KeyValuePair <string, string> > parameters = contentType.Parameters.Select(val => new KeyValuePair <string, string>(val.Name, val.Value)); metadataLevel = ODataMediaTypes.GetMetadataLevel(contentType.MediaType, parameters); } using (ODataMessageWriter messageWriter = new ODataMessageWriter(responseMessage, writerSettings, model)) { ODataSerializerContext writeContext = getODataSerializerContext(); writeContext.NavigationSource = targetNavigationSource; writeContext.Model = model; writeContext.RootElementName = GetRootElementName(path) ?? "root"; writeContext.SkipExpensiveAvailabilityChecks = serializer.ODataPayloadKind == ODataPayloadKind.ResourceSet; writeContext.Path = path; writeContext.MetadataLevel = metadataLevel; writeContext.QueryOptions = internalRequest.Context.QueryOptions; //Set the SelectExpandClause on the context if it was explicitly specified. if (selectExpandDifferentFromQueryOptions != null) { writeContext.SelectExpandClause = selectExpandDifferentFromQueryOptions; } serializer.WriteObject(value, type, messageWriter, writeContext); } }
/// <summary> /// Selects the controller for OData requests. /// </summary> /// <param name="odataPath">The OData path.</param> /// <param name="request">The request.</param> /// <returns> /// <c>null</c> if the request isn't handled by this convention; otherwise, the name of the selected controller /// </returns> internal static SelectControllerResult SelectControllerImpl(ODataPath odataPath, IWebApiRequestMessage request) { if (odataPath == null) { throw Error.ArgumentNull("odataPath"); } if (request == null) { throw Error.ArgumentNull("request"); } if (odataPath.PathTemplate == "~" || odataPath.PathTemplate == "~/$metadata") { return(new SelectControllerResult("Metadata", null)); } return(null); }
/// <inheritdoc /> internal static SelectControllerResult SelectControllerImpl(ODataPath odataPath, IWebApiRequestMessage request, IDictionary <ODataPathTemplate, IWebApiActionDescriptor> attributeMappings) { Dictionary <string, object> values = new Dictionary <string, object>(); foreach (KeyValuePair <ODataPathTemplate, IWebApiActionDescriptor> attributeMapping in attributeMappings) { ODataPathTemplate template = attributeMapping.Key; IWebApiActionDescriptor action = attributeMapping.Value; if (action.IsHttpMethodSupported(request.GetRequestMethodOrPreflightMethod()) && template.TryMatch(odataPath, values)) { values["action"] = action.ActionName; SelectControllerResult result = new SelectControllerResult(action.ControllerName, values); return(result); } } return(null); }
internal static object ReadFromStream( #endif Type type, object defaultValue, IEdmModel model, ODataVersion version, Uri baseAddress, IWebApiRequestMessage internalRequest, Func <IODataRequestMessage> getODataRequestMessage, Func <IEdmTypeReference, ODataDeserializer> getEdmTypeDeserializer, Func <Type, ODataDeserializer> getODataPayloadDeserializer, Func <ODataDeserializerContext> getODataDeserializerContext, Action <IDisposable> registerForDisposeAction, Action <Exception> logErrorAction) { object result; IEdmTypeReference expectedPayloadType; ODataDeserializer deserializer = GetDeserializer(type, internalRequest.Context.Path, model, getEdmTypeDeserializer, getODataPayloadDeserializer, out expectedPayloadType); if (deserializer == null) { throw Error.Argument("type", SRResources.FormatterReadIsNotSupportedForType, type.FullName, typeof(ODataInputFormatterHelper).FullName); } try { ODataMessageReaderSettings oDataReaderSettings = internalRequest.ReaderSettings; oDataReaderSettings.BaseUri = baseAddress; oDataReaderSettings.Validations = oDataReaderSettings.Validations & ~ValidationKinds.ThrowOnUndeclaredPropertyForNonOpenType; oDataReaderSettings.Version = version; IODataRequestMessage oDataRequestMessage = getODataRequestMessage(); string preferHeader = RequestPreferenceHelpers.GetRequestPreferHeader(internalRequest.Headers); string annotationFilter = null; if (!String.IsNullOrEmpty(preferHeader)) { oDataRequestMessage.SetHeader(RequestPreferenceHelpers.PreferHeaderName, preferHeader); annotationFilter = oDataRequestMessage.PreferHeader().AnnotationFilter; } if (annotationFilter != null) { oDataReaderSettings.ShouldIncludeAnnotation = ODataUtils.CreateAnnotationFilter(annotationFilter); } ODataMessageReader oDataMessageReader = new ODataMessageReader(oDataRequestMessage, oDataReaderSettings, model); registerForDisposeAction(oDataMessageReader); ODataPath path = internalRequest.Context.Path; ODataDeserializerContext readContext = getODataDeserializerContext(); readContext.Path = path; readContext.Model = model; readContext.ResourceType = type; readContext.ResourceEdmType = expectedPayloadType; #if NETCORE result = await deserializer.ReadAsync(oDataMessageReader, type, readContext); #else result = deserializer.Read(oDataMessageReader, type, readContext); #endif } catch (Exception e) { logErrorAction(e); result = defaultValue; } return(result); }
/// <summary> /// Performs the query composition after action is executed. It first tries to retrieve the IQueryable from the /// returning response message. It then validates the query from uri based on the validation settings on /// <see cref="EnableQueryAttribute"/>. It finally applies the query appropriately, and reset it back on /// the response message. /// </summary> /// <param name="responseValue">The response content value.</param> /// <param name="singleResultCollection">The content as SingleResult.Queryable.</param> /// <param name="actionDescriptor">The action context, i.e. action and controller name.</param> /// <param name="request">The internal request.</param> /// <param name="modelFunction">A function to get the model.</param> /// <param name="createQueryOptionFunction">A function used to create and validate query options.</param> /// <param name="createResponseAction">An action used to create a response.</param> /// <param name="createErrorAction">A function used to generate error response.</param> private object OnActionExecuted( object responseValue, IQueryable singleResultCollection, IWebApiActionDescriptor actionDescriptor, IWebApiRequestMessage request, Func <Type, IEdmModel> modelFunction, Func <ODataQueryContext, IODataQueryOptions> createQueryOptionFunction, Action <HttpStatusCode> createResponseAction, Action <HttpStatusCode, string, Exception> createErrorAction) { if (!_querySettings.PageSize.HasValue && responseValue != null) { GetModelBoundPageSize(responseValue, singleResultCollection, actionDescriptor, modelFunction, request.Context.Path, createErrorAction); } // Apply the query if there are any query options, if there is a page size set, in the case of // SingleResult or in the case of $count request. bool shouldApplyQuery = responseValue != null && request.RequestUri != null && (!String.IsNullOrWhiteSpace(request.RequestUri.Query) || _querySettings.PageSize.HasValue || _querySettings.ModelBoundPageSize.HasValue || singleResultCollection != null || request.IsCountRequest() || ContainsAutoSelectExpandProperty(responseValue, singleResultCollection, actionDescriptor, modelFunction, request.Context.Path)); object returnValue = null; if (shouldApplyQuery) { try { object queryResult = ExecuteQuery(responseValue, singleResultCollection, actionDescriptor, modelFunction, request, createQueryOptionFunction); if (queryResult == null && (request.Context.Path == null || singleResultCollection != null)) { // This is the case in which a regular OData service uses the EnableQuery attribute. // For OData services ODataNullValueMessageHandler should be plugged in for the service // if this behavior is desired. // For non OData services this behavior is equivalent as the one in the v3 version in order // to reduce the friction when they decide to move to use the v4 EnableQueryAttribute. createResponseAction(HttpStatusCode.NotFound); } returnValue = queryResult; } catch (ArgumentOutOfRangeException e) { createErrorAction( HttpStatusCode.BadRequest, Error.Format(SRResources.QueryParameterNotSupported, e.Message), e); } catch (NotImplementedException e) { createErrorAction( HttpStatusCode.BadRequest, Error.Format(SRResources.UriQueryStringInvalid, e.Message), e); } catch (NotSupportedException e) { createErrorAction( HttpStatusCode.BadRequest, Error.Format(SRResources.UriQueryStringInvalid, e.Message), e); } catch (InvalidOperationException e) { // Will also catch ODataException here because ODataException derives from InvalidOperationException. createErrorAction( HttpStatusCode.BadRequest, Error.Format(SRResources.UriQueryStringInvalid, e.Message), e); } } return(returnValue); }
/// <inheritdoc /> internal static SelectControllerResult SelectControllerImpl(ODataPath odataPath, IWebApiRequestMessage request, IDictionary <ODataPathTemplate, IWebApiActionDescriptor> attributeMappings) { Dictionary <string, object> values = new Dictionary <string, object>(); foreach (KeyValuePair <ODataPathTemplate, IWebApiActionDescriptor> attributeMapping in attributeMappings) { ODataPathTemplate template = attributeMapping.Key; IWebApiActionDescriptor action = attributeMapping.Value; if (action.IsHttpMethodSupported(request.GetRequestMethodOrPreflightMethod()) && template.TryMatch(odataPath, values)) { values["action"] = action.ActionName; SelectControllerResult result = new SelectControllerResult(action.ControllerName, values); return(result); } // It's possible that template.TryMatch inserted values in the values dict even if // it did not match the current path. So let's clear the dict before trying // the next template values.Clear(); } return(null); }