Подтвердить что ты не робот

WebException по запросу HTTP при отладке

У меня есть проект ASP.NET, который включает отправку HTTP-запросов через Web-API Framework. Следующее исключение возникает только при отладке:

Сервер совершил нарушение протокола. Раздел = ResponseStatusLine

Проект работает отлично, если я начинаю без отладки.

Как мне устранить это исключение?

Любая помощь приветствуется!


Обновление

Проблема связана с ASP.NET MVC Identity Framework.

Чтобы получить доступ к другим методам Web-API, клиентское приложение должно сначала выполнить POST запрос login (запрос на вход еще не должен быть защищен, и поэтому я отправляю строки имени пользователя и пароля непосредственно к методу Web-API POST). Если я прошу прокомментировать запрос на вход, больше не будет сделано никаких исключений.

Ниже приведены соответствующие фрагменты кода:

Метод Post:

UserManager<ApplicationUser> UserManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new ApplicationDbContext()));
AccountAccess ac = new AccountAccess();

public async Task<HttpResponseMessage> Post()
{
    string result = await Request.Content.ReadAsStringAsync();
    LoginMessage msg = JsonConvert.DeserializeObject<LoginMessage>(result);
    HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.OK);
    var user = UserManager.Find(msg.username, msg.password);
    if (user == null)
        return response;
    if (user.Roles == null)
        return response;
    var role = from r in user.Roles where (r.RoleId == "1" || r.RoleId == "2") select r;
    if (role.Count() == 0)
    {
        return response;
    }
    bool task = await ac.LoginAsync(msg.username, msg.password);
    response.Content = new StringContent(task.ToString());
    return response;
} 

Класс доступа к учетной записи (имитирующий шаблон AccountController по умолчанию в шаблоне MVC):

public class AccountAccess
{
    public static bool success = false;
    public AccountAccess()
        : this(new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new ApplicationDbContext())))
    {
    }

    public AccountAccess(UserManager<ApplicationUser> userManager)
    {
        UserManager = userManager;
    }

    public UserManager<ApplicationUser> UserManager { get; private set; }

    public async Task<bool> LoginAsync(string username, string password)
    {
        var user = await UserManager.FindAsync(username, password);
        if (user != null)
        {
             await SignInAsync(user, isPersistent: false);
             return true;
        }
        else
        {
            return false;
        }
    }
    ~AccountAccess()
    {
        if (UserManager != null)
        {
            UserManager.Dispose();
            UserManager = null;
        }
    }

    private IAuthenticationManager AuthenticationManager
    {
        get
        {
            return HttpContext.Current.GetOwinContext().Authentication;
        }
    }

    private async Task SignInAsync(ApplicationUser user, bool isPersistent)
    {
        AuthenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie);
        var identity = await UserManager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie);
        AuthenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = isPersistent }, identity);
    }
}

Ниже приведены соответствующие фрагменты кода:

В клиентском приложении:

public static async Task<List<T>> getItemAsync<T>(string urlAction)
{
    message = new HttpRequestMessage();
    message.Method = HttpMethod.Get;
    message.RequestUri = new Uri(urlBase + urlAction);
    HttpResponseMessage response = await client.SendAsync(message);
    string result = await response.Content.ReadAsStringAsync();
    List<T> msgs = JsonConvert.DeserializeObject<List<T>>(result);
    return msgs;
}

В контроллере Web-API:

public HttpResponseMessage Get(string id)
{
    HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.OK);
    if (id == "ItemA")
    {
        List<ItemAMessage> msgs = new List<ItemAMessage>();

        // some code...

        response.Content = new StringContent(JsonConvert.SerializeObject(msgs));
    }
    else if (id == "ItemB")
    {
         List<ItemBMessage> msgs = new List<ItemBMessage>();

        // some code...

        response.Content = new StringContent(JsonConvert.SerializeObject(msgs));
    }
    return response;
}

