Minimize o tráfego nos formulários da Web do ASP.NET, div clicável e pesquisas periódicas no servidor

A tecnologia ASP.NET Web Forms é lenta mas seguramente uma coisa do passado. Ele está sendo substituído por uma API da Web por Angular 6 e pilhas semelhantes. Mas herdei um projeto em Web Forms com enorme legado. Tenho alguns amigos que têm uma situação semelhante a mais ou a menos. Aplicativos de longa data sobre tecnologia antiga que precisam ser desenvolvidos e mantidos. O Web Forms tem a capacidade no PostBack de não atualizar a página inteira, mas apenas parte dela. O que está incluído no UpdatePanel. Isso adiciona interatividade, mas ainda funciona bem devagar e consome muito tráfego, porque Cada vez que a renderização ocorre no servidor e a marcação final é passada para o cliente, que precisa ser inserido em vez da div atual atual. A propósito, UpdatePanel é renderizado apenas em uma div, na qual a marcação é substituída.

O que pode ser feito para minimizar o tráfego?

  1. Escreva WebMethod na página e chame-o pelo cliente usando as ferramentas AJAX. Quando você receber uma resposta, altere o DOM via JS.

    A desvantagem desta solução é que você não pode definir o WebMethod no controle. Não quero escrever toda a funcionalidade da página, especialmente se for usada várias vezes em páginas diferentes.
  2. Escreva um serviço asmx e chame-o do cliente. Isso é melhor, mas neste caso não há conexão explícita entre o controle e o serviço. O número de serviços aumentará com um aumento no número de controles. Além disso, o ViewState não estará disponível para nós, o que significa que passaremos os parâmetros explicitamente ao acessar o serviço; portanto, faremos a validação do servidor e verificaremos se o usuário tem o direito de fazer o que ele solicitou.
  3. Use a interface ICallbackEventHandler. Esta é, na minha opinião, uma boa opção.

    Vou me debruçar sobre isso em mais detalhes.

A primeira coisa a fazer é herdar nosso UserControl de ICallbackEventHandler e gravar os métodos RaiseCallbackEvent e GetCallbackResult. É um pouco estranho que existam 2. O primeiro é responsável por receber parâmetros do cliente, o segundo é responsável por retornar o resultado.
Será algo parecido com isto

public partial class SomeControl : UserControl, ICallbackEventHandler { #region  /// <summary> ///    /// </summary> private Guid _someFileId; #endregion #region  ICallbackEventHandler /// <inheritdoc /> public void RaiseCallbackEvent(string eventArgument) { //    try { dynamic args = JsonConvert.DeserializeObject<dynamic>(eventArgument); _someFileId = (Guid) args.SomeFileId; string type = (string) args.Type; } catch (Exception exc) { //  throw; } } /// <inheritdoc /> public string GetCallbackResult() { //  try { // -  return JsonConvert.SerializeObject(new { Action = actionName, FileId = _someFileId, }); } catch (Exception exc) { //  throw; } } #endregion } 

Era o lado do servidor. Agora cliente

