private static ErrorHandler CreateHandler(IDataService service, AstoriaRequestMessage requestMessage, Exception exception, Version defaultResponseVersion) { Debug.Assert(service != null, "service != null"); Debug.Assert(service.Configuration != null, "service.Configuration != null"); string acceptableContentTypes = null; string requestAcceptCharsetHeader = null; Version responseVersion = defaultResponseVersion; if (requestMessage != null) { acceptableContentTypes = requestMessage.GetAcceptableContentTypes(); requestAcceptCharsetHeader = requestMessage.GetRequestAcceptCharsetHeader(); try { Version maxProtocolVersion = service.Configuration.DataServiceBehavior.MaxProtocolVersion.ToVersion(); requestMessage.InitializeRequestVersionHeaders(maxProtocolVersion); responseVersion = VersionUtil.GetResponseVersionForError(requestMessage.GetAcceptableContentTypes(), requestMessage.RequestMaxVersion, maxProtocolVersion); } catch (Exception e) { if (!CommonUtil.IsCatchableExceptionType(e)) { throw; } // Ignore exceptions as we should use the default response version. } } return(new ErrorHandler(exception, service.Configuration.UseVerboseErrors, responseVersion, acceptableContentTypes, requestAcceptCharsetHeader)); }
/// <summary> /// Create a new instance of ODataMessageWriterSettings for batch requests. /// </summary> /// <param name="dataService">Data service instance.</param> /// <returns>An instance of a message writer with the appropriate settings.</returns> internal static MessageWriterBuilder ForBatch(IDataService dataService) { Uri serviceUri = dataService.OperationContext.RequestMessage.AbsoluteServiceUri; Version responseVersion = VersionUtil.GetEffectiveMaxResponseVersion(dataService.OperationContext.RequestMessage.RequestMaxVersion, dataService.Configuration.DataServiceBehavior.MaxProtocolVersion.ToVersion()); MessageWriterBuilder messageWriterBuilder = new MessageWriterBuilder(serviceUri, responseVersion, dataService, dataService.OperationContext.ResponseMessage, null /*model*/); // Astoria does not do content negotiation for the top level batch payload at all in V1/V2 // Hence passing */* as the accept header value by default. string contentType = XmlConstants.MimeAny; if (dataService.OperationContext.RequestMessage != null && string.CompareOrdinal( XmlConstants.ODataVersion4Dot0, dataService.OperationContext.RequestMessage.GetHeader(XmlConstants.HttpODataVersion)) == 0) { // For V4, batch request & response payload can be in Json format string accept = dataService.OperationContext.RequestMessage.GetHeader(XmlConstants.HttpAccept); if (accept != null && accept.StartsWith(XmlConstants.MimeApplicationJson)) { contentType = accept; } } messageWriterBuilder.WriterSettings.SetContentType(contentType, null /*acceptableCharSets*/); return(messageWriterBuilder); }
/// <summary> /// Create a new instance of ODataMessageWriterSettings for batch requests. /// </summary> /// <param name="dataService">Data service instance.</param> /// <returns>An instance of a message writer with the appropriate settings.</returns> internal static MessageWriterBuilder ForBatch(IDataService dataService) { Uri serviceUri = dataService.OperationContext.RequestMessage.AbsoluteServiceUri; Version responseVersion = VersionUtil.GetEffectiveMaxResponseVersion(dataService.OperationContext.RequestMessage.RequestMaxVersion, dataService.Configuration.DataServiceBehavior.MaxProtocolVersion.ToVersion()); MessageWriterBuilder messageWriterBuilder = new MessageWriterBuilder(serviceUri, responseVersion, dataService, dataService.OperationContext.ResponseMessage, null /*model*/); // Astoria does not do content negotiation for the top level batch payload at all in V1/V2 // Hence passing */* as the accept header value. messageWriterBuilder.WriterSettings.SetContentType(XmlConstants.MimeAny, null /*acceptableCharSets*/); return(messageWriterBuilder); }
/// <summary> /// Tries to create a type name segment if the given identifier refers to a known type. /// </summary> /// <param name="previous">previous segment info.</param> /// <param name="segment">The segment being interpreted.</param> /// <param name="typeNameSegment">The type name segment, if one was created.</param> /// <returns>Whether or not a type segment was created for the identifier.</returns> private bool TryCreateTypeNameSegment(SegmentInfo previous, ODataPathSegment segment, out SegmentInfo typeNameSegment) { var typeSegment = segment as TypeSegment; if (typeSegment == null || previous.TargetResourceSet == null) { typeNameSegment = null; return(false); } ResourceType targetResourceType = MetadataProviderUtils.GetResourceType(typeSegment); // if the new type segment prevents any results from possibly being returned, then short-circuit and throw a 404. ResourceType previousResourceType = previous.TargetResourceType; Debug.Assert(previousResourceType != null, "previous.TargetResourceType != null"); if (!targetResourceType.IsAssignableFrom(previousResourceType) && !previousResourceType.IsAssignableFrom(targetResourceType)) { throw DataServiceException.CreateBadRequestError(Strings.RequestUriProcessor_InvalidTypeIdentifier_UnrelatedType(targetResourceType.FullName, previousResourceType.FullName)); } // Since we allow derived navigation properties or named streams in V1/V2, the server will generate edit links and navigation links with type segment in it. // Hence we need to be able to process type segment in the request even when the server MPV is set to V1/V2. But we do not want to expose new functionality // like filtering collections based on type, etc on V1/V2 servers. Hence only checking for MPV to be v3 or greater if the previous segment is a collection if (!previous.SingleResult) { VersionUtil.CheckMaxProtocolVersion(VersionUtil.Version4Dot0, this.maxProtocolVersion); } typeNameSegment = new SegmentInfo { Identifier = targetResourceType.FullName, Operation = previous.Operation, TargetKind = previous.TargetKind, TargetSource = previous.TargetSource, TargetResourceType = targetResourceType, SingleResult = previous.SingleResult, TargetResourceSet = previous.TargetResourceSet, ProjectedProperty = previous.ProjectedProperty, Key = previous.Key, RequestExpression = previous.RequestExpression, RequestEnumerable = previous.RequestEnumerable, IsTypeIdentifierSegment = true }; return(true); }
/// <summary> /// update the request version header, if it is not specified. /// </summary> /// <param name="maxProtocolVersion">protocol version as specified in the config.</param> internal void InitializeRequestVersionHeaders(Version maxProtocolVersion) { if (this.requestVersionHeadersInitialized) { return; } this.requestVersionHeadersInitialized = true; Debug.Assert(this.requestVersion == null, "this.requestVersion == null"); Debug.Assert(this.RequestMaxVersion == null, "this.RequestMaxVersion == null"); Version maxRequestVersionAllowed = GetMaxRequestVersionAllowed(maxProtocolVersion); // read the request version headers from the underlying host this.requestVersionString = this.host.RequestVersion; this.RequestVersion = ValidateVersionHeader(XmlConstants.HttpODataVersion, this.requestVersionString); this.RequestMaxVersion = ValidateVersionHeader(XmlConstants.HttpODataMaxVersion, this.host.RequestMaxVersion); // If the request version is not specified. if (this.requestVersion == null) { // In request headers, if the OData-Version header is not specified and OData-MaxVersion is specified, // we should ideally set the OData-Version header to whatever the value was specified in OData-MaxVersion // header has. if (this.RequestMaxVersion != null) { // set the OData-Version to minimum of OData-MaxVersion and ProtocolVersion. this.RequestVersion = (this.RequestMaxVersion < maxProtocolVersion) ? this.RequestMaxVersion : maxProtocolVersion; } else { // If both request DSV and request MaxDSV is not specified, then set the request DSV to the MPV this.RequestVersion = maxProtocolVersion; } this.requestVersionString = this.RequestVersion.ToString(2); } else { if (this.RequestVersion > maxRequestVersionAllowed) { throw DataServiceException.CreateBadRequestError(Strings.DataService_RequestVersionMustBeLessThanMPV(this.RequestVersion, maxProtocolVersion)); } // Verify that the request DSV is a known version number. if (!VersionUtil.IsKnownRequestVersion(this.RequestVersion)) { string message = Strings.DataService_InvalidRequestVersion( this.RequestVersion.ToString(2), KnownODataVersionsToString(maxRequestVersionAllowed)); throw DataServiceException.CreateBadRequestError(message); } } // Initialize the request OData-MaxVersion if not specified. if (this.RequestMaxVersion == null) { this.RequestMaxVersion = maxProtocolVersion; } else if (this.RequestMaxVersion < VersionUtil.DataServiceDefaultResponseVersion) { // We need to make sure the MaxDSV is at least 1.0. This was the V1 behavior. // Verified that this was checked both in batch and non-batch cases. string message = Strings.DataService_MaxDSVTooLow( this.RequestMaxVersion.ToString(2), VersionUtil.DataServiceDefaultResponseVersion.Major, VersionUtil.DataServiceDefaultResponseVersion.Minor); throw DataServiceException.CreateBadRequestError(message); } }
/// <summary>Initializes a new <see cref="ResponseBodyWriter"/> that can write the body of a response.</summary> /// <param name="service">Service for the request being processed.</param> /// <param name="queryResults">Enumerator for results.</param> /// <param name="requestDescription">Description of request made to the system.</param> /// <param name="actualResponseMessageWhoseHeadersMayBeOverridden">IODataResponseMessage instance for the response.</param> internal ResponseBodyWriter( IDataService service, QueryResultInfo queryResults, RequestDescription requestDescription, IODataResponseMessage actualResponseMessageWhoseHeadersMayBeOverridden) { Debug.Assert(service != null, "service != null"); Debug.Assert(requestDescription != null, "requestDescription != null"); Debug.Assert(actualResponseMessageWhoseHeadersMayBeOverridden != null, "actualResponseMessageWhoseHeadersMayBeOverridden != null"); this.service = service; this.queryResults = queryResults; this.requestDescription = requestDescription; this.actualResponseMessageWhoseHeadersMayBeOverridden = actualResponseMessageWhoseHeadersMayBeOverridden; Debug.Assert(this.PayloadKind != ODataPayloadKind.Unsupported, "payloadKind != ODataPayloadKind.Unsupported"); this.encoding = ContentTypeUtil.EncodingFromAcceptCharset(this.service.OperationContext.RequestMessage.GetRequestAcceptCharsetHeader()); if (this.PayloadKind == ODataPayloadKind.Entry || this.PayloadKind == ODataPayloadKind.Feed || this.PayloadKind == ODataPayloadKind.Property || this.PayloadKind == ODataPayloadKind.Collection || this.PayloadKind == ODataPayloadKind.EntityReferenceLink || this.PayloadKind == ODataPayloadKind.EntityReferenceLinks || this.PayloadKind == ODataPayloadKind.Error || this.PayloadKind == ODataPayloadKind.ServiceDocument || this.PayloadKind == ODataPayloadKind.Parameter) { AstoriaRequestMessage requestMessage = service.OperationContext.RequestMessage; IODataResponseMessage responseMessageOnOperationContext = service.OperationContext.ResponseMessage; Version effectiveMaxResponseVersion = VersionUtil.GetEffectiveMaxResponseVersion(service.Configuration.DataServiceBehavior.MaxProtocolVersion.ToVersion(), requestMessage.RequestMaxVersion); bool isEntityOrFeed = this.PayloadKind == ODataPayloadKind.Entry || this.PayloadKind == ODataPayloadKind.Feed; if (ContentTypeUtil.IsResponseMediaTypeJsonLight(requestMessage.GetAcceptableContentTypes(), isEntityOrFeed, effectiveMaxResponseVersion)) { // If JSON light 'wins', then bump the version to V3. requestDescription.VerifyAndRaiseResponseVersion(VersionUtil.Version4Dot0, service); responseMessageOnOperationContext.SetHeader(XmlConstants.HttpODataVersion, XmlConstants.ODataVersion4Dot0 + ";"); } } if (this.requestDescription.TargetKind == RequestTargetKind.MediaResource) { Debug.Assert(this.PayloadKind == ODataPayloadKind.BinaryValue, "payloadKind == ODataPayloadKind.BinaryValue"); // Note that GetReadStream will set the ResponseETag before it returns this.mediaResourceStream = service.StreamProvider.GetReadStream( this.queryResults.Current, this.requestDescription.StreamProperty, this.service.OperationContext); } else if (this.PayloadKind != ODataPayloadKind.BinaryValue) { IEdmModel model; if (this.PayloadKind == ODataPayloadKind.MetadataDocument) { model = MetadataSerializer.PrepareModelForSerialization(this.service.Provider, this.service.Configuration); } else { model = this.GetModelFromService(); } // Create the message writer using which the response needs to be written. this.messageWriterBuilder = MessageWriterBuilder.ForNormalRequest( this.service, this.requestDescription, this.actualResponseMessageWhoseHeadersMayBeOverridden, model); this.messageWriter = this.messageWriterBuilder.CreateWriter(); try { // Make sure all the headers are written before the method returns. this.contentFormat = ODataUtils.SetHeadersForPayload(this.messageWriter, this.PayloadKind); } catch (ODataContentTypeException contentTypeException) { throw new DataServiceException(415, null, Strings.DataServiceException_UnsupportedMediaType, null, contentTypeException); } Debug.Assert(requestDescription.ResponseFormat != null, "Response format should already have been determined."); Debug.Assert(ReferenceEquals(this.contentFormat, requestDescription.ResponseFormat.Format), "Response format in request description did not match format when writing."); if (this.PayloadKind == ODataPayloadKind.Value && !String.IsNullOrEmpty(this.requestDescription.MimeType)) { this.actualResponseMessageWhoseHeadersMayBeOverridden.SetHeader(XmlConstants.HttpContentType, this.requestDescription.MimeType); } // EPM is currently removed, but this doesn't seem to be used for EPM only. The old comment was saying: // In astoria, there is a bug in V1/V2 that while computing response version, we did not take // epm into account. Hence while creating the writer, we need to pass the RequestDescription.ActualResponseVersion // so that ODataLib can do the correct payload validation. But we need to write the response version without // the epm into the response headers because of backward-compat issue. Hence over-writing the response version // header with the wrong version value. string responseVersion = this.requestDescription.ResponseVersion.ToString() + ";"; this.actualResponseMessageWhoseHeadersMayBeOverridden.SetHeader(XmlConstants.HttpODataVersion, responseVersion); } }