/// <summary> /// Process the incoming request and forms a formatted outgoing response. /// </summary> /// <param name="incomingRequest">Incoming request</param> /// <returns>Message instance containing the outgoing response</returns> public Message ProcessRequest(Request incomingRequest) { _baseUri = _serviceHost.ServiceBaseUri; _responseSerializationFormat = incomingRequest.ResponseSerializationFormat; // Check and fire request interceptor. if (_configuration.HasRequestInterceptors(this._scopeName, SyncOperations.Download)) { // Init the SyncOperationContext this.InitRequestOperationContext(); // Fire the request Interceptors if any base.ProcessRequestInterceptors(); } IAsymmetricProviderService providerService = new SqlSyncProviderService(_configuration, Convert.ToString(incomingRequest.CommandParams[CommandParamType.ScopeName]), incomingRequest.ProviderParams, base._operationContext); _getChangesResponse = providerService.GetChanges(incomingRequest.SyncBlob); // Check and fire response interceptor. this.PrepareAndProcessResponseInterceptors(); var oDataWriter = GetSyncWriterWithContents(); return(base.CreateResponseMessage(incomingRequest.ResponseSerializationFormat, oDataWriter)); }
protected Message CreateResponseMessage(SyncSerializationFormat serializationFormat, SyncWriter oDataWriter) { var bodyWriter = new DelegateBodyWriter(WriteResponse, oDataWriter); Message message = Message.CreateMessage(MessageVersion.None, String.Empty, bodyWriter); switch (serializationFormat) { case SyncSerializationFormat.ODataAtom: message.Properties.Add(WebBodyFormatMessageProperty.Name, new WebBodyFormatMessageProperty(WebContentFormat.Xml)); break; case SyncSerializationFormat.ODataJson: message.Properties.Add(WebBodyFormatMessageProperty.Name, new WebBodyFormatMessageProperty(WebContentFormat.Json)); break; } var property = new HttpResponseMessageProperty { StatusCode = HttpStatusCode.OK }; property.Headers[HttpResponseHeader.ContentType] = WebUtil.GetContentType(serializationFormat); // Copy the SyncOperationContext's ResponseHeaders if present if (this._operationContext != null) { property.Headers.Add(this._operationContext.ResponseHeaders); } message.Properties.Add(HttpResponseMessageProperty.Name, property); return(message); }
protected Message CreateResponseMessage(SyncSerializationFormat serializationFormat, SyncWriter oDataWriter) { var bodyWriter = new DelegateBodyWriter(WriteResponse, oDataWriter); Message message = Message.CreateMessage(MessageVersion.None, String.Empty, bodyWriter); switch (serializationFormat) { case SyncSerializationFormat.ODataAtom: message.Properties.Add(WebBodyFormatMessageProperty.Name, new WebBodyFormatMessageProperty(WebContentFormat.Xml)); break; case SyncSerializationFormat.ODataJson: message.Properties.Add(WebBodyFormatMessageProperty.Name, new WebBodyFormatMessageProperty(WebContentFormat.Json)); break; } var property = new HttpResponseMessageProperty { StatusCode = HttpStatusCode.OK }; property.Headers[HttpResponseHeader.ContentType] = WebUtil.GetContentType(serializationFormat); // Copy the SyncOperationContext's ResponseHeaders if present if (this._operationContext != null) { property.Headers.Add(this._operationContext.ResponseHeaders); } message.Properties.Add(HttpResponseMessageProperty.Name, property); return message; }
internal static string GetContentType(SyncSerializationFormat format) { switch (format) { case SyncSerializationFormat.ODataAtom: return("application/atom+xml"); case SyncSerializationFormat.ODataJson: return("application/json"); default: throw SyncServiceException.CreateBadRequestError("Unsupported serialization format"); } }
internal static SyncReader GetSyncReader(SyncSerializationFormat serializationFormat, Stream stream, Type[] knownTypes) { switch (serializationFormat) { case SyncSerializationFormat.ODataAtom: return(new ODataAtomReader(stream, knownTypes)); case SyncSerializationFormat.ODataJson: return(new ODataJsonReader(stream, knownTypes)); default: throw new NotImplementedException(); } }
internal static SyncWriter GetSyncWriter(SyncSerializationFormat serializationFormat, Uri baseUri) { switch (serializationFormat) { case SyncSerializationFormat.ODataAtom: return(new ODataAtomWriter(baseUri)); case SyncSerializationFormat.ODataJson: return(new ODataJsonWriter(baseUri)); default: throw new NotImplementedException(); } }
internal Request ParseIncomingRequest() { // Steps: // 1. Parse and validate request URI format (/syncscope/syncoperation) // 2. Validate QueryString using the HttpContextServiceHost.VerifyQueryParameters method. // 3. Parse and save query string parameters // 4. Identify and save request type, scopename, syncblob, request body. // throw BadRequest if duplicates are found. /* * string[] RelativeUriSegments = new string[_serviceHost.RelativeUriSegments.Length - 1]; * for (int i = 1; i < _serviceHost.RelativeUriSegments.Length; i++) * RelativeUriSegments[i - 1] = _serviceHost.RelativeUriSegments[i]; */ string[] RelativeUriSegments = _serviceHost.RelativeUriSegments; _serviceHost.VerifyQueryParameters(); SyncSerializationFormat outputSerializationFormat = _serviceHost.GetOutputSerializationFormat(_configuration.SerializationFormat); SyncTracer.Verbose("Output Serialization format: {0}", outputSerializationFormat); RequestCommand requestCommand = GetRequestCommandType(RelativeUriSegments); SyncTracer.Verbose("RequestCommand type: {0}", requestCommand); List <IOfflineEntity> entities = null; Dictionary <CommandParamType, object> commandParameters = null; // Get command paramaters (filter params, scope name etc.) for all request types except $syncScopes if (requestCommand != RequestCommand.SyncScopes) { commandParameters = GetCommandParameters(_serviceHost.QueryStringCollection, RelativeUriSegments); } // Read the payload, headers etc for upload and download request types. if (requestCommand == RequestCommand.DownloadChanges || requestCommand == RequestCommand.UploadChanges) { ReadIncomingRequestDetails(); entities = GetEntityListFromRequest(requestCommand); } var request = new Request(requestCommand, _serviceHost, commandParameters, _syncBlob, entities, outputSerializationFormat) { IdToTempIdMapping = _idToTempIdMapping }; return(request); }
/// <summary> /// Process the incoming request and forms a formatted outgoing response. /// </summary> /// <param name="incomingRequest">Incoming request</param> /// <returns>Message instance containing the outgoing response</returns> public Message ProcessRequest(Request incomingRequest) { _baseUri = _serviceHost.ServiceBaseUri; _responseSerializationFormat = incomingRequest.ResponseSerializationFormat; _incomingEntities = incomingRequest.EntityList; _idToTempIdMapping = incomingRequest.IdToTempIdMapping; // Check and invoke request interceptor this.PrepareAndProcessRequestInterceptor(); IAsymmetricProviderService providerService = new SqlSyncProviderService(_configuration, Convert.ToString(incomingRequest.CommandParams[CommandParamType.ScopeName]), incomingRequest.ProviderParams, base._operationContext); // Set a callback for the ApplyClientChangeFailed delegate. ((SqlSyncProviderService)providerService).ApplyClientChangeFailed = ClientChangeFailedToApply; // Loop over client input and pull all inserts to a different collection. _incomingNewInsertEntities = _incomingEntities.Where(e => string.IsNullOrEmpty(e.ServiceMetadata.Id)).ToList(); _applyChangesResponse = providerService.ApplyChanges(incomingRequest.SyncBlob, incomingRequest.EntityList); if (_applyChangesResponse.Errors.Count > 0) { throw new Exception(_applyChangesResponse.Errors[0].Description); } // Give the inserts permanent Ids AssignRealIdsForClientInserts(_incomingNewInsertEntities); // Process the rejected entities if any this.ProcessRejectedEntities((SqlSyncProviderService)providerService); // Check and fire response interceptor this.PrepareAndProcessResponseInterceptor(providerService); var oDataWriter = GetSyncWriterWithContents(); return(base.CreateResponseMessage(incomingRequest.ResponseSerializationFormat, oDataWriter)); }
internal Request(RequestCommand requestCommand, HttpContextServiceHost serviceHost, Dictionary<CommandParamType, object> commandParams, byte[] blob, List<IOfflineEntity> entities, SyncSerializationFormat responseSerializationFormat) { IdToTempIdMapping = new Dictionary<string, string>(); RequestCommand = requestCommand; ServiceHost = serviceHost; CommandParams = commandParams; SyncBlob = blob; ResponseSerializationFormat = responseSerializationFormat; if (null != entities && requestCommand != RequestCommand.UploadChanges) { throw SyncServiceException.CreateBadRequestError(Strings.EntitiesOnlyAllowedForUploadChangesRequest); } EntityList = entities; }
internal Request(RequestCommand requestCommand, HttpContextServiceHost serviceHost, Dictionary <CommandParamType, object> commandParams, byte[] blob, List <IOfflineEntity> entities, SyncSerializationFormat responseSerializationFormat) { IdToTempIdMapping = new Dictionary <string, string>(); RequestCommand = requestCommand; ServiceHost = serviceHost; CommandParams = commandParams; SyncBlob = blob; ResponseSerializationFormat = responseSerializationFormat; if (null != entities && requestCommand != RequestCommand.UploadChanges) { throw SyncServiceException.CreateBadRequestError(Strings.EntitiesOnlyAllowedForUploadChangesRequest); } EntityList = entities; }
/// <summary> /// Change the default serialization format. The default value is ODataAtom. /// </summary> /// <param name="serializationFormat">serialization format</param> public void SetDefaultSyncSerializationFormat(SyncSerializationFormat serializationFormat) { SerializationFormat = serializationFormat; }
/// <summary> /// Process the incoming request and forms a formatted outgoing response. /// </summary> /// <param name="incomingRequest">Incoming request</param> /// <returns>Message instance containing the outgoing response</returns> public Message ProcessRequest(Request incomingRequest) { _baseUri = _serviceHost.ServiceBaseUri; _responseSerializationFormat = incomingRequest.ResponseSerializationFormat; _incomingEntities = incomingRequest.EntityList; _idToTempIdMapping = incomingRequest.IdToTempIdMapping; // Check and invoke request interceptor this.PrepareAndProcessRequestInterceptor(); IAsymmetricProviderService providerService = new SqlSyncProviderService(_configuration, Convert.ToString(incomingRequest.CommandParams[CommandParamType.ScopeName]), incomingRequest.ProviderParams, base._operationContext); // Set a callback for the ApplyClientChangeFailed delegate. ((SqlSyncProviderService)providerService).ApplyClientChangeFailed = ClientChangeFailedToApply; // Loop over client input and pull all inserts to a different collection. _incomingNewInsertEntities = _incomingEntities.Where(e => string.IsNullOrEmpty(e.ServiceMetadata.Id)).ToList(); _applyChangesResponse = providerService.ApplyChanges(incomingRequest.SyncBlob, incomingRequest.EntityList); // Give the inserts permanent Ids AssignRealIdsForClientInserts(_incomingNewInsertEntities); // Process the rejected entities if any this.ProcessRejectedEntities((SqlSyncProviderService)providerService); // Check and fire response interceptor this.PrepareAndProcessResponseInterceptor(providerService); var oDataWriter = GetSyncWriterWithContents(); return base.CreateResponseMessage(incomingRequest.ResponseSerializationFormat, oDataWriter); }
/// <summary> /// Process the incoming request and forms a formatted outgoing response. /// </summary> /// <param name="incomingRequest">Incoming request</param> /// <returns>Message instance containing the outgoing response</returns> public Message ProcessRequest(Request incomingRequest) { _baseUri = _serviceHost.ServiceBaseUri; _responseSerializationFormat = incomingRequest.ResponseSerializationFormat; // Check and fire request interceptor. if (_configuration.HasRequestInterceptors(this._scopeName, SyncOperations.Download)) { // Init the SyncOperationContext this.InitRequestOperationContext(); // Fire the request Interceptors if any base.ProcessRequestInterceptors(); } SqlSyncProviderService providerService = new SqlSyncProviderService(_configuration, Convert.ToString(incomingRequest.CommandParams[CommandParamType.ScopeName]), incomingRequest.ProviderParams, base._operationContext); _getChangesResponse = providerService.GetChanges(incomingRequest.SyncBlob); // Check and fire response interceptor. this.PrepareAndProcessResponseInterceptors(); var oDataWriter = GetSyncWriterWithContents(); return base.CreateResponseMessage(incomingRequest.ResponseSerializationFormat, oDataWriter); }
/// <summary> /// Get the serialization format for the response based on the value of the HTTP Accept header. /// /// If $format is not specified then the format comes from the accept header. /// /// The order in which a response content-type is chosen is based on the /// incoming "Accept" header and the types that the service supports. /// According to the HTTP/1.1 Header Field Definitions RFC /// (http:///www.w3.org/Protocols/rfc2616/rfc2616-sec14.html), an absence of the /// Accept header means that the client accepts all response types. /// /// Media ranges can be overridden by more specific media ranges, for example: /// both application/json and application/atom+xml would override */*. /// /// Depending on the service configuration application/atom+xml would override application/json /// if application/atom+xml if the default serialization format, and application/json would /// override application/atom+xml if the default serialization format is application/json. /// /// A client can also send a media range of the following type: application/*, which can be /// substituted for application/atom+xml or application/json depending on the service configuration. /// /// A. If the default configured serialization format is "application/atom+xml" /// /// The formats in order of priority are: /// 1. application/atom+xml /// 2. application/json /// 3. application/* or */* substituted with application/atom+xml /// /// Examples (order of accept headers doesn't matter): /// "application/*" -> ATOM+XML /// "application/*,application/JSON" -> JSON /// "application/*,application/ATOM+XML" -> ATOM+XML /// "application/*,application/ATOM+XML,application/JSON" -> ATOM+XML /// "application/JSON" -> JSON /// "application/ATOM+XML" -> ATOM+XML /// "application/JSON,application/ATOM+XML" -> ATOM+XML /// /// B. If the default configured serialization format is "application/json" /// /// The formats in order of priority are: /// 1. application/json /// 2. application/atom+xml /// 3. application/* or */* substituted with application/json /// /// Examples (order of accept headers doesn't matter): /// "application/*" -> JSON /// "application/*,application/JSON" -> JSON /// "application/*,application/ATOM+XML" -> ATOM+XML /// "application/*,application/ATOM+XML,application/JSON" -> JSON /// "application/JSON" -> JSON /// "application/ATOM+XML" -> ATOM+XML /// "application/JSON,application/ATOM+XML" -> JSON /// /// Note: headers from firefox need to be trimmed before we make a comparison.In other words the media range /// parameter as specified in the above RFC are ignored. /// </summary> /// <returns>Response serialization format</returns> internal SyncSerializationFormat GetOutputSerializationFormat(SyncSerializationFormat defaultSerializationFormat) { // Read $format from querystring first string formatQueryString = QueryStringCollection[SYNC_FORMAT_QUERYKEY]; if (!String.IsNullOrEmpty(formatQueryString)) { if (0 == String.Compare(formatQueryString.ToLowerInvariant(), "atom", StringComparison.InvariantCultureIgnoreCase)) { return SyncSerializationFormat.ODataAtom; } if (0 == String.Compare(formatQueryString.ToLowerInvariant(), "bitmobile", StringComparison.InvariantCultureIgnoreCase)) { return SyncSerializationFormat.ODataBM; } if (0 == String.Compare(formatQueryString.ToLowerInvariant(), "json", StringComparison.InvariantCultureIgnoreCase)) { return SyncSerializationFormat.ODataJson; } } else if (!String.IsNullOrEmpty(RequestAccept)) { var header = RequestAccept.ToLowerInvariant().Split(new[] {','}, StringSplitOptions.RemoveEmptyEntries).Select(h => h.Trim()); SyncSerializationFormat? outputSerializationFormat = null; foreach (string headerString in header) { // Media range followed by optional semi-column and accept-params , string[] headerStringParts = headerString.Split(new[] {';'}, StringSplitOptions.RemoveEmptyEntries); if (0 == headerStringParts.Length) { continue; } // Is this header application/atom+xml if (headerStringParts[0].Equals(CONTENT_TYPE_APPLICATION_ATOM, StringComparison.OrdinalIgnoreCase)) { outputSerializationFormat = SyncSerializationFormat.ODataAtom; } // Is this header application/atom+xml if (headerStringParts[0].Equals(CONTENT_TYPE_APPLICATION_BM, StringComparison.OrdinalIgnoreCase)) { outputSerializationFormat = SyncSerializationFormat.ODataBM; } // Is this header application/json if (headerStringParts[0].Equals(CONTENT_TYPE_APPLICATION_JSON, StringComparison.OrdinalIgnoreCase)) { outputSerializationFormat = SyncSerializationFormat.ODataJson; } // If the default header has been set explicitly then no need to read other headers if (outputSerializationFormat == defaultSerializationFormat) { break; } // Is this header application/* or */* if ((null == outputSerializationFormat) && (headerStringParts[0].Equals(CONTENT_TYPE_APPLICATION_ANY) || headerStringParts[0].Equals(CONTENT_TYPE_ANY))) { // Do not exit the loop as this can be overwritten by an explicit json or atnm+xml header outputSerializationFormat = defaultSerializationFormat; } } if (null == outputSerializationFormat) { throw SyncServiceException.CreateNotAcceptable(Strings.UnsupportedAcceptHeaderValue); } return outputSerializationFormat.Value; } // return the default serialization format. return defaultSerializationFormat; }
/// <summary> /// Get the serialization format for the response based on the value of the HTTP Accept header. /// /// If $format is not specified then the format comes from the accept header. /// /// The order in which a response content-type is chosen is based on the /// incoming "Accept" header and the types that the service supports. /// According to the HTTP/1.1 Header Field Definitions RFC /// (http:///www.w3.org/Protocols/rfc2616/rfc2616-sec14.html), an absence of the /// Accept header means that the client accepts all response types. /// /// Media ranges can be overridden by more specific media ranges, for example: /// both application/json and application/atom+xml would override */*. /// /// Depending on the service configuration application/atom+xml would override application/json /// if application/atom+xml if the default serialization format, and application/json would /// override application/atom+xml if the default serialization format is application/json. /// /// A client can also send a media range of the following type: application/*, which can be /// substituted for application/atom+xml or application/json depending on the service configuration. /// /// A. If the default configured serialization format is "application/atom+xml" /// /// The formats in order of priority are: /// 1. application/atom+xml /// 2. application/json /// 3. application/* or */* substituted with application/atom+xml /// /// Examples (order of accept headers doesn't matter): /// "application/*" -> ATOM+XML /// "application/*,application/JSON" -> JSON /// "application/*,application/ATOM+XML" -> ATOM+XML /// "application/*,application/ATOM+XML,application/JSON" -> ATOM+XML /// "application/JSON" -> JSON /// "application/ATOM+XML" -> ATOM+XML /// "application/JSON,application/ATOM+XML" -> ATOM+XML /// /// B. If the default configured serialization format is "application/json" /// /// The formats in order of priority are: /// 1. application/json /// 2. application/atom+xml /// 3. application/* or */* substituted with application/json /// /// Examples (order of accept headers doesn't matter): /// "application/*" -> JSON /// "application/*,application/JSON" -> JSON /// "application/*,application/ATOM+XML" -> ATOM+XML /// "application/*,application/ATOM+XML,application/JSON" -> JSON /// "application/JSON" -> JSON /// "application/ATOM+XML" -> ATOM+XML /// "application/JSON,application/ATOM+XML" -> JSON /// /// Note: headers from firefox need to be trimmed before we make a comparison.In other words the media range /// parameter as specified in the above RFC are ignored. /// </summary> /// <returns>Response serialization format</returns> internal SyncSerializationFormat GetOutputSerializationFormat(SyncSerializationFormat defaultSerializationFormat) { // Read $format from querystring first string formatQueryString = QueryStringCollection[SYNC_FORMAT_QUERYKEY]; if (!String.IsNullOrEmpty(formatQueryString)) { if (0 == String.Compare(formatQueryString.ToLowerInvariant(), "atom", StringComparison.InvariantCultureIgnoreCase)) { return(SyncSerializationFormat.ODataAtom); } if (0 == String.Compare(formatQueryString.ToLowerInvariant(), "json", StringComparison.InvariantCultureIgnoreCase)) { return(SyncSerializationFormat.ODataJson); } } else if (!String.IsNullOrEmpty(RequestAccept)) { var header = RequestAccept.ToLowerInvariant().Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(h => h.Trim()); SyncSerializationFormat?outputSerializationFormat = null; foreach (string headerString in header) { // Media range followed by optional semi-column and accept-params , string[] headerStringParts = headerString.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries); if (0 == headerStringParts.Length) { continue; } // Is this header application/atom+xml if (headerStringParts[0].Equals(CONTENT_TYPE_APPLICATION_ATOM, StringComparison.OrdinalIgnoreCase)) { outputSerializationFormat = SyncSerializationFormat.ODataAtom; } // Is this header application/json if (headerStringParts[0].Equals(CONTENT_TYPE_APPLICATION_JSON, StringComparison.OrdinalIgnoreCase)) { outputSerializationFormat = SyncSerializationFormat.ODataJson; } // If the default header has been set explicitly then no need to read other headers if (outputSerializationFormat == defaultSerializationFormat) { break; } // Is this header application/* or */* if ((null == outputSerializationFormat) && (headerStringParts[0].Equals(CONTENT_TYPE_APPLICATION_ANY) || headerStringParts[0].Equals(CONTENT_TYPE_ANY))) { // Do not exit the loop as this can be overwritten by an explicit json or atnm+xml header outputSerializationFormat = defaultSerializationFormat; } } if (null == outputSerializationFormat) { throw SyncServiceException.CreateNotAcceptable(Strings.UnsupportedAcceptHeaderValue); } return(outputSerializationFormat.Value); } // return the default serialization format. return(defaultSerializationFormat); }