nodejs中如何使用mysql資料庫[node-mysql翻譯]

来源:http://www.cnblogs.com/leftthen/archive/2016/08/13/5768307.html
-Advertisement-
Play Games

使用node-mysql,在nodejs中訪問mysql資料庫.包含連接池,sql轉義,多種查詢語句使用 ...


nodejs中如何使用mysql資料庫

db-mysql因為node-waf: not found已經不能使用,可以使用mysql代替.

本文主要是[node-mysql]: https://www.npmjs.com/package/node-mysql 的翻譯,也去除了一部分自己暫時沒有使用到的,如集群.

安裝

npm install mysql

簡介

純JavaScript編寫,使用的MIT協議.

var mysql      = require('mysql');
var connection = mysql.createConnection({
  host     : 'localhost',
  user     : 'me',
  password : 'secret',
  database : 'my_db'
});

connection.connect();

// 順序執行
connection.query('SELECT 1 + 1 AS solution', function(err, rows, fields) {
  if (err) throw err;

  console.log('The solution is: ', rows[0].solution);
});

// 關閉資料庫連接
connection.end();

建立資料庫連接

官方推薦如下方式建立資料庫連接

var mysql      = require('mysql');
var connection = mysql.createConnection({
  host     : 'example.org',
  user     : 'bob',
  password : 'secret'
});

connection.connect(function(err) {
  if (err) {
    console.error('error connecting: ' + err.stack);
    return;
  }

  console.log('connected as id ' + connection.threadId);
});

也可以直接通過查詢建立連接

var mysql      = require('mysql');
var connection = mysql.createConnection(...);

connection.query('SELECT 1', function(err, rows) {
  // connected! (unless `err` is set)
});