Некоторые наблюдения, которые у меня есть:

  • Я думал, что мне может потребоваться отправить запрос асинхронно (с синтаксисом async-await), но исключение все еще сохраняется.
  • Если я прохожу через код, запрос делает, введите метод HTTP, но код прерывается в случайной строке (почему?!), прежде чем возвращать ответ, поэтому я не предполагаю, что никакой ответ не отправляется назад.
  • Я попробовал следующие решения, как это было предложено в ответах на подобные вопросы, ни одна из которых не работает для меня:
    • Настройка useUnsafeHeaderParsing - true
    • Добавление заголовка Keep-Alive: false
    • Изменение настроек порта Skype (у меня нет Skype, а порты 80 и 443 не заняты)

Дополнительная информация, если они имеют значение:

  • Mac OS, работающая под управлением Windows 8.1 с помощью VMware Fusion
  • Visual Studio 2013
  • .NET Framework 4.5
  • Сервер IIS Express

Обновление 2

Исключение разрешено, но я не уверен, какая модификация сделала трюк. AFAIK, либо один, либо оба из следующих зафиксировали его:

  • У меня есть метод checkConnection(), который в основном отправляет запрос GET и возвращает true при успешном завершении. Я добавил await в метод HttpClient.SendAsync() и принудительно async до конца.
  • Я удалил весь код в конструкторе MainWindow, за исключением метода InitializeComponent(), в обработчик события Initial Initialized.

Любая идея?

Ниже приведены соответствующие коды к приведенным выше изменениям:

метод checkConnectionAsync:

public static async Task<bool> checkConnectionAsync()
{
    message = new HttpRequestMessage();
    message.Method = HttpMethod.Get;
    message.RequestUri = new Uri(urlBase);
    try
    {
        HttpResponseMessage response = await client.SendAsync(message);
        return (response.IsSuccessStatusCode);
    }
    catch (AggregateException)
    {
        return false;
    }
}

Обработчик события инициализированного окна (отведенный из конструктора MainWindow):

private async void Window_Initialized(object sender, EventArgs e)
{
    if (await checkConnectionAsync())
    {
        await loggingIn();
        getItemA();
        getItemB();
    }
    else
    {
        logMsg.Content = "Connection Lost. Restart GUI and try again.";
    }
}

Обновление 3

Хотя это может быть немного не по теме, я хотел бы добавить примечание на стороне, если кто-то еще попадает в это - Я использовал неправильный подход к аутентификации для веб-API для начала. Шаблон проекта Web-API уже имеет встроенную структуру Identity, и я как-то "заменил" ее довольно простым, но сломанным подходом...

Это видео - это хороший учебник для начала.

В этой статье содержится более подробное объяснение.

4b9b3361

Ответ 1

В клиентском приложении вы не ожидаете task. Доступ к результату без ожидания может привести к непредсказуемым ошибкам. Если он не работает в режиме отладки, я не могу сказать точно, но это, конечно, не та же программа (добавлены дополнительные проверки, обычно оптимизация не включена). Независимо от того, когда отладка активна, если у вас есть ошибка кода, вы должны исправить это, и она должна работать в обоих режимах.

Итак, сделайте эту функцию асинхронной и вызовите задачу с помощью модификатора await или вызовите task.WaitAndUnwrapException() в задаче, чтобы она блокировала синхронно, пока результат не будет возвращен с сервера.

Ответ 2

Убедитесь, что URL-адрес имеет строку запроса ID со значением либо как Item A, либо Item B. В противном случае вы не будете возвращать контент с кодом статуса Http 200, который может привести к нарушению протокола.

Ответ 3

Когда вы используете SendAsync, вам необходимо предоставить все соответствующие заголовки сообщений, в том числе message.Headers.Authorization = new AuthenticationHeaderValue("Basic", token);, например.
Вместо этого вы можете использовать GetAsync (и вызвать конкретный метод get на сервере).
Кроме того, уверены ли вы, что исключение разрешено? Если у вас есть метод async с высоким уровнем, который возвращает Task, а не void, это исключение можно игнорировать без пауз.