• С чего начать обучение C# для разработки софта под соц.сети и дальнейшей работы с API этих соц.сетей?

    AlekseyNemiro
    @AlekseyNemiro
    full-stack developer
    Работа с API - это просто веб-запросы.

    Подготовка запросов и обработка ответов - это в большинстве своем сериализация/десериализация, в основном JSON-данных, реже XML.

    Все остальное будет зависеть от типа приложений, которые вы планируете разрабатывать.

    По работе с API социальных сетей лучше читать официальную документацию.

    У меня есть библиотека для авторизации по протоколу OAuth (используется многими, если не всеми, социальными сетями), которая помимо прочего, имеет унифицированные механизмы работы с API. Посмотреть на работу библиотеки можно по следующей ссылке: demo-oauth.nemiro.net

    Для многих крупных проектов (сайтов) можно найти готовые узкопрофильные решения.

    Ниже представлен небольшой пример использования библиотеки Nemiro.OAuth в проекте Windows Forms для авторизации через Instagram и использование полученного маркера доступа для загрузки списка последних изображений текущего пользователя. Изображения загружаются в ImageList и затем выводятся в ListView:
    using System;
    using System.Drawing;
    using System.Net.Http;
    using System.Windows.Forms;
    using Nemiro.OAuth;
    using Nemiro.OAuth.LoginForms;
    
    namespace InstagramWinForms
    {
    
      public partial class Form1 : Form
      {
    
        // базовый адрес API
        private const string API_BASE_URL = "https://api.instagram.com/v1";
    
        // элемент для хранения полученных изображений
        private ImageList ImageList = new ImageList();
    
        // элемент для вывода изображений
        private ListView ListView1 = new ListView();
    
        private string AccessToken;
    
        public Form1()
        {
          InitializeComponent();
    
          // размер изображений 150x150px, 16bit
          this.ImageList.ImageSize = new Size(150, 150);
          this.ImageList.ColorDepth = ColorDepth.Depth16Bit;
    
          // настраиваем список для вывода
          this.ListView1.View = View.LargeIcon;
          this.ListView1.LargeImageList = this.ImageList;
    
          this.ListView1.Dock = DockStyle.Fill;
    
          // добавляем список на форму
          this.Controls.Add(this.ListView1);
        }
    
        private void Form1_Load(object sender, EventArgs e)
        {
          // запрос на получение маркера доступа
          this.GetAccessToken();
        }
    
        private void GetAccessToken()
        {
          // создаем форму для Instagram
          // ВНИМАНИЕ: используйте собственный идентификатор и ключ
          // получить идентификатор и ключ можно на сайте instagram:
          // https://www.instagram.com/developer/clients/manage/
          var login = new InstagramLogin
          (
            // client id вашего приложения
            "9fcad1f7740b4b66ba9a0357eb9b7dda", 
            // client key вашего приложения
            "3f04cbf48f194739a10d4911c93dcece", 
            // требуется адрес возврата, 
            // можно использовать указанный, 
            // но лучше сделать свой
            "http://oauthproxy.nemiro.net/",
            // права доступа
            // https://www.instagram.com/developer/authorization/
            // для public_content (и возможно других) убедитесь, 
            // что в настройках приложения (на сайте instagram) 
            // в разделе Permissions нет никаких требований 
            // (если требования есть, то чтобы все работало, 
            // нужно их удовлетворить :-) ...)
            scope: "basic public_content",
            // требуем получить данные профиля пользователя
            loadUserInfo: true
          );
    
          // привязываем форму авторизации к текущей форме
          login.Owner = this;
    
          // показываем форму
          login.ShowDialog();
    
          if (login.IsSuccessfully)
          {
            // все прошло успешно, запоминаем маркер доступа
            this.AccessToken = login.AccessToken.Value;
    
            // выводим в заголовок текущей формы имя пользователя
            this.Text = (login.UserInfo.DisplayName ?? login.UserInfo.UserName);
    
            // получаем изображения
            this.GetRecentMedia();
          }
          else
          {
            MessageBox.Show("Error...", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
          }
        }
    
        private void GetRecentMedia()
        {
          // отправляем запрос на получение изображений
          OAuthUtility.GetAsync
          (
            String.Format
            (
              "{0}/users/self/media/recent?access_token={1}", 
              API_BASE_URL, 
              this.AccessToken
            ),
            // результат запроса будет передан в метод GetRecentMedia_Result
            callback: GetRecentMedia_Result
          );
        }
    
        private async void GetRecentMedia_Result(RequestResult result)
        {
          if (result.StatusCode == 200)
          {
            // получили успешный ответ
            // обрабатываем его
            foreach (UniValue item in result["data"])
            {
              // загружаем текущую картинку
              using (var client = new HttpClient())
              {
                var s = await client.GetStreamAsync(item["images"]["thumbnail"]["url"].ToString());
    
                // добавляем изображение в список
                Invoke(new Action(() => this.ImageList.Images.Add(Image.FromStream(s))));
              }
    
              // создаем элемент для вывода в список 
              var image = new ListViewItem();
              // название изображения
              image.Text = item["caption"]["text"].ToString();
              // индекс изображения в списке (ImageList)
              image.ImageIndex = this.ImageList.Images.Count - 1;
    
              // добавляем элемент в список
              Invoke(new Action(() => this.ListView1.Items.Add(image)));
            }
          }
          else
          {
            this.ShowError(result);
          }
        }
    
        private void ShowError(RequestResult result)
        {
          if (result["meta"]["error_message"].HasValue)
          {
            MessageBox.Show
            (
              result["meta"]["error_message"].ToString(), 
              "Error", 
              MessageBoxButtons.OK, 
              MessageBoxIcon.Error
            );
          }
          else
          {
            MessageBox.Show
            (
              result.ToString(), 
              "Error", 
              MessageBoxButtons.OK, 
              MessageBoxIcon.Error
            );
          }
        }
    
        private void button1_Click(object sender, EventArgs e)
        {
          this.GetRecentMedia();
        }
    
      }
    
    }

    preview.gif?raw=true
    Код проекта можно найти по следующей ссылке:
    https://github.com/alekseynemiro/nemiro.oauth.dll/...
    Ответ написан
    2 комментария
  • Какой шаблон для RegEx коррентный?

    AlekseyNemiro
    @AlekseyNemiro
    full-stack developer
    // .* - может съесть лишнего, 
    // если есть возможность, лучше строго ограничивать
    // в данном случае четкой границей 
    // может служить открытие следующего тега (<)
    // "(.+?)<div" имеет смысл использовать, если в искомом тексте могут быть другие теги
    // class="margin-top-xx-small" тоже можно использовать, 
    // но только если это действительно необходимо
    var pattern = @"<div(\s+)class=""currency-table__rate__text"">(?<data>[^\<]+)<";
    var reg = new Regex(pattern, RegexOptions.IgnoreCase);
    var m = reg.Match(value); // вместо value переменная с данными для разбора
    var result = m.Groups["data"].Value.Trim();
    Console.WriteLine(result);
    Ответ написан
    Комментировать
  • Как научиться писать код быстро?

    AlekseyNemiro
    @AlekseyNemiro
    full-stack developer
    Методика есть и она стара как мир:

    Семь раз подумай, один раз напиши.
    Чем меньше напишешь, тем меньше будет работы.

    А каким образом осуществлять мыслительный процесс - это вам виднее :-)

    Учитесь выносить обдумывание на задний план, так сказать в фоновый режим, чтобы мозг сам думал, а вы могли заниматься чем-то другим. В особенности это касается сложных задач. Сложные задачи где-то в глубинах мозга решаются быстрее. Каким образом развивать этот навык, не могу точно сказать, все само как-то получается. Но однозначно можно утверждать, что нужно чаще думать :-)

    Что касается механики, то быстро вводить код - это не то, к чему стоит стремиться, конечно если целью не является желание стать секретаршей :-)
    Ответ написан
    5 комментариев
  • Представления (View) в ASP.NET должен разрабатывать из чистой верстки фронтендер или бекенд-программист?

    AlekseyNemiro
    @AlekseyNemiro
    full-stack developer
    Это front-end и если в компании есть соответствующий разработчик, то это его удел.

    Серверного кода со стороны представлений не должно быть слишком много и все что будет должно быть простым. По сути просто вывод данных, элементарные условия, циклы, использование готовых методов. Любой разработчик справится, иначе не был бы разработчик разработчиком :-) Базовые знания C#, конечно, понадобятся, но для разработчика это не проблема, поскольку разработчик уже должен иметь навыки программирования и новый язык - это просто альтернативное представление уже имеющихся знаний и опыта. В данном случае, роль C# следует рассматривать как JavaScript.

    Если в проекте много клиентского кода, то лучше front-end разработчика никто не сможет обеспечить его правильную работу и использование. Если этим будет заниматься back-end разработчик, то он потратит больше времени, поскольку ему придется сильно углубляться во front-end, рискуя при этом переквалифицироваться. А вот front-end разработчику уйти в back-end будет сложнее, поскольку область его деятельности ограничена представлениями.

    Сделать HTML - это верстка, а не разработка, напрягать только ради этого разработчиков нет смысла, все равно что из пушки по воробьям стрелять, для этого есть верстальщики.
    Ответ написан
    Комментировать
  • Возможно ли вообще организовать локальную MySQL для C#, доступную для коннектора из программы?

    AlekseyNemiro
    @AlekseyNemiro
    full-stack developer
    Конечно возможно. Интернет для локальных соединений не нужен.

    Строка соединения примерно такая:

    Server=localhost;Database=example;UID=username;Password=password

    где:
    • example - имя базы данных;
    • usersname - имя пользователя;
    • password - пароль.

    Если используется нестандартный порт, то его можно указать отдельно: Port=123.

    Denwer и прочие тут вообще не нужны. Достаточно скачать официальный дистрибутив MySql и установить.

    С помощью MySQL Workbench можно управлять базами данных через графический интерфейс.

    Код получения данных из MySql в C# может быть примерно таким:

    var connectionString = "Server=localhost;Database=example;UID=username;Password=password";
    using (var connection = new MySqlConnection(connectionString))
    {
      // открываем соединение
      connection.Open();
    
      // создаем команду
      var cmd = new MySqlCommand();
      cmd.Connection = connection;
      cmd.CommandText = "SELECT * FROM table1";
      
      // создаем адаптер
      var adapter = new MySqlDataAdapter(cmd);
      // создаем таблицу
      var table = new DataTable();
      // получаем данные в таблицу
      adapter.Fill(table);
      
      // выводим
      foreach (DataRow row in table.Rows)
      {
        Console.WriteLine(row[0]);
      }
    }
    Ответ написан
    Комментировать
  • Как сделать возможным запуск c# приложения на разных платформах NET?

    AlekseyNemiro
    @AlekseyNemiro
    full-stack developer
    Совместимость версий в .NET Framework
    Элемент <supportedRuntime>
    <configuration>
      <startup>
        <supportedRuntime version="v4.0" /> 
        <supportedRuntime version="v2.0.50727" /> 
      </startup>
    </configuration>

    также хотелось бы узнать как компилировать в месте с ним.

    Код для младших версий должен нормально работать в старших.

    В крайнем случае можно использовать Директивы препроцессора C#.
    Ответ написан
  • Почему Margin у button не меняется?

    AlekseyNemiro
    @AlekseyNemiro
    full-stack developer
    Вероятно что-то, например ThicknessAnimation, меняет Margin у кнопки.

    Нужно убрать анимацию перед внесением изменений:
    Button btn = (Button)sender;
    
    button.BeginAnimation(MarginProperty, null);
    
    btn.Margin = new Thickness();
    Thickness margin = btn.Margin;
    // ...
    Ответ написан
    Комментировать
  • Как сравнить button==button из массива в c#?

    AlekseyNemiro
    @AlekseyNemiro
    full-stack developer
    Кнопки сравнить просто так не получится, это разные экземпляры и они будут разными, даже если имеют одинаковый набор значений свойств.

    Можно сравнить, например, Text.

    Для этого достаточно сделать переменную на уровне класса, в которую помещать текст первой нажатой кнопки (или ссылку на саму кнопку). Когда будет нажата следующая кнопка, сравнивать её текст с сохраненным значением.

    private Button FirstButton = null;
    
    void S_MouseClick(object sender, MouseEventArgs e)
    {
      var button = (sender as Button);
    
      if (this.FirstButton == null)
      {
        // это первая кнопка в текущей сессии,
        // запоминаем ссылку на кнопку
        this.FirstButton = button;
      }
      else
      {
        // это вторая кнопка в текущей сессии
        // сравниваем текст с первой
        if (this.FirstButton.Text == button.Text)
        {
          Console.WriteLine("Текст совпадает!");
          // меняем свойства кнопок
          this.FirstButton.Text = button.Text = "--";
          this.FirstButton.Enabled = button.Enabled = false;
        }
        else
        {
          Console.WriteLine("Текст не совпадает.");
        }
        // сбрасываем выбор, запуская тем самым новую сессию
        this.FirstButton = null;
      }
    }

    Если понадобится сравнивать более двух кнопок, то придется делать коллекцию.

    Вместо Text можно сравнивать по какому-нибудь другому признаку. У элементов есть свойство Tag, которое можно использовать для хранения любых дополнительных данных.

    Если все будет совсем сложно, то можно расширить Button дополнительными свойствами путем наследования. В таком случае можно будет переопределить Equals и с операторами поиграть.
    Ответ написан
    2 комментария
  • Как лучше сделать в MVC подобее Url.Action в аттрибуте на server side?

    AlekseyNemiro
    @AlekseyNemiro
    full-stack developer
    Перенаправление можно выполнить примерно так:

    HttpContext.Current.Response.RedirectToRoute
    (
      new 
      { 
        controller = "Home", 
        action = "Index" 
      }
    );

    Лучше сделать вспомогательный класс и соответствующие методы для этого.

    Маршруты можно найти в System.Web.Routing.RouteTable.Routes.

    Данные текущего маршрута:

    var routeData = ((System.Web.Mvc.MvcHandler)HttpContext.Current.Handler).RequestContext.RouteData;

    Сделать экземпляр UrlHelper для контекста текущего запроса можно следующим образом:

    var urlHelper = new System.Web.Mvc.UrlHelper(HttpContext.Current.Request.RequestContext);
    var url = urlHelper.Action("Index", "Home");
    Ответ написан
    Комментировать
  • Как определить промежуток времени в (System.Timers.Timer)?

    AlekseyNemiro
    @AlekseyNemiro
    full-stack developer
    Интервал указывается в миллисекундах. Одна секунда равняется 1 000 миллисекунд.
    Соответственно 25 000 миллисекунд - это 25 секунд.

    // счетчик секунд
    int secondsCounter = 0;
    
    var s500A2 = new System.Timers.Timer();
    
    // интервал - одна секунда
    s500A2.Interval = 1000;
    
    // обработчик истечения интервала
    s500A2.Elapsed += (sender, e) => { // имена sender и e можно поменять, если будут проблемы
      Console.WriteLine("Прошла секунда");
    
      // увеличиваем значение счетчика
      secondsCounter++;
    
      if (secondsCounter >= 10)
      {
        Console.WriteLine("Всего прошло 10 секунд");
        secondsCounter = 0;
      }
    };
    
    s500A2.Start();

    Либо можно установить Interval в значение 10 000, тогда Elapsed будет вызываться каждые 10 секунд.

    Однако с точность все не так просто. Есть незначительная погрешность (в миллисекундах), которая со временем будет расти и в какой-то момент может потеряться секунда. Если точность критична, то лучше использовать меньшие значения.

    Следующий пример демонстрирует погрешность относительно текущего времени:

    // интервал - одна секунда
    s500A2.Interval = 1000;
    
    s500A2.Elapsed += (sender, e) => {
      Console.WriteLine("Прошла секунда: {0:HH:mm:ss.fff}", DateTime.Now);
    };
    
    // На выходе будет примерно это:
    // Прошла секунда: 14:18:20.950
    // Прошла секунда: 14:18:21.964
    // Прошла секунда: 14:18:22.978
    // Прошла секунда: 14:18:23.992 
    // Прошла секунда: 14:18:25.006 // потеряли 14:18:24
    // Прошла секунда: 14:18:26.020
    // Прошла секунда: 14:18:27.034

    В следующем примере показано решение проблемы с потерей секунд:

    int lastSecond = 0;
    // интервал - десять миллисекунд
    s500A2.Interval = 10;
    
    s500A2.Elapsed += (sender, e) => {
      int currentSecond = DateTime.Now.Second;
    
      if (currentSecond != lastSecond)
      {
        Console.WriteLine("Прошла секунда: {0:HH:mm:ss.fff}", DateTime.Now);
        lastSecond = currentSecond;
      }
    };
    Ответ написан
    5 комментариев
  • Как найти одинаковые элементы массива объектов (в 1 массиве)?

    AlekseyNemiro
    @AlekseyNemiro
    full-stack developer
    Собрать коллекцию, примерно так:
    var result = new Dictionary<int, List<object>>();
    
    foreach (var item in arrComputer)
    {
      var c = (Comp)item;
      // проверяем, есть такой год в коллекции или нет
      if (!result.ContainsKey(c.year))
      {
        // такого года еще нет, добавляем
        result.Add(c.year, new List<object> { c });
      }
      else
      {
        // год есть, добавляем запись в него
        result[c.year].Add(c);
      }
    }
    
    // в result будет коллекция: год-компьютеры
    foreach (int year in result.Keys)
    {
      Console.WriteLine
      (
        "В {0} году на Земле вылупилось компьютеров: {1}", 
        year, 
        result[year].Count
      );
    
      if (result[year].Count > 1)
      {
        Console.WriteLine("Да это просто демографический взрыв какой-то!");
        foreach (var item in result[year])
        {
          Console.WriteLine("+ {0}", ((Comp)item).marka);
        }
      }
    }

    Вместо object можно сразу использовать Comp, если использование других типов не предполагается.
    Ответ написан
    Комментировать
  • Как завершить редактирование ячейки DataGrid стрелками на клавиатуре?

    AlekseyNemiro
    @AlekseyNemiro
    full-stack developer
    Попробуйте обрабатывать событие PreviewKeyDown:
    private void dataGrid_PreviewKeyDown(object sender, KeyEventArgs e)
    {
      if (e.Key == Key.Up || e.Key == Key.Down)
      {
        dataGrid.CancelEdit();
      }
    }

    <DataGrid x:Name="dataGrid" PreviewKeyDown="dataGrid_PreviewKeyDown" ...>
    Ответ написан
  • Как дебажить ajax ошибку?

    AlekseyNemiro
    @AlekseyNemiro
    full-stack developer
    Отключите обработку ошибок (в web.config и фильтры) и уведите в чем проблема.

    Метод научного тыка говорит, что проблема в кодированных данных (выделено жирным):

    +%26%23171%3B%D0%94%D0%A0%D0%A3%D0%93%D0%90+%D0%91%D0%9E%D0%A0%D0%9E%D0%94%D0%90%26%23187%3B

    %26%23171%3B => &#171;
    %26%23187%3B => &#187;
    Ответ написан
    2 комментария
  • Как обновить грид с другой страницы?

    AlekseyNemiro
    @AlekseyNemiro
    full-stack developer
    Если страница/окно с кнопкой создается с помощью JavaScript, то можно отправить прямой запрос родительскому окну (через window.parent). Если нет, то видимо с AJAX или задействовать SignalR, что окажется проще/ближе.
    Ответ написан
    Комментировать
  • Как скомпилировать проект на c# в dotnet?

    AlekseyNemiro
    @AlekseyNemiro
    full-stack developer
    # клонируем рекурсивно, т.к. там ссылка на другой репозиторий
    git clone --recursive https://github.com/yar229/WebDavMailRuCloud.git
    # тянем последнюю версию nuget, если таковой еще нет
    wget https://dist.nuget.org/win-x86-commandline/latest/nuget.exe -O nuget.exe --no-check-certificate
    # восстанавливаем пакеты nuget
    mono nuget.exe restore WebDavMailRuCloud/WebDAVMailRuCloud.sln
    # пробуем выполнить сборку решения
    MONO_IOMAP=case xbuild WebDavMailRuCloud/WebDAVMailRuCloud.sln

    Если получаем ошибку:

    MailRuCloudApi.cs(1020,51): error CS0019: Operator `>' cannot be applied to operands of type `long' and `object'

    Вот эта строчка:
    https://github.com/yar229/Mail.Ru-.net-cloud-clien...

    Открываем локальную копию указанного файла и добавляем скобки (кстати, можно сделать pull request):

    return (fileStream.Length > 0) as object;

    Пробуем собрать еще раз:

    MONO_IOMAP=case xbuild WebDavMailRuCloud/WebDAVMailRuCloud.sln

    В случае успеха получаем что-то типа этого:

    18 Warning(s)
    0 Error(s)

    Пробуем запустить:

    mono WebDavMailRuCloud/WDMRC.Console/bin/Debug/wdmrc.exe --help

    Получаем:

    log4net:ERROR Could not create Appender [ColoredConsoleAppender] of type [log4net.Appender.ColoredConsoleAppender]. Reported error follows.
    System.EntryPointNotFoundException: GetConsoleOutputCP
    ...

    В суть не буду вникать, но очевидно, что какая-то проблема с log4net. Можно просто отключить log4net. Откройте файл конфигурации:

    nano WebDavMailRuCloud/WDMRC.Console/bin/Debug/wdmrc.exe.config

    Замените ветку <log4net>...</log4net> на:

    <log4net threshold="OFF" />

    У меня с параметром --help запустилось нормально, дальше не стал смотреть:

    mono WebDavMailRuCloud/WDMRC.Console/bin/Debug/wdmrc.exe --help
    WebDAVCloudMailRu 1.0.0.0
    yar229@yandex.ru
    
      -p, --port        Required. WebDAV server port
    
      -l, --login       Required. Login to Mail.ru Cloud
    
      -s, --password    Required. Password to Mail.ru Cloud
    
      --maxthreads      (Default: 5) Maximum concurrent connections to cloud.mail.ru
    
      --user-agent      "browser" user-agent
    
      --help            Display this help screen.
    
      --version         Display version information.

    UPD: Попробовал запустить с login и password, получил ошибку вида:

    Unhandled Exception:
    System.InvalidOperationException: Property 'enabled' not found in configuration element
    at System.Configuration.ConfigurationElement.get_Item (System.String property_name) <0xb50f5608 + 0x000a7> in :0
    at System.Configuration.ConfigurationElement.get_Item (System.Configuration.ConfigurationProperty property) <0xb50f55d8 + 0x0001f> in :0
    at System.Net.Configuration.DefaultProxySection.get_Enabled () <0xb506a270 + 0x0001b> in :0
    at MailRuCloudApi.Account.Login () <0xb50699f8 + 0x000af> in :0

    Проблема в получении значений прокси-сервера по умолчанию:
    https://github.com/yar229/Mail.Ru-.net-cloud-clien...

    По идее, прокси-сервер по умолчанию можно отключить/настроить в файле конфигурации приложения, добавив в секцию configuration нечто вроде этого:

    <system.net>
      <defaultProxy enabled="false" useDefaultCredentials="false">
        <proxy/>
        <bypasslist/>
        <module/>
      </defaultProxy>
    </system.net>

    Но у меня почему-то это не сработало. Можно удалить указанные строки (if (new DefaultProxySection().Enabled)...) из файла и собрать решение заново:

    nano WebDavMailRuCloud/MailRuNetCloudClient/MailRuCloudApi/Account.cs
    MONO_IOMAP=case xbuild WebDavMailRuCloud/WebDAVMailRuCloud.sln

    У меня с login и password запустилось:

    WebDAV server running. Press 'x' to quit.

    Если потребуется журналирование (log4net) и прокси, то придется разбираться в этих проблемах более углубленно.

    -----------------------------------------------------------------------------

    В проверке принимали участие:
    • Debian v8.1
    • Mono v4.2.3
    • XBuild Engine v12.0
    • Nuget v3.4.4.1321
    Ответ написан
    2 комментария
  • Перезагрузка страницы приводит к 404 ошибке (React Router)?

    AlekseyNemiro
    @AlekseyNemiro
    full-stack developer
    Все запросы нужно направлять на главную страницу.

    web.config
    <system.webServer>
      <rewrite>
        <rules>
          <rule name="All" patternSyntax="Wildcard" stopProcessing="true" enabled="true">
            <match url="*" />
            <conditions logicalGrouping="MatchAll">
              <add input="{REQUEST_FILENAME}" 
                   matchType="IsFile" 
                   negate="true" 
                   pattern="" 
                   ignoreCase="false" 
              />
            </conditions>
            <action type="Rewrite" url="index.html" />
          </rule>
        </rules>
      </rewrite>
    </system.webServer>

    После внесения изменений, не забудьте перезапустить рабочий процесс (пул).
    Ответ написан
    3 комментария
  • Как скачать файл, в имени которого есть пробел?

    AlekseyNemiro
    @AlekseyNemiro
    full-stack developer
    1. Попробуйте поместить имя файла в кавычки.

    2. Свойство FullName будет содержать полное имя файла, включая путь. В данном случае лучше использовать свойство Name (просто имя файла, без пути расположения).

    response.AddHeader("Content-Disposition", "attachment; filename='" + file.Name + "';");
    // или
    // response.AddHeader("Content-Disposition", "attachment; filename=\"" + file.Name + "\";");
    Ответ написан
    1 комментарий
  • Как вывести переменную за массив foreach?

    AlekseyNemiro
    @AlekseyNemiro
    full-stack developer
    Судя по всему вы циклом проходите по всем товарам?

    Что такое spec? В вашем примере это константа, полагаю проблема именно в этом.

    Вероятно, spec должна быть переменной:

    $good[$spec] = array('mall', 'discount', 'promotion');
    //    ^
    
    foreach($good[$spec] as $mall) {
      //          ^
      // ...
    }

    ---

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

    php.net/manual/ru/language.types.array.php
    Почему $foo[bar] неверно?

    Всегда заключайте в кавычки строковый литерал в индексе ассоциативного массива. К примеру, пишите $foo['bar'], а не $foo[bar]. Но почему? Часто в старых скриптах можно встретить следующий синтаксис:

    <?php
    $foo[bar] = 'враг';
    echo $foo[bar];
    // и т.д.
    ?>

    Это неверно, хотя и работает. Причина в том, что этот код содержит неопределенную константу (bar), а не строку ('bar' - обратите внимание на кавычки). Это работает, потому что PHP автоматически преобразует "голую строку" (не заключенную в кавычки строку, которая не соответствует ни одному из известных символов языка) в строку со значением этой "голой строки". Например, если константа с именем bar не определена, то PHP заменит bar на строку 'bar' и использует ее.
    Ответ написан
    4 комментария
  • ASP в роутере как установлен?

    AlekseyNemiro
    @AlekseyNemiro
    full-stack developer
    Да, и Windows :-)

    1. Расширение файлов страниц не гарантирует, что используется именно ASP, там может быть все что угодно.

    2. ASP может быть под Linux, в теории, под любым веб-сервером.

    Для Apache есть отдельная реализация ASP на основе Perl: Apache::ASP.
    Ответ написан
    Комментировать
  • Как добавить строку в таблицу в ReactJS?

    AlekseyNemiro
    @AlekseyNemiro
    full-stack developer
    Если вы хотите что-то изменить в текущем компоненте, то используйте состояние (setState).

    Если вы хотите изменить дочерний компонент, меняйте состояние текущего (setState) и передавайте измененные данные из состояния (state) дочернему компоненту через props.

    В вашем примере, корневой компонент - Table, в который вы передаете свойства (props): head_names и rows. Если вы хотите сделать возможность добавлять новую строку в Table, то вам следует сделать контейнер, в который поместить Table и кнопку (или другой элемент) для добавления новых строк. В состояние (state) контейнера нужно добавит head_names и rows, на основе которых и будет создаваться таблица.

    class TableManagement extends React.Component {
    
      constructor(props) {
        super(props);
        
        this.state = {
          head_names: ['qwe0', 'qwe1'],
          rows: [
            [1, 2],
            [3, 4]
          ]
        };
      }  
    
      AddRow() {
        let newRows = this.state.rows;
        newRows.push([0, 0]);
        this.setState({rows: newRows});
      }
    
      render() {
        return (
          <div>
            <Table head={this.state.head_names} rows={this.state.rows} />
            <hr />
            <button onClick={ this.AddRow.bind(this) }>Add row</button>
          </div>
        );
      }
    }
    
    class Table extends React.Component {
      render() {
        return (
          <table>
            <thead>
              {this.genHead()}
            </thead>
            <tbody>
              {this.genRow()}
            </tbody>
          </table>
        );
      }
    
      genHead() {
        var head = this.props.head;
    
        return head.map(function(v, i) {
          return (
            <th key={'th' + i}>
              {v}
            </th>
          );
        });
      }
    
      genRow() {
        var rows = this.props.rows;
    
        return rows.map(function(v, i) {
          var tmp = v.map(function(v2, j) {
            return (
              <td key={'td' + i + '_' + j}>
                {v2}
              </td>
            );
          });
    
          return (
            <tr key={'tr' + i}>
              {tmp}
            </tr>
          )
        });
      }
    }
    
    ReactDOM.render(
      <TableManagement />,
      document.getElementById('root')
    );

    https://jsfiddle.net/m11x34fp/

    В процессе разработки, важно определиться, кто будет тупым, а кто умным :-)

    Тупые (простые) компоненты - это компоненты, которые ничего не делают, просто принимают свойства и рисуются.

    Умные компоненты способны управлять состоянием, как своим, так и передавать его своим тупым отпрыскам потомкам :-)

    Если компонентов много, и особенно, если много вложенных компонентов, то следует хорошо продумать роль каждого компонента. Один умный компонент с множеством тупых вложенных компонентов - это самый простой вариант. Если дети тоже будут умными и должны будут общаться с компонентами своего уровня (братьями и сестрами) или верхнего уровня (дедушками, бабушками и выше), то организовать взаимодействие между компонентами будет сложно, будут конфликты, лишние обновления, в худшем случае зацикливание. Для решения проблем таких масштабов можно использования библиотеки управления состоянием, такие как Redux, Flux и т.п.
    Ответ написан
    2 комментария