連接的選項

  • host: The hostname of the database you are connecting to. (Default:
    localhost)
  • port: The port number to connect to. (Default: 3306)
  • localAddress: The source IP address to use for TCP connection. (Optional)
  • socketPath: The path to a unix domain socket to connect to. When used host
    and port are ignored.
  • user: The MySQL user to authenticate as.
  • password: The password of that MySQL user.
  • database: Name of the database to use for this connection (Optional).
  • charset: The charset for the connection. This is called "collation" in the SQL-level
    of MySQL (like utf8_general_ci). If a SQL-level charset is specified (like utf8mb4)
    then the default collation for that charset is used. (Default: 'UTF8_GENERAL_CI')
  • timezone: The timezone used to store local dates. (Default: 'local')
  • connectTimeout: The milliseconds before a timeout occurs during the initial connection
    to the MySQL server. (Default: 10000)
  • stringifyObjects: Stringify objects instead of converting to values. See
    issue #501. (Default: 'false')
  • insecureAuth: Allow connecting to MySQL instances that ask for the old
    (insecure) authentication method. (Default: false)
  • typeCast: Determines if column values should be converted to native
    JavaScript types. (Default: true)
  • queryFormat: A custom query format function. See Custom format.
  • supportBigNumbers: When dealing with big numbers (BIGINT and DECIMAL columns) in the database,
    you should enable this option (Default: false).
  • bigNumberStrings: Enabling both supportBigNumbers and bigNumberStrings forces big numbers
    (BIGINT and DECIMAL columns) to be always returned as JavaScript String objects (Default: false).
    Enabling supportBigNumbers but leaving bigNumberStrings disabled will return big numbers as String
    objects only when they cannot be accurately represented with [JavaScript Number objects] (http://ecma262-5.com/ELS5_HTML.htm#Section_8.5)
    (which happens when they exceed the [-2^53, +2^53] range), otherwise they will be returned as
    Number objects. This option is ignored if supportBigNumbers is disabled.
  • dateStrings: Force date types (TIMESTAMP, DATETIME, DATE) to be returned as strings rather then
    inflated into JavaScript Date objects. (Default: false)
  • debug: Prints protocol details to stdout. (Default: false)
  • trace: Generates stack traces on Error to include call site of library
    entrance ("long stack traces"). Slight performance penalty for most calls.
    (Default: true)
  • multipleStatements: Allow multiple mysql statements per query. Be careful
    with this, it could increase the scope of SQL injection attacks. (Default: false)
  • flags: List of connection flags to use other than the default ones. It is
    also possible to blacklist default ones. For more information, check
    Connection Flags.
  • ssl: object with ssl parameters or a string containing name of ssl profile. See SSL options.

下麵這樣通過字元串方式也可以:

var connection = mysql.createConnection('mysql://user:pass@host/db?debug=true&charset=BIG5_CHINESE_CI&timezone=-0700');

關閉連接

有兩種方式關閉連接:end和destroy

使用end回調關閉會更優雅一些,他會確保已經在隊列中的查詢會發送一個COM_QUIT 給mysql.

connection.end(function(err) {
  // The connection is terminated now
});

使用destroy會直接粗暴關閉連接,不會觸發connection的任何回調函數.

connection.destroy();

使用連接池

var mysql = require('mysql');
var pool  = mysql.createPool({
  connectionLimit : 10,
  host            : 'example.org',
  user            : 'bob',
  password        : 'secret',
  database        : 'my_db'
});

pool.query('SELECT 1 + 1 AS solution', function(err, rows, fields) {
  if (err) throw err;

  console.log('The solution is: ', rows[0].solution);
});

通過connection.release()釋放連接

var mysql = require('mysql');
var pool  = mysql.createPool(...);

pool.getConnection(function(err, connection) {
  // Use the connection
  connection.query( 'SELECT something FROM sometable', function(err, rows) {
    // And done with the connection.
    // 釋放連接
    connection.release();

    // Don't use the connection here, it has been returned to the pool.
  });
});

如果你想從連接池掛壁一個連接,使用connection.destroy().當然如果有需要連接池會新建一個代替.
連接池對於連接時懶載入的.比如你配置了100個連接,而現在只使用了5個,那隻會初始化5個.
連接池回收一個連接,後會往mysql伺服器發送一個ping,確認連接是否有效.

連接池的選項

連接池可以直接使用連接的選項,然後在新建連接時,直接用這些配置新建連接.連接池添加了下麵的選項:

  • acquireTimeout: The milliseconds before a timeout occurs during the connection
    acquisition. This is slightly different from connectTimeout, because acquiring
    a pool connection does not always involve making a connection. (Default: 10000)
  • waitForConnections: Determines the pool's action when no connections are
    available and the limit has been reached. If true, the pool will queue the
    connection request and call it when one becomes available. If false, the
    pool will immediately call back with an error. (Default: true)
  • connectionLimit: The maximum number of connections to create at once.
    (Default: 10)
  • queueLimit: The maximum number of connection requests the pool will queue
    before returning an error from getConnection. If set to 0, there is no
    limit to the number of queued connection requests. (Default: 0)

連接池事件

建立連接會觸發connection.

pool.on('connection', function (connection) {
  connection.query('SET SESSION auto_increment_increment=1')
});

當有回調排隊等待連接時,觸發enqueue

pool.on('enqueue', function () {
  console.log('Waiting for available connection slot');
});

關閉連接池

之前提到關閉連接池中的連接後,當需要使用時連接池會自動新建,所以使用connection.end()connection.destroy()時無法關閉連接池的,需要使用pool.end():

pool.end(function (err) {
  // all connections in the pool have ended
});

查詢語句

ConnectionPool實例上使用.query()是最簡單的查詢.

第一種方式是直接拼接好查詢用的sql.query(sqlString, callback)

connection.query('SELECT * FROM `books` WHERE `author` = "David"', function (error, results, fields) {
  // error will be an Error if one occurred during the query
  // results will contain the results of the query
  // fields will contain information about the returned results fields (if any)
});

或者使用占位符,然後傳參.query(sqlString, values, callback)

connection.query('SELECT * FROM `books` WHERE `author` = ?', ['David'], function (error, results, fields) {
  // error will be an Error if one occurred during the query
  // results will contain the results of the query
  // fields will contain information about the returned results fields (if any)
});

第三種方式是使用options..query(options, callback)

connection.query({
  sql: 'SELECT * FROM `books` WHERE `author` = ?',
  timeout: 40000, // 40s
  values: ['David']
}, function (error, results, fields) {
  // error will be an Error if one occurred during the query
  // results will contain the results of the query
  // fields will contain information about the returned results fields (if any)
});

第二種和第三種使用方式可以混合使用

connection.query({
    sql: 'SELECT * FROM `books` WHERE `author` = ?',
    timeout: 40000, // 40s
  },
  ['David'],
  function (error, results, fields) {
    // error will be an Error if one occurred during the query
    // results will contain the results of the query
    // fields will contain information about the returned results fields (if any)
  }
);

查詢參數轉義 Escaping query values

為了避免sql註入攻擊,在sql查詢使用前,我們需要轉義用戶提供的任何數據. 使用mysql.escape(), connection.escape()pool.escape() 方法:

var userId = 'some user provided value';
var sql    = 'SELECT * FROM users WHERE id = ' + connection.escape(userId);
connection.query(sql, function(err, results) {
  // ...
});

使用占位符?,也行.

connection.query('SELECT * FROM users WHERE id = ?', [userId], function(err, results) {
  // ...
});

占位符是按順序替換的.

connection.query('UPDATE users SET foo = ?, bar = ?, baz = ? WHERE id = ?', ['a', 'b', 'c', userId], function(err, results) {
  // ...
});

不同類型的參數,轉義規則是不一樣的:

  • Numbers are left untouched
  • Booleans are converted to true / false
  • Date objects are converted to 'YYYY-mm-dd HH:ii:ss' strings
  • Buffers are converted to hex strings, e.g. X'0fa5'
  • Strings are safely escaped
  • Arrays are turned into list, e.g. ['a', 'b'] turns into 'a', 'b'
  • Nested arrays are turned into grouped lists (for bulk inserts), e.g. [['a', 'b'], ['c', 'd']] turns into ('a', 'b'), ('c', 'd')
  • Objects are turned into key = 'val' pairs for each enumerable property on the object. If the property's value is a function, it is skipped; if the
    property's value is an object, toString() is called on it and the returned value is used.
  • undefined / null are converted to NULL
  • NaN / Infinity are left as-is. MySQL does not support these, and trying to insert them as values will trigger MySQL errors until they implement
    support.

轉義還提供對象方式傳參數

var post  = {id: 1, title: 'Hello MySQL'};
var query = connection.query('INSERT INTO posts SET ?', post, function(err, result) {
  // Neat!
});
console.log(query.sql); // INSERT INTO posts SET `id` = 1, `title` = 'Hello MySQL'

不嫌麻煩的話,咱們也可以自己手動轉義:

var query = "SELECT * FROM posts WHERE title=" + mysql.escape("Hello MySQL");

console.log(query); // SELECT * FROM posts WHERE title='Hello MySQL'

轉義查詢的關鍵詞 Escaping query identifiers

如果你對用戶提供的關鍵詞沒把我 (database / table / column name) ,可以使用 mysql.escapeId(identifier),
connection.escapeId(identifier) or pool.escapeId(identifier) 轉義:

var sorter = 'date';
var sql    = 'SELECT * FROM posts ORDER BY ' + connection.escapeId(sorter);
connection.query(sql, function(err, results) {
  // ...
});
var sorter = 'date';
var sql    = 'SELECT * FROM posts ORDER BY ' + connection.escapeId('posts.' + sorter);
connection.query(sql, function(err, results) {
  // ...
});

還可以使用??做占位符:

var userId = 1;
var columns = ['username', 'email'];
var query = connection.query('SELECT ?? FROM ?? WHERE id = ?', [columns, 'users', userId], function(err, results) {
  // ...
});

console.log(query.sql); // SELECT `username`, `email` FROM `users` WHERE id = 1

Please note that this last character sequence is experimental and syntax might change

When you pass an Object to .escape() or .query(), .escapeId() is used to avoid SQL injection in object keys.

準備查詢語句Preparing Queries

You can use mysql.format to prepare a query with multiple insertion points, utilizing the proper escaping for ids and values. A simple example of this follows:
我們可以使用mysql.format來準備一個插入語句,解決轉義問題.

var sql = "SELECT * FROM ?? WHERE ?? = ?";
var inserts = ['users', 'id', userId];
sql = mysql.format(sql, inserts);

這樣我們就可以得到一個安全有效,轉義好的查詢語句.mysql.formatSqlString.format暴露的,所以可以傳入stringifyObject和timezone來自定義對象如何轉為字元串.

自定義格式 Custom format

如果我們想使用其他方式來轉義查詢語句,可以使用connection的配置.可以使用內置的.escape()或其他配置函數.

connection.config.queryFormat = function (query, values) {
  if (!values) return query;
  return query.replace(/\:(\w+)/g, function (txt, key) {
    if (values.hasOwnProperty(key)) {
      return this.escape(values[key]);
    }
    return txt;
  }.bind(this));
};

connection.query("UPDATE posts SET title = :title", { title: "Hello MySQL" });

獲取插入行的id

如果是id自增長方式插入數據,你可以這樣獲取id:

connection.query('INSERT INTO posts SET ?', {title: 'test'}, function(err, result) {
  if (err) throw err;

  console.log(result.insertId);
});

When dealing with big numbers (above JavaScript Number precision limit), you should consider enabling supportBigNumbers option to be able to read the insert id as a string, otherwise it will throw an error.

This option is also required when fetching big numbers from the database, otherwise you will get values rounded to hundreds or thousands due to the precision limit.

獲取影響的行數

我們可以獲取影響(新建,修改,刪除)涉及的行數

connection.query('DELETE FROM posts WHERE title = "wrong"', function (err, result) {
  if (err) throw err;

  console.log('deleted ' + result.affectedRows + ' rows');
})

獲取修改的行數 Getting the number of changed rows

我們可以獲取update語句修改涉及的行數/

"changedRows" 不同於 "affectedRows" 不統計符合條件但沒有改變值的記錄. in that it does not count updated rows whose values were not changed.

connection.query('UPDATE posts SET ...', function (err, result) {
  if (err) throw err;

  console.log('changed ' + result.changedRows + ' rows');
})

獲取連接id

connection.connect(function(err) {
  if (err) throw err;
  console.log('connected as id ' + connection.threadId);
});

並行執行查詢

mysql是順序執行的,所以我們需要使用多個連接來並行查詢.最簡答的最法是每個http請求分配一個連接.

流查詢 Streaming query rows

如果需要查詢大量數據並處理每行,可以這樣做:
Sometimes you may want to select large quantities of rows and process each of them as they are received. This can be done like this:

var query = connection.query('SELECT * FROM posts');
query
  .on('error', function(err) {
    // Handle error, an 'end' event will be emitted after this as well
  })
  .on('fields', function(fields) {
    // the field packets for the rows to follow
  })
  .on('result', function(row) {
    // Pausing the connnection is useful if your processing involves I/O
    connection.pause();

    processRow(row, function() {
      connection.resume();
    });
  })
  .on('end', function() {
    // all rows have been received
  });

Please note a few things about the example above:

  • Usually you will want to receive a certain amount of rows before starting to
    throttle the connection using pause(). This number will depend on the
    amount and size of your rows.
  • pause() / resume() operate on the underlying socket and parser. You are
    guaranteed that no more 'result' events will fire after calling pause().
  • You MUST NOT provide a callback to the query() method when streaming rows.
  • The 'result' event will fire for both rows as well as OK packets
    confirming the success of a INSERT/UPDATE query.
  • It is very important not to leave the result paused too long, or you may
    encounter Error: Connection lost: The server closed the connection.
    The time limit for this is determined by the
    net_write_timeout setting
    on your MySQL server.

Additionally you may be interested to know that it is currently not possible to
stream individual row columns, they will always be buffered up entirely. If you
have a good use case for streaming large fields to and from MySQL, I'd love to
get your thoughts and contributions on this.

Piping results with Streams2

The query object provides a convenience method .stream([options]) that wraps
query events into a Readable
Streams2 object. This
stream can easily be piped downstream and provides automatic pause/resume,
based on downstream congestion and the optional highWaterMark. The
objectMode parameter of the stream is set to true and cannot be changed
(if you need a byte stream, you will need to use a transform stream, like
objstream for example).

For example, piping query results into another stream (with a max buffer of 5
objects) is simply:

connection.query('SELECT * FROM posts')
  .stream({highWaterMark: 5})
  .pipe(...);

多語句查詢 Multiple statement queries

由於sql註入的安全問題,多語句查詢預設禁用.需要手動啟用{multipleStatements: true}.

var connection = mysql.createConnection({multipleStatements: true});

之後就跟普通使用是一樣的.

connection.query('SELECT 1; SELECT 2', function(err, results) {
  if (err) throw err;

  // `results` is an array with one element for every statement in the query:
  console.log(results[0]); // [{1: 1}]
  console.log(results[1]); // [{2: 2}]
});

Additionally you can also stream the results of multiple statement queries:

var query = connection.query('SELECT 1; SELECT 2');

query
  .on('fields', function(fields, index) {
    // the fields for the result rows that follow
  })
  .on('result', function(row, index) {
    // index refers to the statement this result belongs to (starts at 0)
  });

If one of the statements in your query causes an error, the resulting Error
object contains a err.index property which tells you which statement caused
it. MySQL will also stop executing any remaining statements when an error
occurs.

Please note that the interface for streaming multiple statement queries is
experimental and I am looking forward to feedback on it.

存儲過程 Stored procedures

跟普通語句一樣使用存儲過程就好.如果存儲過程返回了多個集合的數據,會像多語句查詢那樣返回結果集.

join語句時相同列名處理 Joins with overlapping column names

執行join語句時,很可能會收到重覆的列名.

By default, node-mysql will overwrite colliding column names in the
order the columns are received from MySQL, causing some of the received values
to be unavailable.

However, you can also specify that you want your columns to be nested below
the table name like this:

var options = {sql: '...', nestTables: true};
connection.query(options, function(err, results) {
  /* results will be an array like this now:
  [{
    table1: {
      fieldA: '...',
      fieldB: '...',
    },
    table2: {
      fieldA: '...',
      fieldB: '...',
    },
  }, ...]
  */
});

Or use a string separator to have your results merged.

var options = {sql: '...', nestTables: '_'};
connection.query(options, function(err, results) {
  /* results will be an array like this now:
  [{
    table1_fieldA: '...',
    table1_fieldB: '...',
    table2_fieldA: '...',
    table2_fieldB: '...',
  }, ...]
  */
});

事務 Transactions

在connection中提供事務

connection.beginTransaction(function(err) {
  if (err) { throw err; }
  connection.query('INSERT INTO posts SET title=?', title, function(err, result) {
    if (err) {
      return connection.rollback(function() {
        throw err;
      });
    }

    var log = 'Post ' + result.insertId + ' added';

    connection.query('INSERT INTO log SET data=?', log, function(err, result) {
      if (err) {
        return connection.rollback(function() {
          throw err;
        });
      }  
      connection.commit(function(err) {
        if (err) {
          return connection.rollback(function() {
            throw err;
          });
        }
        console.log('success!');
      });
    });
  });
});

beginTransaction(), commit() 和 rollback()只是簡單執行START TRANSACTION, COMMIT, 和 ROLLBACK命令.而mysql中很多語句是可以自動提交的.自己翻MySQL documentation

Ping

ping一下,確認連接是否有效,連接池也用.

A ping packet can be sent over a connection using the connection.ping method. This
method will send a ping packet to the server and when the server responds, the callback
will fire. If an error occurred, the callback will fire with an error argument.

connection.ping(function (err) {
  if (err) throw err;
  console.log('Server responded to ping');
})

Timeouts

Every operation takes an optional inactivity timeout option. This allows you to
specify appropriate timeouts for operations. It is important to note that these
timeouts are not part of the MySQL protocol, and rather timeout operations through
the client. This means that when a timeout is reached, the connection it occurred
on will be destroyed and no further operations can be performed.

// Kill query after 60s
connection.query({sql: 'SELECT COUNT(*) AS count FROM big_table', timeout: 60000}, function (err, rows) {
  if (err && err.code === 'PROTOCOL_SEQUENCE_TIMEOUT') {
    throw new Error('too long to count table rows!');
  }

  if (err) {
    throw err;
  }

  console.log(rows[0].count + ' rows');
});

Error handling

This module comes with a consistent approach to error handling that you should
review carefully in order to write solid applications.

Most errors created by this module are instances of the JavaScript Error
object. Additionally they typically come with two extra properties:

  • err.code: Either a MySQL server error (e.g.
    'ER_ACCESS_DENIED_ERROR'), a Node.js error (e.g. 'ECONNREFUSED') or an
    internal error (e.g. 'PROTOCOL_CONNECTION_LOST').
  • err.fatal: Boolean, indicating if this error is terminal to the connection
    object. If the error is not from a MySQL protocol operation, this properly
    will not be defined.

Fatal errors are propagated to all pending callbacks. In the example below, a
fatal error is triggered by trying to connect to an invalid port. Therefore the
error object is propagated to both pending callbacks:

var connection = require('mysql').createConnection({
  port: 84943, // WRONG PORT
});

connection.connect(function(err) {
  console.log(err.code); // 'ECONNREFUSED'
  console.log(err.fatal); // true
});

connection.query('SELECT 1', function(err) {
  console.log(err.code); // 'ECONNREFUSED'
  console.log(err.fatal); // true
});

Normal errors however are only delegated to the callback they belong to. So in
the example below, only the first callback receives an error, the second query
works as expected:

connection.query('USE name_of_db_that_does_not_exist', function(err, rows) {
  console.log(err.code); // 'ER_BAD_DB_ERROR'
});

connection.query('SELECT 1', function(err, rows) {
  console.log(err); // null
  console.log(rows.length); // 1
});

Last but not least: If a fatal errors occurs and there are no pending
callbacks, or a normal error occurs which has no callback belonging to it, the
error is emitted as an 'error' event on the connection object. This is
demonstrated in the example below:

connection.on('error', function(err) {
  console.log(err.code); // 'ER_BAD_DB_ERROR'
});

connection.query('USE name_of_db_that_does_not_exist');

Note: 'error' events are special in node. If they occur without an attached
listener, a stack trace is printed and your process is killed.

tl;dr: This module does not want you to deal with silent failures. You
should always provide callbacks to your method calls. If you want to ignore
this advice and suppress unhandled errors, you can do this:

// I am Chuck Norris:
connection.on('error', function() {});

Exception Safety

This module is exception safe. That means you can continue to use it, even if
one of your callback functions throws an error which you're catching using
'uncaughtException' or a domain.

Type casting

For your convenience, this driver will cast mysql types into native JavaScript
types by default. The following mappings exist:

Number

  • TINYINT
  • SMALLINT
  • INT
  • MEDIUMINT
  • YEAR
  • FLOAT
  • DOUBLE

Date

  • TIMESTAMP
  • DATE
  • DATETIME

Buffer

  • TINYBLOB
  • MEDIUMBLOB
  • LONGBLOB
  • BLOB
  • BINARY
  • VARBINARY
  • BIT (last byte will be filled with 0 bits as necessary)

String

Note text in the binary character set is returned as Buffer, rather
than a string.

  • CHAR
  • VARCHAR
  • TINYTEXT
  • MEDIUMTEXT
  • LONGTEXT
  • TEXT
  • ENUM
  • SET
  • DECIMAL (may exceed float precision)
  • BIGINT (may exceed float precision)
  • TIME (could be mapped to Date, but what date would be set?)
  • GEOMETRY (never used those, get in touch if you do)

It is not recommended (and may go away / change in the future) to disable type
casting, but you can currently do so on either the connection:

var connection = require('mysql').createConnection({typeCast: false});

Or on the query level:

var options = {sql: '...', typeCast: false};
var query = connection.query(options, function(err, results) {

});

You can also pass a function and handle type casting yourself. You're given some
column information like database, table and name and also type and length. If you
just want to apply a custom type casting to a specific type you can do it and then
fallback to the default. Here's an example of converting TINYINT(1) to boolean:

connection.query({
  sql: '...',
  typeCast: function (field, next) {
    if (field.type == 'TINY' && field.length == 1) {
      return (field.string() == '1'); // 1 = true, 0 = false
    }
    return next();
  }
});

WARNING: YOU MUST INVOKE the parser using one of these three field functions in your custom typeCast callback. They can only be called once. (see #539 for discussion)

field.string()
field.buffer()
field.geometry()

are aliases for

parser.parseLengthCodedString()
parser.parseLengthCodedBuffer()
parser.parseGeometryValue()

You can find which field function you need to use by looking at: RowDataPacket.prototype._typeCast

Connection Flags

If, for any reason, you would like to change the default connection flags, you
can use the connection option flags. Pass a string with a comma separated list
of items to add to the default flags. If you don't want a default flag to be used

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • Ajax 完整教程 第 1 頁 Ajax 簡介Ajax 由 HTML、JavaScript™ 技術、DHTML 和 DOM 組成,這一傑出的方法可以將笨拙的 Web 界面轉化成交互性的 Ajax 應用程式。本文的作者是一位 Ajax 專家,他演示了這些技術如何協同工作 —— 從總體概述到細節的討論 ...
  • HTML 什麼是瀏覽器什麼是伺服器? 可以顯示伺服器中的html文件。 可以讓用戶與這些文件交互。 特點: 處理請求。 必須保證二十四小時開機狀態。 請求協議:http協議: Url的組成:協議://伺服器的ip地址:埠號/請求頁面。 原來瀏覽器所做的事情是將伺服器響應回來的response進行從 ...
  • 例一 var x=10;全局變數(開闢空間)function outer(){x=20;//此處未聲明變數(未開闢空間),只給全局變數聲明瞭,此處賦值會把全局變數開闢的存儲空間的值替換掉(全局變數中的x替換成20)。function inner(){x=30;//此處未聲明變數(未開闢空間),只給全 ...
  • { "total": 16, "rows":[ { "name": "張三", "email": "[email protected]", "date": "2016-08-13" }, { "name": "李四", "email": "[email protected]", "date": " ...
  • web前端之HTML的大框架 body元素與frameset元素 對於從事html的人員來說,我們一般熟悉的框架是先聲明html ,然後在<html>標簽對里包著<head>標簽對和<body>標簽對,body元素定義文檔的主體,包含文檔的所有內容(比如文本、超鏈接、圖像、表格和列表等等)。而我們想 ...
  • 一、表單事件: 一、表單事件: input事件當<input>、<textarea>的值發生變化時觸發。此外,打開contenteditable屬性的元素,只要值發生變化,也會觸發input事件。input事件的一個特點,就是會連續觸發,比如用戶每次按下一次按鍵,就會觸發一次input事件。 inp ...
  • 事件是一種非同步編程的實現方式,本質上是程式各個組成部分之間的通信,DOM支持大量的事件; 本文通過這幾點向大家詳細解析事件處理的基本原理:事件類型、事件目標、事件處理程式、事件對象、事件傳播 最後再向大家介紹Event對象; (原創文章,轉摘請註明:蘇服:http://www.cnblogs.com ...
  • 雖然有很多插件可用,但為了共同提高,我做了一系列JavaScript實戰系列的實例,分享給大家,前輩們若有好的建議,請務必指出,免得誤人子弟啊! ( 原創文章,轉摘請註明:蘇服:http://www.cnblogs.com/susufufu/p/5768402.html ) 今天是第一戰:帶收放動畫 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...