 var SomeControl = { _successCallbackHandler: function (responseData) { let data = JSON.parse(responseData); switch (data.Action) { case "continue": // -   break; case "success": //  -  break; case "fail": //   break; default: //   alert,     alert("      "); break; } }, _failCallbackHandler: function() { alert("      "); }, } 

Isso não é tudo. Ainda precisamos gerar JS para conectar todas as nossas funções.

  protected override void OnLoad(EventArgs e) { base.OnLoad(e); //   SomeControl.js,     Page.ClientScript.RegisterClientScriptInclude(Page.GetType(), "SomeControl", "/Scripts/controls/SomeControl.js?v=2.24.0"); string callbackArgument = //   //***  .***   JS   SomeControl  CallServer.       ,     ScriptManager.RegisterStartupScript(Page, Page.GetType(), "SomeControl.Initialize", $@"SomeControl.CallServer = function(someFileId) {{ let args = JSON.stringify({{ SomeFileId : someFileId, Type: '{callbackArgument}' }}); {Page.ClientScript.GetCallbackEventReference(this, "args", "SomeControl._successCallbackHandler", string.Empty, "SomeControl._failCallbackHandler", true)}; }};", true); //    ScriptManager.GetCurrent(Page)?.RegisterAsyncPostBackControl(this); } 

Obviamente, esse é o código por trás do controle.

O mais interessante é a geração de funções JS usando o método GetCallbackEventReference.

Nós passamos por ele

  • link para o nosso controle
  • o nome da variável JS cujo valor será passado para o servidor no método RaiseCallbackEvent via eventArgument (a linha acima serializa o objeto em JSON para transmissão e realmente define o valor dessa variável args)
  • Nome de retorno de chamada da função JS para obter sucesso
  • contexto de execução (não o uso)
  • o nome da função de retorno de chamada JS no caso de algo dar errado
  • validaremos a solicitação que chegou ao servidor usando as ferramentas ASP.NET

Como tudo isso funcionará juntos?

No JS, podemos chamar SomeControl.CallServer, essa função criará uma variável local args e passará o controle para uma função que fará uma solicitação ao servidor através do AJAX.
Em seguida, o controle é passado para o método do servidor RaiseCallbackEvent. Tudo o que estava na variável do cliente args agora caiu no parâmetro de entrada de servidor eventArgument.
Depois que o RaiseCallbackEvent for executado, o controle será passado para GetCallbackResult.

A string que retornaremos por retorno será enviada ao cliente e entrará no parâmetro de entrada da função SomeControl._successCallbackHandler, ou seja, em responseData.
Se, em algum momento, o código do servidor lançar uma exceção, o controle será transferido para o cliente SomeControl._failCallbackHandler

Ainda preciso dizer sobre o ViewState. O ViewState é transferido do cliente para o servidor e pode ser usado, mas apenas no modo ReadOnly, como O ViewState não é enviado de volta ao cliente.

O design é confuso à primeira vista, mas, se você observar, é bastante conveniente e o tráfego foi economizado.

A segunda pergunta que quero abordar neste artigo é divs clicáveis ​​ou como você pode chamar a atualização UpdatePanel no lado do cliente.

Por que divs clicáveis ​​são necessárias, você pode simplesmente usar <asp: Button>?
Eu gosto que a div possa ser composta como eu quero, não estou limitado pelo tipo de entrada = "botão"

Para implementação, é necessário ser herdado da interface IPostBackEventHandler

Ele tem apenas 1 método

 public void RaisePostBackEvent(string eventArgument) 

Agora, como no caso anterior, precisamos gerar JS para chamar esse método

Parece que isso

 Page.ClientScript.GetPostBackEventReference(this, callbackArgument) 

callbackArgument está definido no servidor e alterá-lo no cliente não funcionará. Mas você sempre pode colocar algo em um HiddenField. Temos um PostBack completo

Agora, o resultado do GetPostBackEventReference pode ser suspenso com o clique de qualquer div ou span, ou o que for.

Ou apenas ligue de JS por timer.

Certifique-se de registrar o controle como assíncrono (no OnLoad chamamos

 ScriptManager.GetCurrent(Page)?.RegisterAsyncPostBackControl(this); 
), caso contrário, mesmo dentro do UpdatePanel, o PostBack síncrono será chamado e a página inteira será atualizada, e não apenas o conteúdo do UpdatePanel

Usando os 2 métodos descritos acima, implementei, por exemplo, esse cenário.

O usuário clicou no botão, uma pequena solicitação para uma operação longa (10 a 15 segundos) foi enviada ao servidor, o usuário recebeu uma resposta curta, durante a análise da qual o script do cliente chama setTimeout. No setTimeout, uma função é passada para um retorno de chamada para o servidor para descobrir os resultados de uma operação solicitada anteriormente. Se o resultado estiver pronto, chame PostBack no UpdatePanel - o UpdatePanel especificado é atualizado. Se o resultado ainda não estiver pronto, chame setTimeout novamente.

Boa sorte para todos que ainda trabalham com Web Forms, espero que o artigo torne seus sistemas mais rápidos e mais bonitos, e os usuários agradecerão.

Source: https://habr.com/ru/post/pt427291/


All Articles