2015-08-27 4 views
21

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

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

Проект работает отлично, если «Начать без отладки».

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

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


Update

Проблема, кажется, связано с ASP.NET MVC Framework идентичности.

Чтобы получить доступ к другим методам Web-API, клиентское приложение должно первый POST логин запрос (запрос Логин не должны быть безопасными еще и поэтому я посылаю имя пользователя и пароль строки непосредственно в Интернете -API 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; 
} 

Класс Account Access (имитируя 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; 
} 

Некоторые замечания у меня есть:

  1. I th должно быть, мне может потребоваться отправить запрос асинхронно (с синтаксисом async-await), но исключение все еще сохраняется.
  2. Если я прохожу через код, запрос делает, введите метод HTTP, но код возвращается в случайную строку (почему ?!), прежде чем возвращать ответ, поэтому я не предполагаю, что ответ не отправляется обратно.
  3. Я попытался следующие решения, как это было предложено в ответах на подобные вопросы, ни один из которых работает для меня:
    • Установка 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 Server

Обновление 2

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

  • У меня есть checkConnection() метод, который в основном отправляет запрос GET и возвращает истину в случае успеха. Я добавил await к методу HttpClient.SendAsync() и ввел в действие асинхронный путь до.
  • Я отменил весь код в конструкторе 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."; 
    } 
} 

Update 3

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

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

This article содержит более подробное объяснение.

+1

Что произойдет, если вы удалите вызов сна в LoginAsync? –

+0

@Brianfromstatefarm Я действительно добавил эту строку, чтобы проверить что-то еще и забыл удалить ее при публикации кода. Спасибо, что указали! –

+0

Возможно ли, что VM добавляет к проблеме? –

ответ

6

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

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

+0

На самом деле у меня есть обе версии (с и без ожидания) метода getItem, если это тот, о котором вы говорите. К сожалению, ожидание и обеспечение «async all the down/up» не устраняет исключение, которое я получаю. Спасибо, что все-таки указали! Я редактировал код, чтобы избежать путаницы. –

+0

Я наградил щедростью этот ответ, так как он указывает на самую возможную причину моей проблемы. –

1

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

1

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

+0

Не могли бы вы рассказать о своем последнем заявлении? Как указано во втором обновлении, я изменил код, который будет «асинхронным до конца», причем самый высокий уровень (обработчики событий) возвращает «void». Все остальные методы async возвращают 'Task'. –

+0

Что касается другого вопроса, следует ли использовать GetAsync с запросами GET (или HEAD) и SendAsync для остальных? –

+0

Я хотел убедиться, что на самом деле нет обработчиков событий, возвращающих «Task» или другие задачи, которые не ожидаются. –

Смежные вопросы