Ошибка Error: 400: Bad Request для REST-запросов с HTTP-заголовком Referer на кириллице

После установки четвёртого пакета обновлени (Update Rollup 4) внезапно перестали работать клиентские REST-запросы из форм действий: задач, эл. писем, кастомных действий. Каждый вызов REST заканчивался ошибкой Error: 400: Bad Request: "Сервер обнаружил ошибку при обработке запроса. Дополнительные сведения см. в журналах сервера." (The server encountered an error processing the request. See server logs for more details.).

Error: 400: Bad Request: The server encountered an error processing the request. See server logs for more details

Пример запроса:

http://server/contoso/XRMServices/2011/OrganizationData.svc/AccountSet(guid'3F54544D-0BD7-4FC2-B38E-1E6BC2785DF9')?$select=Address1_PostalCode,Address1_StateOrProvince,Address1_City,Address1_Line1

Причём тот же самый запрос выполненный напрямую из адресной строки браузера отрабатывал успешно.

Трассировка CRM показала наличие управляющих символов в теле запроса: "Указанное значение содержит недопустимые знаки управления. Имя параметра: value".

at ServiceModelTraceRedirector.TraceData(TraceEventCache eventCache, String source, TraceEventType eventType, Int32 id, Object data)
at TraceSource.TraceData(TraceEventType eventType, Int32 id, Object data)
at DiagnosticTrace.TraceEvent(TraceEventType type, Int32 code, String msdnTraceCode, String description, TraceRecord trace, Exception exception, Object source)
at ExceptionUtility.TraceHandledException(Exception exception, TraceEventType eventType)
at MessageRpc.ProcessError(Exception e)
at MessageRpc.Process(Boolean isOperationContextSet)
at ChannelHandler.DispatchAndReleasePump(RequestContext request, Boolean cleanThread, OperationContext currentOperationContext)
at ChannelHandler.HandleRequest(RequestContext request, OperationContext currentOperationContext)
at ChannelHandler.AsyncMessagePump(IAsyncResult result)
at AsyncThunk.UnhandledExceptionFrame(IAsyncResult result)
at AsyncResult.Complete(Boolean completedSynchronously)
at AsyncQueueReader.Set(Item item)
at InputQueue`1.EnqueueAndDispatch(Item item, Boolean canDispatchOnThisThread)
at InputQueue`1.EnqueueAndDispatch(T item, Action dequeuedCallback, Boolean canDispatchOnThisThread)
at SingletonChannelAcceptor`3.Enqueue(QueueItemType item, Action dequeuedCallback, Boolean canDispatchOnThisThread)
at HttpChannelListener.HttpContextReceived(HttpRequestContext context, Action callback)
at HostedHttpTransportManager.HttpContextReceived(HostedHttpRequestAsyncResult result)
at HostedHttpRequestAsyncResult.HandleRequest()
at HostedHttpRequestAsyncResult.BeginRequest()
at HostedHttpRequestAsyncResult.OnBeginRequest(Object state)
at AspNetPartialTrustHelpers.PartialTrustInvoke(ContextCallback callback, Object state)
at HostedHttpRequestAsyncResult.OnBeginRequestWithFlow(Object state)
at ScheduledOverlapped.IOCallback(UInt32 errorCode, UInt32 numBytes, NativeOverlapped* nativeOverlapped)
at IOCompletionThunk.UnhandledExceptionFrame(UInt32 error, UInt32 bytesRead, NativeOverlapped* nativeOverlapped)
at _IOCompletionCallback.PerformIOCompletionCallback(UInt32 errorCode, UInt32 numBytes, NativeOverlapped* pOVERLAP)
><TraceRecord xmlns="http://schemas.microsoft.com/2004/10/E2ETraceEvent/TraceRecord" Severity="Error"><TraceIdentifier>http://msdn.microsoft.com/ru-RU/library/System.ServiceModel.Diagnostics....Обработка исключения.</Description><AppDomain>/LM/W3SVC/1/ROOT-1-116635956286758268</AppDomain><Exception><ExceptionType>System.ArgumentException, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</ExceptionType><Message>Указанное значение содержит недопустимые знаки управления.
>Имя параметра: value</Message><StackTrace>   в System.Net.WebHeaderCollection.CheckBadChars(String name, Boolean isHeaderValue)
>   в System.Net.WebHeaderCollection.Add(String name, String value)
>   в System.Collections.Specialized.NameValueCollection.Add(NameValueCollection c)
>   в System.ServiceModel.Activation.HostedHttpContext.HostedRequestContainer.System.ServiceModel.Channels.HttpRequestMessageProperty.IHttpHeaderProvider.CopyHeaders(WebHeaderCollection headers)
>   в System.ServiceModel.Web.IncomingWebRequestContext.get_Accept()
>   в System.Data.Services.DataServiceHostWrapper..ctor(IDataServiceHost host)
>   в System.Data.Services.DataService`1.HandleRequest()
>   в System.Data.Services.DataService`1.ProcessRequestForMessage(Stream messageBody)
>   в SyncInvokeProcessRequestForMessage(Object , Object[] , Object[] )
>   в System.ServiceModel.Dispatcher.SyncMethodInvoker.Invoke(Object instance, Object[] inputs, Object[]&amp; outputs)
>   в System.ServiceModel.Dispatcher.DispatchOperationRuntime.InvokeBegin(MessageRpc&amp; rpc)
>   в System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage5(MessageRpc&amp; rpc)
>   в System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage31(MessageRpc&amp; rpc)
>   в System.ServiceModel.Dispatcher.MessageRpc.Process(Boolean isOperationContextSet)</StackTrace><ExceptionString>System.ArgumentException: Указанное значение содержит недопустимые знаки управления.
>Имя параметра: value
>   в System.Net.WebHeaderCollection.CheckBadChars(String name, Boolean isHeaderValue)
>   в System.Net.WebHeaderCollection.Add(String name, String value)
>   в System.Collections.Specialized.NameValueCollection.Add(NameValueCollection c)
>   в System.ServiceModel.Activation.HostedHttpContext.HostedRequestContainer.System.ServiceModel.Channels.HttpRequestMessageProperty.IHttpHeaderProvider.CopyHeaders(WebHeaderCollection headers)
>   в System.ServiceModel.Web.IncomingWebRequestContext.get_Accept()
>   в System.Data.Services.DataServiceHostWrapper..ctor(IDataServiceHost host)
>   в System.Data.Services.DataService`1.HandleRequest()
>   в System.Data.Services.DataService`1.ProcessRequestForMessage(Stream messageBody)
>   в SyncInvokeProcessRequestForMessage(Object , Object[] , Object[] )
>   в System.ServiceModel.Dispatcher.SyncMethodInvoker.Invoke(Object instance, Object[] inputs, Object[]&amp; outputs)
>   в System.ServiceModel.Dispatcher.DispatchOperationRuntime.InvokeBegin(MessageRpc&amp; rpc)
>   в System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage5(MessageRpc&amp; rpc)
>   в System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage31(MessageRpc&amp; rpc)
>   в System.ServiceModel.Dispatcher.MessageRpc.Process(Boolean isOperationContextSet)</ExceptionString></Exception></TraceRecord>

Для разъяснениями обращаемся к Fiddler (Fiddler HTTP Debugging Proxy) и видим, что с каждым моим REST-запросом (вызываемым из javascript через XMLHttpRequest объект) отправляется HTTP-заголовок Referer, содержащий ссылку на текущую страницу (URL формы на которой исполняется скрипт).

Fiddler View

Обратите внимание, что в заголовке Referer присутствуют GET-параметры значения которых заданы кириллицей (параметры partyname и pName):

http://server/contoso/userdefined/edit.aspx?contactInfo=&etc=4202&id=%7b...Фамилия%20Имя%20Отчество&partytype=2&pId=%7b05F1C96C-7CDE-DE11-A23B-003048712BF7%7d&pName=Фамилия%20Имя%20Отчество&pType=2&sitemappath=Workplace%7cSFA%7cnav_orders#

Вот именно эти кириллические значения и ломают все REST-запросы! Эта догадка подтвердилась проверкой вызова скрипта для клиента с именем на латинице - запрос отработал успешно. Осталось избавиться от этого паразитного заголовка в нашем запросе. Самым простым решением кажется написание HTTP-модуля для IIS, который будет анализировать заголовки и кодировать кириллицу в HTML или обрубать эти значения (поскольку они никак не влияют на основной запрос).

Как оказалось, существует гораздо более удобный способ для фильтрации заголовков в IIS - URL Rewrite Module Version 2.0 for IIS 7. Этот модуль расширяет возможности встроенного URL Rewrite модуля Internet Information Server'а версии 7+.

Итак, скачиваем и устанавливаем URL Rewrite Module (внимание! потребуется перезагрузка сервера). И приступаем к созданию правила:

IIS URL Rewrite Module Version 2.0

Создаём правило для входящих запросов:

URL Rewrite Inbound Rules

Сначала задаём маску URL, для которого будет применятся правило (вместо contoso - имя вашей организации в CRM):

New URL Rewrite Rule: Match URL RegEx

Затем дополнительное условие на HTTP-заголовок Referer - обрабатываем только те запросы, в заголовке Referer которых присутствует кириллица:

New URL Rewrite Rule: HTTP_REFERER Condition

Для соответствующих правилу запросов заменяем значение HTTP-заголовка Referer (по-сути, конечной точке REST-сервиса он не нужен, поэтому оставляем только имя сервера):

New URL Rewrite Rule: Replace Server Variables

Теперь осталось разрешить серверу обрабатывать нужный нам заголовок, для чего добавляем его в разрешённые серверные переменные:

IIS - URL Rewrite Module - View Server Variables

IIS - URL Rewrite Module - Add Server Variables

После активации этого правила клиентские REST-запросы с форм сущностей действий выполняются успешно.

Замечено, что вызывая окно создания/редактирования действий разными путями (из меню "Добавить" основной записи, из меню "Добавить новый элемент типа Действие" связанного представления Действий) ошибка возникает только в первом случае. В предыдущей версии системы (CRM 2011 Rollup 1-3) русские имена записей кодировались для передачи в URL, четвёртый пакет исправлений сломал этот механизм, в пятом это так и не было исправлено.

 

Дополнительно о модуле URL Rewrite 2.0 можно прочитать по этим ссылкам:

 

Русский
field_vote: 
3.173555
Average: 3.2 (121 vote)

Комментарии

Диспетчер служб IIS надо запускать от администратора, иначе всё будет не так
регулярное выражение лучше задать так /XRMServices/2011/OrganizationData\.svc.+$
HTTP_REFERER надо добавить в разрешенные серверные переменные
дополнительное условие на русские буквы задавать не надо

Добавить комментарий