/** * Write in specific format * * @param IService $service * @param RequestDescription $request the OData request * @param mixed $entityModel OData model instance * @param String $responseContentType Content type of the response * */ public static function write(IService $service, RequestDescription $request, $entityModel, $responseContentType) { $responseBody = null; $dataServiceVersion = $request->getResponseVersion(); $targetKind = $request->getTargetKind(); if ($targetKind == TargetKind::METADATA()) { // /$metadata $writer = new MetadataWriter($service->getProvidersWrapper()); $responseBody = $writer->writeMetadata(); $dataServiceVersion = $writer->getDataServiceVersion(); } else { if ($targetKind == TargetKind::PRIMITIVE_VALUE() && $responseContentType != MimeTypes::MIME_APPLICATION_OCTETSTREAM) { //This second part is to exclude binary properties // /Customer('ALFKI')/CompanyName/$value // /Customers/$count $responseBody = utf8_encode($request->getTargetResult()); } else { if ($responseContentType == MimeTypes::MIME_APPLICATION_OCTETSTREAM || $targetKind == TargetKind::MEDIA_RESOURCE()) { // Binary property or media resource if ($request->getTargetKind() == TargetKind::MEDIA_RESOURCE()) { $result = $request->getTargetResult(); $streamInfo = $request->getResourceStreamInfo(); $provider = $service->getStreamProviderWrapper(); $eTag = $provider->getStreamETag($result, $streamInfo); $service->getHost()->setResponseETag($eTag); $responseBody = $provider->getReadStream($result, $streamInfo); } else { $responseBody = $request->getTargetResult(); } if (is_null($responseContentType)) { $responseContentType = MimeTypes::MIME_APPLICATION_OCTETSTREAM; } } else { $writer = $service->getODataWriterRegistry()->getWriter($request->getResponseVersion(), $responseContentType); //TODO: move ot Messages if (is_null($writer)) { throw new \Exception("no writer can handle the request"); } if (is_null($entityModel)) { //TODO: this seems like a weird way to know that the request is for a service document..i'd think we know this some other way $responseBody = $writer->writeServiceDocument($service->getProvidersWrapper())->getOutput(); } else { $responseBody = $writer->write($entityModel)->getOutput(); } } } } $service->getHost()->setResponseStatusCode(HttpStatus::CODE_OK); $service->getHost()->setResponseContentType($responseContentType); $service->getHost()->setResponseVersion($dataServiceVersion->toString() . ';'); $service->getHost()->setResponseCacheControl(ODataConstants::HTTPRESPONSE_HEADER_CACHECONTROL_NOCACHE); $service->getHost()->getOperationContext()->outgoingResponse()->setStream($responseBody); }
/** * Process the request Uri and creates an instance of * RequestDescription from the processed uri. * * @param IService $service Reference to the data service instance. * * @return RequestDescription * * @throws ODataException If any exception occurs while processing the segments * or in case of any version incompatibility. */ public static function process(IService $service) { $host = $service->getHost(); $absoluteRequestUri = $host->getAbsoluteRequestUri(); $absoluteServiceUri = $host->getAbsoluteServiceUri(); $requestUriSegments = array_slice($absoluteRequestUri->getSegments(), $absoluteServiceUri->getSegmentCount()); $segments = SegmentParser::parseRequestUriSegments($requestUriSegments, $service->getProvidersWrapper(), true); $request = new RequestDescription($segments, $absoluteRequestUri, $service->getConfiguration()->getMaxDataServiceVersion(), $host->getRequestVersion(), $host->getRequestMaxVersion()); $kind = $request->getTargetKind(); if ($kind == TargetKind::METADATA() || $kind == TargetKind::BATCH() || $kind == TargetKind::SERVICE_DIRECTORY()) { return $request; } if ($kind == TargetKind::PRIMITIVE_VALUE() || $kind == TargetKind::MEDIA_RESOURCE()) { // http://odata/NW.svc/Orders/$count // http://odata/NW.svc/Orders(123)/Customer/CustomerID/$value // http://odata/NW.svc/Employees(1)/$value // http://odata/NW.svc/Employees(1)/ThumbNail_48X48/$value $request->setContainerName($segments[count($segments) - 2]->getIdentifier()); } else { $request->setContainerName($request->getIdentifier()); } if ($request->getIdentifier() === ODataConstants::URI_COUNT_SEGMENT) { if (!$service->getConfiguration()->getAcceptCountRequests()) { throw ODataException::createBadRequestError(Messages::configurationCountNotAccepted()); } $request->queryType = QueryType::COUNT(); // use of $count requires request DataServiceVersion // and MaxDataServiceVersion greater than or equal to 2.0 $request->raiseResponseVersion(2, 0); $request->raiseMinVersionRequirement(2, 0); } else { if ($request->isNamedStream()) { $request->raiseMinVersionRequirement(3, 0); } else { if ($request->getTargetKind() == TargetKind::RESOURCE()) { if (!$request->isLinkUri()) { $resourceSetWrapper = $request->getTargetResourceSetWrapper(); //assert($resourceSetWrapper != null) $hasNamedStream = $resourceSetWrapper->hasNamedStreams($service->getProvidersWrapper()); $hasBagProperty = $resourceSetWrapper->hasBagProperty($service->getProvidersWrapper()); if ($hasNamedStream || $hasBagProperty) { $request->raiseResponseVersion(3, 0); } } } else { if ($request->getTargetKind() == TargetKind::BAG()) { $request->raiseResponseVersion(3, 0); } } } } return $request; }
/** * Process the $expand and $select option and update the request description. * * @return void * * @throws ODataException Throws bad request error in the following cases * (1) If $expand or select cannot be applied to the * requested resource. * (2) If projection is disabled by the developer * (3) If some error occurs while parsing the options */ private function _processExpandAndSelect() { $expand = $this->service->getHost()->getQueryStringItem(ODataConstants::HTTPQUERY_STRING_EXPAND); if (!is_null($expand)) { $this->_checkExpandOrSelectApplicable(ODataConstants::HTTPQUERY_STRING_EXPAND); } $select = $this->service->getHost()->getQueryStringItem(ODataConstants::HTTPQUERY_STRING_SELECT); if (!is_null($select)) { if (!$this->service->getConfiguration()->getAcceptProjectionRequests()) { throw ODataException::createBadRequestError(Messages::configurationProjectionsNotAccepted()); } $this->_checkExpandOrSelectApplicable(ODataConstants::HTTPQUERY_STRING_SELECT); } // We will generate RootProjectionNode in case of $link request also, but // expand and select in this case must be null (we are ensuring this above) // 'RootProjectionNode' is required while generating next page Link if ($this->_expandSelectApplicable || $this->request->isLinkUri()) { $rootProjectionNode = ExpandProjectionParser::parseExpandAndSelectClause($this->request->getTargetResourceSetWrapper(), $this->request->getTargetResourceType(), $this->request->getInternalOrderByInfo(), $this->request->getSkipCount(), $this->request->getTopCount(), $expand, $select, $this->service->getProvidersWrapper()); if ($rootProjectionNode->isSelectionSpecified()) { $this->request->raiseMinVersionRequirement(2, 0); } if ($rootProjectionNode->hasPagedExpandedResult()) { $this->request->raiseResponseVersion(2, 0); } $this->request->setRootProjectionNode($rootProjectionNode); } }
/** * Pushes a segment for the current navigation property being written out. * Note: Refer 'ObjectModelSerializerNotes.txt' for more details about * 'Segment Stack' and this method. * Note: Calls to this method should be balanced with calls to popSegment. * * @param ResourceProperty &$resourceProperty Current navigation property * being written out * * @return bool true if a segment was pushed, false otherwise * * @throws InvalidOperationException If this function invoked with non-navigation * property instance. */ private function _pushSegmentForNavigationProperty(ResourceProperty &$resourceProperty) { if ($resourceProperty->getTypeKind() == ResourceTypeKind::ENTITY) { $this->assert(!empty($this->_segmentNames), '!is_empty($this->_segmentNames'); $currentResourceSetWrapper = $this->_getCurrentResourceSetWrapper(); $currentResourceType = $currentResourceSetWrapper->getResourceType(); $currentResourceSetWrapper = $this->service->getProvidersWrapper()->getResourceSetWrapperForNavigationProperty($currentResourceSetWrapper, $currentResourceType, $resourceProperty); $this->assert(!is_null($currentResourceSetWrapper), '!null($currentResourceSetWrapper)'); return $this->_pushSegment($resourceProperty->getName(), $currentResourceSetWrapper); } else { throw new InvalidOperationException('pushSegmentForNavigationProperty should not be called with non-entity type'); } }